CORVO-AI commited on
Commit
259b50c
·
verified ·
1 Parent(s): 91c7b38

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +35 -12
app.py CHANGED
@@ -1,6 +1,7 @@
1
  """
2
  MindStudio WebSocket Streaming Client — Flask API Endpoint
3
- FIXED: Per-thread continuation tokens for proper conversation memory
 
4
  FIXED: WebSocket compatibility
5
  """
6
 
@@ -72,7 +73,7 @@ class MindStudioClient:
72
  self.access_token = access_token
73
  self.model = model
74
 
75
- # ✅ FIX: Per-thread continuation tokens instead of one global token
76
  self._continuation_tokens: dict = {}
77
  self._tokens_lock = threading.Lock()
78
 
@@ -115,16 +116,35 @@ class MindStudioClient:
115
  return check_ws_open(self.ws)
116
 
117
  def get_continuation_token(self, thread_id: str) -> str:
118
- """✅ Get the continuation token for a specific thread."""
119
  with self._tokens_lock:
120
  return self._continuation_tokens.get(thread_id, INITIAL_CONTINUATION_TOKEN)
121
 
122
  def set_continuation_token(self, thread_id: str, token: str):
123
- """✅ Save the continuation token for a specific thread."""
124
  with self._tokens_lock:
125
  self._continuation_tokens[thread_id] = token
126
  print(f"[TOKEN] Thread {thread_id[:8]}... token updated")
127
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  def send_message_stream(self, message: str, thread_id: str):
129
  import queue
130
 
@@ -133,12 +153,18 @@ class MindStudioClient:
133
 
134
  async def _run():
135
  try:
136
- listen_task = asyncio.create_task(
137
- self._listen_for_response(chunk_queue, done_event, thread_id)
138
- )
139
- await asyncio.sleep(0.1)
140
  await self._post_message(message, thread_id)
141
- await listen_task
 
 
 
 
 
 
142
  except Exception as e:
143
  print(f"[ERROR] send_message_stream: {e}")
144
  chunk_queue.put(f"[ERROR] {e}")
@@ -158,7 +184,6 @@ class MindStudioClient:
158
  async def _post_message(self, message: str, thread_id: str):
159
  post_url = f"https://v1.mindstudio-api.com/v1/apps/load/{APP_ID}/threads/post"
160
 
161
- # ✅ FIX: Use per-thread continuation token
162
  continuation_token = self.get_continuation_token(thread_id)
163
  print(f"[POST] Using token: {continuation_token[:30]}... for thread: {thread_id[:8]}...")
164
 
@@ -187,7 +212,6 @@ class MindStudioClient:
187
  print(f"[POST] Body: {resp.text}")
188
 
189
  async def _listen_for_response(self, chunk_queue, done_event, thread_id: str):
190
- """✅ FIX: Accept thread_id to update its specific continuation token."""
191
  full_response = ""
192
  previous_content_length = 0
193
  system_post_id = None
@@ -216,7 +240,6 @@ class MindStudioClient:
216
  data = event.get("appThreadContinuationActionUpdated", {})
217
  new_token = data.get("continuationAction", {}).get("token")
218
  if new_token:
219
- # ✅ FIX: Save token per thread, not globally
220
  self.set_continuation_token(thread_id, new_token)
221
 
222
  underlying = (
 
1
  """
2
  MindStudio WebSocket Streaming Client — Flask API Endpoint
3
+ FIXED: Race condition AI was answering previous message (buffer drain fix)
4
+ FIXED: Per-thread continuation tokens
5
  FIXED: WebSocket compatibility
6
  """
7
 
 
73
  self.access_token = access_token
74
  self.model = model
75
 
76
+ # Per-thread continuation tokens
77
  self._continuation_tokens: dict = {}
78
  self._tokens_lock = threading.Lock()
79
 
 
116
  return check_ws_open(self.ws)
117
 
118
  def get_continuation_token(self, thread_id: str) -> str:
 
119
  with self._tokens_lock:
120
  return self._continuation_tokens.get(thread_id, INITIAL_CONTINUATION_TOKEN)
121
 
122
  def set_continuation_token(self, thread_id: str, token: str):
 
123
  with self._tokens_lock:
124
  self._continuation_tokens[thread_id] = token
125
  print(f"[TOKEN] Thread {thread_id[:8]}... token updated")
126
 
127
+ # ─────────────────────────────────────────
128
+ # ✅ FIX: Drain leftover WS buffer
129
+ # ─────────────────────────────────────────
130
+ async def _drain_ws_buffer(self):
131
+ """Flush any leftover WebSocket messages from a previous response."""
132
+ flushed = 0
133
+ try:
134
+ while True:
135
+ raw = await asyncio.wait_for(self.ws.recv(), timeout=0.05)
136
+ flushed += 1
137
+ print(f"[DRAIN] Flushed leftover msg #{flushed}: {str(raw)[:80]}...")
138
+ except asyncio.TimeoutError:
139
+ pass
140
+ except Exception:
141
+ pass
142
+ if flushed:
143
+ print(f"[DRAIN] Total flushed: {flushed} leftover message(s)")
144
+
145
+ # ─────────────────────────────────────────
146
+ # ✅ FIX: Post FIRST, then listen
147
+ # ─────────────────────────────────────────
148
  def send_message_stream(self, message: str, thread_id: str):
149
  import queue
150
 
 
153
 
154
  async def _run():
155
  try:
156
+ # STEP 1: Drain any leftover buffer from previous message
157
+ await self._drain_ws_buffer()
158
+
159
+ # ✅ STEP 2: Post the new message
160
  await self._post_message(message, thread_id)
161
+
162
+ # ✅ STEP 3: Small wait for server to process
163
+ await asyncio.sleep(0.15)
164
+
165
+ # ✅ STEP 4: Listen for THIS message's response only
166
+ await self._listen_for_response(chunk_queue, done_event, thread_id)
167
+
168
  except Exception as e:
169
  print(f"[ERROR] send_message_stream: {e}")
170
  chunk_queue.put(f"[ERROR] {e}")
 
184
  async def _post_message(self, message: str, thread_id: str):
185
  post_url = f"https://v1.mindstudio-api.com/v1/apps/load/{APP_ID}/threads/post"
186
 
 
187
  continuation_token = self.get_continuation_token(thread_id)
188
  print(f"[POST] Using token: {continuation_token[:30]}... for thread: {thread_id[:8]}...")
189
 
 
212
  print(f"[POST] Body: {resp.text}")
213
 
214
  async def _listen_for_response(self, chunk_queue, done_event, thread_id: str):
 
215
  full_response = ""
216
  previous_content_length = 0
217
  system_post_id = None
 
240
  data = event.get("appThreadContinuationActionUpdated", {})
241
  new_token = data.get("continuationAction", {}).get("token")
242
  if new_token:
 
243
  self.set_continuation_token(thread_id, new_token)
244
 
245
  underlying = (