Spaces:
Running
Running
Update main.py
Browse files
main.py
CHANGED
|
@@ -77,7 +77,6 @@ class AsyncBrowserThread(threading.Thread):
|
|
| 77 |
"Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"
|
| 78 |
)
|
| 79 |
|
| 80 |
-
# ูุจูู ุงูุดุฑูุท ู
ุฑุฉ ูุงุญุฏุฉ ุนูุฏ ุงูุฅููุงุน
|
| 81 |
setup_page = await self.persistent_context.new_page()
|
| 82 |
try:
|
| 83 |
print("[SERVER] Opening duck.ai for setup...")
|
|
@@ -102,9 +101,10 @@ 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 |
-
|
|
|
|
| 106 |
|
| 107 |
-
# โโ 1. ูุชุญ ุตูุญุฉ
|
| 108 |
await page.goto(
|
| 109 |
"https://duckduckgo.com/aichat", wait_until="domcontentloaded"
|
| 110 |
)
|
|
@@ -121,12 +121,10 @@ class AsyncBrowserThread(threading.Thread):
|
|
| 121 |
pass
|
| 122 |
|
| 123 |
# โโ 3. ุงูุชุธุงุฑ textarea โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 124 |
-
await page.wait_for_selector(
|
| 125 |
-
'textarea[name="user-prompt"]', timeout=30000
|
| 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()
|
|
@@ -145,27 +143,18 @@ class AsyncBrowserThread(threading.Thread):
|
|
| 145 |
print(f"[DUCK] Model โ {model_label} โ")
|
| 146 |
else:
|
| 147 |
await page.keyboard.press("Escape")
|
| 148 |
-
print(
|
| 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 |
-
|
| 162 |
-
|
| 163 |
-
state="visible",
|
| 164 |
-
timeout=10000
|
| 165 |
-
)
|
| 166 |
-
|
| 167 |
-
# โโ 5. ูุชุงุจุฉ ุงูุฑุณุงูุฉ ุจู JavaScript โโโโโโโโโโโโโโโโโโโโ
|
| 168 |
-
# ูุชุฌุงูุฒ React's synthetic events ููุถู
ู ุชูุนูู ุฒุฑ Send
|
| 169 |
await page.evaluate(
|
| 170 |
"""
|
| 171 |
(text) => {
|
|
@@ -175,40 +164,40 @@ class AsyncBrowserThread(threading.Thread):
|
|
| 175 |
window.HTMLTextAreaElement.prototype, 'value'
|
| 176 |
).set;
|
| 177 |
nativeSetter.call(ta, text);
|
| 178 |
-
ta.dispatchEvent(new InputEvent('input',
|
| 179 |
ta.dispatchEvent(new Event('change', { bubbles: true }));
|
| 180 |
ta.focus();
|
| 181 |
}
|
| 182 |
""",
|
| 183 |
prompt
|
| 184 |
)
|
| 185 |
-
await asyncio.sleep(
|
| 186 |
|
| 187 |
# โโ 6. ุฅุฑุณุงู ุงูุฑุณุงูุฉ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
for _ in range(10):
|
| 193 |
is_disabled = await send_btn.get_attribute("disabled")
|
| 194 |
if is_disabled is None:
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
|
|
|
|
|
|
| 198 |
|
| 199 |
-
|
| 200 |
-
|
| 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.
|
| 207 |
await page.keyboard.press("Enter")
|
| 208 |
-
print(f"[DUCK] Sent via Enter โ
|
|
|
|
|
|
|
| 209 |
|
| 210 |
# โโ 7. ุงูุชุธุงุฑ ุจุฏุก ุงูุฑุฏ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 211 |
-
await asyncio.sleep(
|
| 212 |
response_started = False
|
| 213 |
try:
|
| 214 |
stop_btn = page.locator('button[aria-label="Stop generating"]')
|
|
@@ -216,11 +205,10 @@ class AsyncBrowserThread(threading.Thread):
|
|
| 216 |
print("[DUCK] Response started โ")
|
| 217 |
response_started = True
|
| 218 |
except Exception:
|
| 219 |
-
print("[DUCK] Stop btn not detected,
|
| 220 |
|
| 221 |
# โโ 8. ุงูุชุธุงุฑ ุงูุชู
ุงู ุงูุฑุฏ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 222 |
-
|
| 223 |
-
max_wait = 120
|
| 224 |
elapsed = 0
|
| 225 |
|
| 226 |
if response_started:
|
|
@@ -231,17 +219,16 @@ class AsyncBrowserThread(threading.Thread):
|
|
| 231 |
'button[aria-label="Stop generating"]:not([disabled])'
|
| 232 |
).count()
|
| 233 |
if still_running == 0:
|
| 234 |
-
print(f"[DUCK]
|
| 235 |
break
|
| 236 |
else:
|
| 237 |
-
# ุงูุชุธุฑ ุธููุฑ article
|
| 238 |
while elapsed < max_wait:
|
| 239 |
await asyncio.sleep(2)
|
| 240 |
elapsed += 2
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
print(f"[DUCK] Content appeared (~{elapsed}s) โ")
|
| 245 |
break
|
| 246 |
|
| 247 |
await asyncio.sleep(2)
|
|
@@ -249,31 +236,26 @@ class AsyncBrowserThread(threading.Thread):
|
|
| 249 |
# โโ 9. ุงุณุชุฎุฑุงุฌ ุงูุฑุฏ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 250 |
response_text = await page.evaluate("""
|
| 251 |
() => {
|
| 252 |
-
// ุทุฑููุฉ 1: article
|
| 253 |
const articles = document.querySelectorAll('article');
|
| 254 |
if (articles.length > 0) {
|
| 255 |
return articles[articles.length - 1].innerText.trim();
|
| 256 |
}
|
| 257 |
-
// ุทุฑููุฉ 2: class ูุญุชูู message
|
| 258 |
const msgDivs = document.querySelectorAll(
|
| 259 |
'[class*="message"]:not([class*="user"])' +
|
| 260 |
':not([class*="User"]):not([class*="input"])'
|
| 261 |
);
|
| 262 |
if (msgDivs.length > 0) {
|
| 263 |
const valid = [...msgDivs].filter(el =>
|
| 264 |
-
el.innerText &&
|
| 265 |
-
el.innerText.trim().length > 20 &&
|
| 266 |
!el.querySelector('textarea')
|
| 267 |
);
|
| 268 |
if (valid.length > 0) {
|
| 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 &&
|
| 276 |
-
el.innerText.trim().length > 50 &&
|
| 277 |
!el.querySelector('textarea') &&
|
| 278 |
!el.querySelector('button[type="submit"]')
|
| 279 |
);
|
|
@@ -286,7 +268,6 @@ class AsyncBrowserThread(threading.Thread):
|
|
| 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,7 +293,7 @@ class AsyncBrowserThread(threading.Thread):
|
|
| 312 |
future = asyncio.run_coroutine_threadsafe(
|
| 313 |
self._chat(model_label, prompt), self.loop
|
| 314 |
)
|
| 315 |
-
return future.result(timeout=
|
| 316 |
|
| 317 |
|
| 318 |
browser_engine = AsyncBrowserThread()
|
|
@@ -344,7 +325,9 @@ def _build_prompt(messages: list) -> str:
|
|
| 344 |
if not content.strip():
|
| 345 |
continue
|
| 346 |
if role == "system":
|
| 347 |
-
parts.append(
|
|
|
|
|
|
|
| 348 |
elif role == "assistant":
|
| 349 |
parts.append(f"[Assistant]: {content}")
|
| 350 |
else:
|
|
|
|
| 77 |
"Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"
|
| 78 |
)
|
| 79 |
|
|
|
|
| 80 |
setup_page = await self.persistent_context.new_page()
|
| 81 |
try:
|
| 82 |
print("[SERVER] Opening duck.ai for setup...")
|
|
|
|
| 101 |
async def _chat(self, model_label: str, prompt: str) -> str:
|
| 102 |
page = await self.persistent_context.new_page()
|
| 103 |
try:
|
| 104 |
+
# !! timeout ูุงุญุฏ ูุจูุฑ ููู ุงูุตูุญุฉ โ ูุง ููุบูููุฑ ุฃุจุฏุงู
|
| 105 |
+
page.set_default_timeout(180000)
|
| 106 |
|
| 107 |
+
# โโ 1. ูุชุญ ุงูุตูุญุฉ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 108 |
await page.goto(
|
| 109 |
"https://duckduckgo.com/aichat", wait_until="domcontentloaded"
|
| 110 |
)
|
|
|
|
| 121 |
pass
|
| 122 |
|
| 123 |
# โโ 3. ุงูุชุธุงุฑ textarea โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 124 |
+
await page.wait_for_selector('textarea[name="user-prompt"]')
|
|
|
|
|
|
|
| 125 |
print("[DUCK] Input ready โ")
|
| 126 |
|
| 127 |
+
# โโ 4. ุชุบููุฑ ุงููู
ูุฐุฌ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 128 |
try:
|
| 129 |
model_btn = page.locator('button[data-testid="model-select-button"]')
|
| 130 |
current_text = await model_btn.inner_text()
|
|
|
|
| 143 |
print(f"[DUCK] Model โ {model_label} โ")
|
| 144 |
else:
|
| 145 |
await page.keyboard.press("Escape")
|
| 146 |
+
print("[DUCK] Model not found, using default")
|
| 147 |
|
|
|
|
| 148 |
await asyncio.sleep(3)
|
| 149 |
await page.wait_for_selector(
|
| 150 |
+
'textarea[name="user-prompt"]', state="visible"
|
|
|
|
|
|
|
| 151 |
)
|
| 152 |
print("[DUCK] Textarea re-confirmed โ")
|
| 153 |
|
| 154 |
except Exception as e:
|
| 155 |
print(f"[DUCK] Model select (non-fatal): {e}")
|
| 156 |
+
|
| 157 |
+
# โโ 5. ุญูู ุงููุต ุนุจุฑ JavaScript โโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 158 |
await page.evaluate(
|
| 159 |
"""
|
| 160 |
(text) => {
|
|
|
|
| 164 |
window.HTMLTextAreaElement.prototype, 'value'
|
| 165 |
).set;
|
| 166 |
nativeSetter.call(ta, text);
|
| 167 |
+
ta.dispatchEvent(new InputEvent('input', { bubbles: true }));
|
| 168 |
ta.dispatchEvent(new Event('change', { bubbles: true }));
|
| 169 |
ta.focus();
|
| 170 |
}
|
| 171 |
""",
|
| 172 |
prompt
|
| 173 |
)
|
| 174 |
+
await asyncio.sleep(2)
|
| 175 |
|
| 176 |
# โโ 6. ุฅุฑุณุงู ุงูุฑุณุงูุฉ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 177 |
+
# ู
ุญุงููุฉ 1: ุฒุฑ Submit
|
| 178 |
+
sent = False
|
| 179 |
+
try:
|
| 180 |
+
send_btn = page.locator('button[type="submit"][aria-label="Send"]')
|
|
|
|
| 181 |
is_disabled = await send_btn.get_attribute("disabled")
|
| 182 |
if is_disabled is None:
|
| 183 |
+
await send_btn.click()
|
| 184 |
+
print(f"[DUCK] Sent via button โ")
|
| 185 |
+
sent = True
|
| 186 |
+
except Exception:
|
| 187 |
+
pass
|
| 188 |
|
| 189 |
+
# ู
ุญุงููุฉ 2: Enter ู
ุจุงุดุฑุฉ
|
| 190 |
+
if not sent:
|
|
|
|
|
|
|
|
|
|
| 191 |
ta = page.locator('textarea[name="user-prompt"]')
|
| 192 |
await ta.click()
|
| 193 |
+
await asyncio.sleep(0.5)
|
| 194 |
await page.keyboard.press("Enter")
|
| 195 |
+
print(f"[DUCK] Sent via Enter โ")
|
| 196 |
+
|
| 197 |
+
print(f"[DUCK] Prompt length: {len(prompt)} chars")
|
| 198 |
|
| 199 |
# โโ 7. ุงูุชุธุงุฑ ุจุฏุก ุงูุฑุฏ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 200 |
+
await asyncio.sleep(3)
|
| 201 |
response_started = False
|
| 202 |
try:
|
| 203 |
stop_btn = page.locator('button[aria-label="Stop generating"]')
|
|
|
|
| 205 |
print("[DUCK] Response started โ")
|
| 206 |
response_started = True
|
| 207 |
except Exception:
|
| 208 |
+
print("[DUCK] Stop btn not detected, watching for content...")
|
| 209 |
|
| 210 |
# โโ 8. ุงูุชุธุงุฑ ุงูุชู
ุงู ุงูุฑุฏ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 211 |
+
max_wait = 150
|
|
|
|
| 212 |
elapsed = 0
|
| 213 |
|
| 214 |
if response_started:
|
|
|
|
| 219 |
'button[aria-label="Stop generating"]:not([disabled])'
|
| 220 |
).count()
|
| 221 |
if still_running == 0:
|
| 222 |
+
print(f"[DUCK] Complete (~{elapsed}s) โ")
|
| 223 |
break
|
| 224 |
else:
|
| 225 |
+
# ุงูุชุธุฑ ุธููุฑ article
|
| 226 |
while elapsed < max_wait:
|
| 227 |
await asyncio.sleep(2)
|
| 228 |
elapsed += 2
|
| 229 |
+
if await page.locator('article').count() > 0:
|
| 230 |
+
await asyncio.sleep(6)
|
| 231 |
+
print(f"[DUCK] Article appeared (~{elapsed}s) โ")
|
|
|
|
| 232 |
break
|
| 233 |
|
| 234 |
await asyncio.sleep(2)
|
|
|
|
| 236 |
# โโ 9. ุงุณุชุฎุฑุงุฌ ุงูุฑุฏ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 237 |
response_text = await page.evaluate("""
|
| 238 |
() => {
|
|
|
|
| 239 |
const articles = document.querySelectorAll('article');
|
| 240 |
if (articles.length > 0) {
|
| 241 |
return articles[articles.length - 1].innerText.trim();
|
| 242 |
}
|
|
|
|
| 243 |
const msgDivs = document.querySelectorAll(
|
| 244 |
'[class*="message"]:not([class*="user"])' +
|
| 245 |
':not([class*="User"]):not([class*="input"])'
|
| 246 |
);
|
| 247 |
if (msgDivs.length > 0) {
|
| 248 |
const valid = [...msgDivs].filter(el =>
|
| 249 |
+
el.innerText && el.innerText.trim().length > 20 &&
|
|
|
|
| 250 |
!el.querySelector('textarea')
|
| 251 |
);
|
| 252 |
if (valid.length > 0) {
|
| 253 |
return valid[valid.length - 1].innerText.trim();
|
| 254 |
}
|
| 255 |
}
|
|
|
|
| 256 |
const divs = [...document.querySelectorAll('div')].filter(el =>
|
| 257 |
el.children.length < 10 &&
|
| 258 |
+
el.innerText && el.innerText.trim().length > 50 &&
|
|
|
|
| 259 |
!el.querySelector('textarea') &&
|
| 260 |
!el.querySelector('button[type="submit"]')
|
| 261 |
);
|
|
|
|
| 268 |
}
|
| 269 |
""")
|
| 270 |
|
|
|
|
| 271 |
if not response_text or len(response_text.strip()) < 10:
|
| 272 |
await asyncio.sleep(5)
|
| 273 |
response_text = await page.evaluate("""
|
|
|
|
| 293 |
future = asyncio.run_coroutine_threadsafe(
|
| 294 |
self._chat(model_label, prompt), self.loop
|
| 295 |
)
|
| 296 |
+
return future.result(timeout=210)
|
| 297 |
|
| 298 |
|
| 299 |
browser_engine = AsyncBrowserThread()
|
|
|
|
| 325 |
if not content.strip():
|
| 326 |
continue
|
| 327 |
if role == "system":
|
| 328 |
+
parts.append(
|
| 329 |
+
f"=== SYSTEM INSTRUCTIONS ===\n{content}\n=== END INSTRUCTIONS ==="
|
| 330 |
+
)
|
| 331 |
elif role == "assistant":
|
| 332 |
parts.append(f"[Assistant]: {content}")
|
| 333 |
else:
|