infinityonline commited on
Commit
6304f51
ยท
verified ยท
1 Parent(s): 56ab5c2

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +81 -48
main.py CHANGED
@@ -102,7 +102,7 @@ class AsyncBrowserThread(threading.Thread):
102
  async def _chat(self, model_label: str, prompt: str) -> str:
103
  page = await self.persistent_context.new_page()
104
  try:
105
- page.set_default_timeout(120000)
106
 
107
  # โ”€โ”€ 1. ูุชุญ ุตูุญุฉ ุฌุฏูŠุฏุฉ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
108
  await page.goto(
@@ -115,6 +115,7 @@ class AsyncBrowserThread(threading.Thread):
115
  agree = page.locator('button:has-text("Agree and Continue")')
116
  if await agree.count() > 0 and await agree.first.is_visible():
117
  await agree.first.click()
 
118
  await asyncio.sleep(2)
119
  except Exception:
120
  pass
@@ -125,7 +126,7 @@ class AsyncBrowserThread(threading.Thread):
125
  )
126
  print("[DUCK] Input ready โœ“")
127
 
128
- # โ”€โ”€ 4. ุชุบูŠูŠุฑ ุงู„ู†ู…ูˆุฐุฌ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
129
  try:
130
  model_btn = page.locator('button[data-testid="model-select-button"]')
131
  current_text = await model_btn.inner_text()
@@ -137,7 +138,6 @@ class AsyncBrowserThread(threading.Thread):
137
 
138
  option = page.locator(
139
  f"li:has-text('{model_label}'), "
140
- f"button:has-text('{model_label}'), "
141
  f"[role='option']:has-text('{model_label}')"
142
  )
143
  if await option.count() > 0:
@@ -147,80 +147,106 @@ class AsyncBrowserThread(threading.Thread):
147
  await page.keyboard.press("Escape")
148
  print(f"[DUCK] Model not found, using default")
149
 
150
- # ุงู†ุชุธุฑ ุฅุนุงุฏุฉ ุชุญู…ูŠู„ ุงู„ู€ textarea ุจุนุฏ ุชุบูŠูŠุฑ ุงู„ู†ู…ูˆุฐุฌ
151
- await asyncio.sleep(2)
152
  await page.wait_for_selector(
153
- 'textarea[name="user-prompt"]', timeout=15000
 
 
154
  )
 
 
155
  except Exception as e:
156
  print(f"[DUCK] Model select (non-fatal): {e}")
157
-
158
- # โ”€โ”€ 5. ุฅุฑุณุงู„ ุงู„ุฑุณุงู„ุฉ ุจู€ JavaScript ู…ุจุงุดุฑุฉ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
159
- # ู‡ุฐุง ูŠุชุฌุงูˆุฒ ู…ุดูƒู„ุฉ ุฒุฑ Send ุงู„ู€ disabled
160
- textarea = page.locator('textarea[name="user-prompt"]')
161
- await textarea.click()
162
- await asyncio.sleep(0.5)
163
-
164
- # ุงุณุชุฎุฏุงู… JavaScript ู„ุถุจุท ุงู„ู‚ูŠู…ุฉ ูˆุฅุทู„ุงู‚ ุงู„ุฃุญุฏุงุซ
165
- await page.evaluate("""
 
166
  (text) => {
167
  const ta = document.querySelector('textarea[name="user-prompt"]');
168
  if (!ta) return;
169
- // React/Vue ูŠุญุชุงุฌ nativeInputValueSetter
170
  const nativeSetter = Object.getOwnPropertyDescriptor(
171
  window.HTMLTextAreaElement.prototype, 'value'
172
  ).set;
173
  nativeSetter.call(ta, text);
174
- ta.dispatchEvent(new Event('input', { bubbles: true }));
175
  ta.dispatchEvent(new Event('change', { bubbles: true }));
 
176
  }
177
- """, prompt)
178
- await asyncio.sleep(1)
 
 
179
 
180
- # ุงู†ุชุธุฑ ุฃู† ูŠุตุจุญ ุฒุฑ Send enabled
 
 
 
181
  send_enabled = False
182
  for _ in range(10):
183
- disabled = await page.locator(
184
- 'button[type="submit"][aria-label="Send"]'
185
- ).get_attribute("disabled")
186
- if disabled is None:
187
  send_enabled = True
188
  break
189
  await asyncio.sleep(0.5)
190
 
191
  if send_enabled:
192
- await page.locator('button[type="submit"][aria-label="Send"]').click()
193
  print(f"[DUCK] Sent via button โœ“ ({len(prompt)} chars)")
194
  else:
195
- # fallback: Enter ู…ู† ู„ูˆุญุฉ ุงู„ู…ูุงุชูŠุญ
196
- await textarea.press("Enter")
 
 
 
197
  print(f"[DUCK] Sent via Enter โœ“ ({len(prompt)} chars)")
198
 
199
- # โ”€โ”€ 6. ุงู†ุชุธุงุฑ ุจุฏุก ุงู„ุฑุฏ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
200
- await asyncio.sleep(3)
 
201
  try:
202
  stop_btn = page.locator('button[aria-label="Stop generating"]')
203
- await stop_btn.wait_for(state="visible", timeout=20000)
204
  print("[DUCK] Response started โœ“")
 
205
  except Exception:
206
- print("[DUCK] Stop btn not visible, continuing...")
207
 
208
- # โ”€โ”€ 7. ุงู†ุชุธุงุฑ ุงูƒุชู…ุงู„ ุงู„ุฑุฏ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
 
209
  max_wait = 120
210
  elapsed = 0
211
- while elapsed < max_wait:
212
- await asyncio.sleep(2)
213
- elapsed += 2
214
- still_running = await page.locator(
215
- 'button[aria-label="Stop generating"]:not([disabled])'
216
- ).count()
217
- if still_running == 0:
218
- print(f"[DUCK] Done after ~{elapsed}s โœ“")
219
- break
220
 
221
- await asyncio.sleep(1.5)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
222
 
223
- # โ”€โ”€ 8. ุงุณุชุฎุฑุงุฌ ุงู„ุฑุฏ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
224
  response_text = await page.evaluate("""
225
  () => {
226
  // ุทุฑูŠู‚ุฉ 1: article
@@ -243,7 +269,7 @@ class AsyncBrowserThread(threading.Thread):
243
  return valid[valid.length - 1].innerText.trim();
244
  }
245
  }
246
- // ุทุฑูŠู‚ุฉ 3: ุฃุทูˆู„ div
247
  const divs = [...document.querySelectorAll('div')].filter(el =>
248
  el.children.length < 10 &&
249
  el.innerText &&
@@ -260,7 +286,7 @@ class AsyncBrowserThread(threading.Thread):
260
  }
261
  """)
262
 
263
- # fallback
264
  if not response_text or len(response_text.strip()) < 10:
265
  await asyncio.sleep(5)
266
  response_text = await page.evaluate("""
@@ -286,7 +312,7 @@ class AsyncBrowserThread(threading.Thread):
286
  future = asyncio.run_coroutine_threadsafe(
287
  self._chat(model_label, prompt), self.loop
288
  )
289
- return future.result(timeout=180)
290
 
291
 
292
  browser_engine = AsyncBrowserThread()
@@ -360,7 +386,10 @@ def _parse_tool_calls(text: str):
360
 
361
 
362
  def _auth(request: Request) -> bool:
363
- return request.headers.get("authorization", "").replace("Bearer ", "").strip() == API_SECRET_KEY
 
 
 
364
 
365
 
366
  def _get_model_label(model: str) -> str:
@@ -497,7 +526,11 @@ async def list_models(request: Request):
497
  @app.get("/health")
498
  @app.get("/")
499
  async def health():
500
- return {"status": "running", "message": "Duck.ai API Server is active!", "models": ALL_MODELS}
 
 
 
 
501
 
502
 
503
  if __name__ == "__main__":
 
102
  async def _chat(self, model_label: str, prompt: str) -> str:
103
  page = await self.persistent_context.new_page()
104
  try:
105
+ page.set_default_timeout(30000)
106
 
107
  # โ”€โ”€ 1. ูุชุญ ุตูุญุฉ ุฌุฏูŠุฏุฉ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
108
  await page.goto(
 
115
  agree = page.locator('button:has-text("Agree and Continue")')
116
  if await agree.count() > 0 and await agree.first.is_visible():
117
  await agree.first.click()
118
+ print("[DUCK] Terms re-accepted โœ“")
119
  await asyncio.sleep(2)
120
  except Exception:
121
  pass
 
126
  )
127
  print("[DUCK] Input ready โœ“")
128
 
129
+ # โ”€โ”€ 4. ุชุบูŠูŠุฑ ุงู„ู†ู…ูˆุฐุฌ ุจุทุฑูŠู‚ุฉ ุขู…ู†ุฉ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
130
  try:
131
  model_btn = page.locator('button[data-testid="model-select-button"]')
132
  current_text = await model_btn.inner_text()
 
138
 
139
  option = page.locator(
140
  f"li:has-text('{model_label}'), "
 
141
  f"[role='option']:has-text('{model_label}')"
142
  )
143
  if await option.count() > 0:
 
147
  await page.keyboard.press("Escape")
148
  print(f"[DUCK] Model not found, using default")
149
 
150
+ # ุงู†ุชุธุฑ ุฅุนุงุฏุฉ render ุจุนุฏ ุชุบูŠูŠุฑ ุงู„ู†ู…ูˆุฐุฌ
151
+ await asyncio.sleep(3)
152
  await page.wait_for_selector(
153
+ 'textarea[name="user-prompt"]',
154
+ state="visible",
155
+ timeout=15000
156
  )
157
+ print("[DUCK] Textarea re-confirmed โœ“")
158
+
159
  except Exception as e:
160
  print(f"[DUCK] Model select (non-fatal): {e}")
161
+ await page.wait_for_selector(
162
+ 'textarea[name="user-prompt"]',
163
+ state="visible",
164
+ timeout=10000
165
+ )
166
+
167
+ # โ”€โ”€ 5. ูƒุชุงุจุฉ ุงู„ุฑุณุงู„ุฉ ุจู€ JavaScript โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
168
+ # ูŠุชุฌุงูˆุฒ React's synthetic events ูˆูŠุถู…ู† ุชูุนูŠู„ ุฒุฑ Send
169
+ await page.evaluate(
170
+ """
171
  (text) => {
172
  const ta = document.querySelector('textarea[name="user-prompt"]');
173
  if (!ta) return;
 
174
  const nativeSetter = Object.getOwnPropertyDescriptor(
175
  window.HTMLTextAreaElement.prototype, 'value'
176
  ).set;
177
  nativeSetter.call(ta, text);
178
+ ta.dispatchEvent(new InputEvent('input', { bubbles: true }));
179
  ta.dispatchEvent(new Event('change', { bubbles: true }));
180
+ ta.focus();
181
  }
182
+ """,
183
+ prompt
184
+ )
185
+ await asyncio.sleep(1.5)
186
 
187
+ # โ”€โ”€ 6. ุฅุฑุณุงู„ ุงู„ุฑุณุงู„ุฉ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
188
+ send_btn = page.locator('button[type="submit"][aria-label="Send"]')
189
+
190
+ # ุงู†ุชุธุฑ ุญุชู‰ 5 ุซูˆุงู† ู„ุฒุฑ Send
191
  send_enabled = False
192
  for _ in range(10):
193
+ is_disabled = await send_btn.get_attribute("disabled")
194
+ if is_disabled is None:
 
 
195
  send_enabled = True
196
  break
197
  await asyncio.sleep(0.5)
198
 
199
  if send_enabled:
200
+ await send_btn.click()
201
  print(f"[DUCK] Sent via button โœ“ ({len(prompt)} chars)")
202
  else:
203
+ # fallback: Enter ุนู„ู‰ ุงู„ู€ textarea
204
+ ta = page.locator('textarea[name="user-prompt"]')
205
+ await ta.click()
206
+ await asyncio.sleep(0.3)
207
+ await page.keyboard.press("Enter")
208
  print(f"[DUCK] Sent via Enter โœ“ ({len(prompt)} chars)")
209
 
210
+ # โ”€โ”€ 7. ุงู†ุชุธุงุฑ ุจุฏุก ุงู„ุฑุฏ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
211
+ await asyncio.sleep(2)
212
+ response_started = False
213
  try:
214
  stop_btn = page.locator('button[aria-label="Stop generating"]')
215
+ await stop_btn.wait_for(state="visible", timeout=30000)
216
  print("[DUCK] Response started โœ“")
217
+ response_started = True
218
  except Exception:
219
+ print("[DUCK] Stop btn not detected, will watch for content...")
220
 
221
+ # โ”€โ”€ 8. ุงู†ุชุธุงุฑ ุงูƒุชู…ุงู„ ุงู„ุฑุฏ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
222
+ page.set_default_timeout(120000)
223
  max_wait = 120
224
  elapsed = 0
 
 
 
 
 
 
 
 
 
225
 
226
+ if response_started:
227
+ while elapsed < max_wait:
228
+ await asyncio.sleep(2)
229
+ elapsed += 2
230
+ still_running = await page.locator(
231
+ 'button[aria-label="Stop generating"]:not([disabled])'
232
+ ).count()
233
+ if still_running == 0:
234
+ print(f"[DUCK] Response complete (~{elapsed}s) โœ“")
235
+ break
236
+ else:
237
+ # ุงู†ุชุธุฑ ุธู‡ูˆุฑ article (ู…ุญุชูˆู‰ ุงู„ุฑุฏ)
238
+ while elapsed < max_wait:
239
+ await asyncio.sleep(2)
240
+ elapsed += 2
241
+ arts = await page.locator('article').count()
242
+ if arts > 0:
243
+ await asyncio.sleep(5)
244
+ print(f"[DUCK] Content appeared (~{elapsed}s) โœ“")
245
+ break
246
+
247
+ await asyncio.sleep(2)
248
 
249
+ # โ”€โ”€ 9. ุงุณุชุฎุฑุงุฌ ุงู„ุฑุฏ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
250
  response_text = await page.evaluate("""
251
  () => {
252
  // ุทุฑูŠู‚ุฉ 1: article
 
269
  return valid[valid.length - 1].innerText.trim();
270
  }
271
  }
272
+ // ุทุฑูŠู‚ุฉ 3: ุฃุทูˆู„ div ู†ุธูŠู
273
  const divs = [...document.querySelectorAll('div')].filter(el =>
274
  el.children.length < 10 &&
275
  el.innerText &&
 
286
  }
287
  """)
288
 
289
+ # fallback ู†ู‡ุงุฆูŠ
290
  if not response_text or len(response_text.strip()) < 10:
291
  await asyncio.sleep(5)
292
  response_text = await page.evaluate("""
 
312
  future = asyncio.run_coroutine_threadsafe(
313
  self._chat(model_label, prompt), self.loop
314
  )
315
+ return future.result(timeout=200)
316
 
317
 
318
  browser_engine = AsyncBrowserThread()
 
386
 
387
 
388
  def _auth(request: Request) -> bool:
389
+ return (
390
+ request.headers.get("authorization", "")
391
+ .replace("Bearer ", "").strip() == API_SECRET_KEY
392
+ )
393
 
394
 
395
  def _get_model_label(model: str) -> str:
 
526
  @app.get("/health")
527
  @app.get("/")
528
  async def health():
529
+ return {
530
+ "status": "running",
531
+ "message": "Duck.ai API Server is active!",
532
+ "models": ALL_MODELS,
533
+ }
534
 
535
 
536
  if __name__ == "__main__":