entari-plugin-hyw 4.0.0rc14__py3-none-any.whl → 4.0.0rc15__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of entari-plugin-hyw might be problematic. Click here for more details.
- entari_plugin_hyw/__init__.py +149 -367
- {entari_plugin_hyw-4.0.0rc14.dist-info → entari_plugin_hyw-4.0.0rc15.dist-info}/METADATA +1 -1
- {entari_plugin_hyw-4.0.0rc14.dist-info → entari_plugin_hyw-4.0.0rc15.dist-info}/RECORD +11 -12
- {entari_plugin_hyw-4.0.0rc14.dist-info → entari_plugin_hyw-4.0.0rc15.dist-info}/WHEEL +1 -1
- hyw_core/agent.py +115 -18
- hyw_core/browser_control/assets/card-dist/index.html +35 -31
- hyw_core/browser_control/service.py +43 -0
- hyw_core/definitions.py +57 -12
- hyw_core/search.py +4 -6
- hyw_core/stages/summary.py +45 -18
- entari_plugin_hyw/Untitled-1 +0 -1865
- {entari_plugin_hyw-4.0.0rc14.dist-info → entari_plugin_hyw-4.0.0rc15.dist-info}/top_level.txt +0 -0
entari_plugin_hyw/__init__.py
CHANGED
|
@@ -55,6 +55,43 @@ except Exception:
|
|
|
55
55
|
_event_deduper = RecentEventDeduper()
|
|
56
56
|
|
|
57
57
|
|
|
58
|
+
class TaskManager:
|
|
59
|
+
"""Manages async tasks for cancellation"""
|
|
60
|
+
def __init__(self):
|
|
61
|
+
self.tasks: Dict[str, asyncio.Task] = {}
|
|
62
|
+
self.cleanups: Dict[str, callable] = {}
|
|
63
|
+
|
|
64
|
+
def register(self, msg_id: str, task: asyncio.Task, cleanup: Optional[callable] = None):
|
|
65
|
+
self.tasks[msg_id] = task
|
|
66
|
+
if cleanup:
|
|
67
|
+
self.cleanups[msg_id] = cleanup
|
|
68
|
+
|
|
69
|
+
def unregister(self, msg_id: str):
|
|
70
|
+
self.tasks.pop(msg_id, None)
|
|
71
|
+
self.cleanups.pop(msg_id, None)
|
|
72
|
+
|
|
73
|
+
async def cancel(self, msg_id: str) -> bool:
|
|
74
|
+
task = self.tasks.get(msg_id)
|
|
75
|
+
if task and not task.done():
|
|
76
|
+
task.cancel()
|
|
77
|
+
|
|
78
|
+
# Run cleanup if available
|
|
79
|
+
cleanup = self.cleanups.get(msg_id)
|
|
80
|
+
if cleanup:
|
|
81
|
+
try:
|
|
82
|
+
if asyncio.iscoroutinefunction(cleanup):
|
|
83
|
+
await cleanup()
|
|
84
|
+
else:
|
|
85
|
+
cleanup()
|
|
86
|
+
except Exception as e:
|
|
87
|
+
logger.warning(f"Cleanup failed for task {msg_id}: {e}")
|
|
88
|
+
|
|
89
|
+
self.unregister(msg_id)
|
|
90
|
+
return True
|
|
91
|
+
return False
|
|
92
|
+
|
|
93
|
+
_task_manager = TaskManager()
|
|
94
|
+
|
|
58
95
|
|
|
59
96
|
@dataclass
|
|
60
97
|
class HywConfig(BasicConfModel):
|
|
@@ -63,6 +100,7 @@ class HywConfig(BasicConfModel):
|
|
|
63
100
|
models: List[Dict[str, Any]] = field(default_factory=list)
|
|
64
101
|
question_command: str = "/q"
|
|
65
102
|
web_command: str = "/w"
|
|
103
|
+
stop_command: str = "/x"
|
|
66
104
|
help_command: str = "/h"
|
|
67
105
|
language: str = "Simplified Chinese"
|
|
68
106
|
temperature: float = 0.4
|
|
@@ -199,6 +237,29 @@ async def process_request(
|
|
|
199
237
|
local_renderer = await get_content_renderer()
|
|
200
238
|
render_tab_task = asyncio.create_task(local_renderer.prepare_tab())
|
|
201
239
|
|
|
240
|
+
# Register cleanup for this specific request's resources
|
|
241
|
+
msg_id = str(session.event.message.id) if hasattr(session.event, 'message') else str(session.event.id)
|
|
242
|
+
|
|
243
|
+
async def cleanup_resources():
|
|
244
|
+
try:
|
|
245
|
+
# If tab task is still running, cancel it
|
|
246
|
+
if not render_tab_task.done():
|
|
247
|
+
render_tab_task.cancel()
|
|
248
|
+
else:
|
|
249
|
+
# If tab is ready, close it
|
|
250
|
+
try:
|
|
251
|
+
tab_id = render_tab_task.result()
|
|
252
|
+
if tab_id:
|
|
253
|
+
await local_renderer.close_tab(tab_id)
|
|
254
|
+
except:
|
|
255
|
+
pass
|
|
256
|
+
except Exception as e:
|
|
257
|
+
logger.warning(f"Resource cleanup failed: {e}")
|
|
258
|
+
|
|
259
|
+
# Update task manager with cleanup callback
|
|
260
|
+
if _task_manager.tasks.get(msg_id):
|
|
261
|
+
_task_manager.cleanups[msg_id] = cleanup_resources
|
|
262
|
+
|
|
202
263
|
async def send_noti(msg: str):
|
|
203
264
|
try:
|
|
204
265
|
if conf.quote:
|
|
@@ -267,21 +328,26 @@ async def process_request(
|
|
|
267
328
|
msg_chain = MessageChain(Quote(session.event.message.id)) + msg_chain
|
|
268
329
|
|
|
269
330
|
sent = await session.send(msg_chain)
|
|
270
|
-
|
|
331
|
+
|
|
271
332
|
sent_id = next((str(e.id) for e in sent if hasattr(e, 'id')), None) if sent else None
|
|
272
333
|
msg_id = str(session.event.message.id) if hasattr(session.event, 'message') else str(session.event.id)
|
|
273
|
-
|
|
334
|
+
|
|
274
335
|
updated_history = hist_payload + [
|
|
275
336
|
{"role": "user", "content": msg_text},
|
|
276
337
|
{"role": "assistant", "content": response.content}
|
|
277
338
|
]
|
|
278
|
-
|
|
339
|
+
|
|
279
340
|
# Save to Memory
|
|
280
341
|
history_manager.remember(
|
|
281
342
|
sent_id, updated_history, [msg_id],
|
|
282
343
|
{"model": model}, context_id, code=display_session_id,
|
|
283
344
|
)
|
|
284
|
-
|
|
345
|
+
|
|
346
|
+
# Store web results in search cache for continuous conversation context
|
|
347
|
+
# This allows users to reply to this message and have the AI "remember" the search results
|
|
348
|
+
if response.web_results and sent_id:
|
|
349
|
+
search_cache.store(sent_id, response.web_results, f"Context for {msg_id}")
|
|
350
|
+
|
|
285
351
|
# Save to Disk (Debug/Logging)
|
|
286
352
|
if conf.save_conversation:
|
|
287
353
|
# Extract traces from response
|
|
@@ -310,7 +376,7 @@ async def process_request(
|
|
|
310
376
|
|
|
311
377
|
alc = Alconna(conf.question_command, Args["all_param;?", AllParam])
|
|
312
378
|
|
|
313
|
-
@command.on(alc)
|
|
379
|
+
@command.on(alc)
|
|
314
380
|
async def handle_question_command(session: Session[MessageCreatedEvent], result: Arparma):
|
|
315
381
|
try:
|
|
316
382
|
mid = str(session.event.message.id) if getattr(session.event, "message", None) else str(session.event.id)
|
|
@@ -319,367 +385,69 @@ async def handle_question_command(session: Session[MessageCreatedEvent], result:
|
|
|
319
385
|
return
|
|
320
386
|
except Exception:
|
|
321
387
|
pass
|
|
322
|
-
|
|
388
|
+
|
|
323
389
|
args = result.all_matched_args
|
|
324
390
|
all_param = args.get("all_param")
|
|
325
|
-
|
|
326
|
-
#
|
|
327
|
-
if all_param:
|
|
328
|
-
if isinstance(all_param, MessageChain):
|
|
329
|
-
query_text = str(all_param.get(Text)).strip()
|
|
330
|
-
else:
|
|
331
|
-
query_text = str(all_param).strip()
|
|
332
|
-
else:
|
|
333
|
-
query_text = ""
|
|
334
|
-
|
|
335
|
-
# Check if replying to a cached search result
|
|
391
|
+
|
|
392
|
+
# Check if replying to a cached search result (/w context summary)
|
|
336
393
|
reply_msg_id = None
|
|
337
394
|
if session.reply and hasattr(session.reply.origin, 'id'):
|
|
338
395
|
reply_msg_id = str(session.reply.origin.id)
|
|
339
|
-
|
|
340
|
-
# Quote mode: Use cached search results
|
|
396
|
+
|
|
341
397
|
if reply_msg_id:
|
|
342
398
|
cached = search_cache.get(reply_msg_id)
|
|
343
399
|
if cached:
|
|
344
|
-
#
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
if query_text and indices is None:
|
|
349
|
-
# Check if it looks like indices but exceeded limit
|
|
350
|
-
if re.match(r'^[\d,、\s\-–]+$', query_text):
|
|
351
|
-
await session.send("最多选择3个结果进行总结")
|
|
352
|
-
search_cache.cleanup()
|
|
353
|
-
return
|
|
354
|
-
|
|
355
|
-
if conf.reaction:
|
|
356
|
-
asyncio.create_task(react(session, "✨"))
|
|
357
|
-
|
|
358
|
-
core = get_hyw_core()
|
|
359
|
-
local_renderer = await get_content_renderer()
|
|
360
|
-
tab_task = asyncio.create_task(local_renderer.prepare_tab())
|
|
361
|
-
|
|
362
|
-
# Collect screenshots for selected pages
|
|
363
|
-
screenshots = []
|
|
364
|
-
if indices:
|
|
365
|
-
# Screenshot mode: capture pages for selected indices
|
|
366
|
-
for idx in indices:
|
|
367
|
-
if idx < len(cached.results):
|
|
368
|
-
url = cached.results[idx].get("url", "")
|
|
369
|
-
if url:
|
|
370
|
-
b64_img = await core.screenshot(url)
|
|
371
|
-
if b64_img:
|
|
372
|
-
screenshots.append(b64_img)
|
|
373
|
-
|
|
374
|
-
if not screenshots:
|
|
375
|
-
try: await tab_task
|
|
376
|
-
except: pass
|
|
377
|
-
await session.send("无法截图所选页面")
|
|
378
|
-
search_cache.cleanup()
|
|
379
|
-
return
|
|
380
|
-
|
|
381
|
-
user_query = f"总结关于 \"{cached.query}\" 的内容"
|
|
382
|
-
else:
|
|
383
|
-
# No indices - summarize based on cached snippets (no screenshots)
|
|
384
|
-
context_parts = []
|
|
385
|
-
for i, res in enumerate(cached.results[:10]):
|
|
386
|
-
title = res.get("title", f"Result {i+1}")
|
|
387
|
-
snippet = res.get("content", "") or res.get("snippet", "")
|
|
388
|
-
context_parts.append(f"## {title}\n{snippet}")
|
|
389
|
-
|
|
390
|
-
context_message = f"基于搜索 \"{cached.query}\" 的结果摘要回答用户问题:\n\n" + "\n\n".join(context_parts)
|
|
391
|
-
user_query = query_text if query_text else f"总结关于 \"{cached.query}\" 的搜索结果"
|
|
392
|
-
|
|
393
|
-
# Build request with screenshots (if any)
|
|
394
|
-
if screenshots:
|
|
395
|
-
request = QueryRequest(
|
|
396
|
-
user_input=user_query,
|
|
397
|
-
images=screenshots,
|
|
398
|
-
conversation_history=[],
|
|
399
|
-
model_name=None,
|
|
400
|
-
)
|
|
401
|
-
else:
|
|
402
|
-
request = QueryRequest(
|
|
403
|
-
user_input=f"{context_message}\n\n用户问题: {user_query}",
|
|
404
|
-
images=[],
|
|
405
|
-
conversation_history=[],
|
|
406
|
-
model_name=None,
|
|
407
|
-
)
|
|
408
|
-
|
|
409
|
-
with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as tf:
|
|
410
|
-
output_path = tf.name
|
|
411
|
-
|
|
412
|
-
response = await core.query(request, output_path=None)
|
|
413
|
-
|
|
414
|
-
try:
|
|
415
|
-
tab_id = await tab_task
|
|
416
|
-
except Exception:
|
|
417
|
-
tab_id = None
|
|
418
|
-
|
|
419
|
-
if response.success and response.content:
|
|
420
|
-
render_ok = await core.render(
|
|
421
|
-
markdown_content=response.content,
|
|
422
|
-
output_path=output_path,
|
|
423
|
-
stats={"total_time": response.total_time},
|
|
424
|
-
references=[],
|
|
425
|
-
page_references=[],
|
|
426
|
-
tab_id=tab_id
|
|
427
|
-
)
|
|
428
|
-
|
|
429
|
-
if render_ok and os.path.exists(output_path):
|
|
430
|
-
with open(output_path, "rb") as f:
|
|
431
|
-
img_data = base64.b64encode(f.read()).decode()
|
|
432
|
-
|
|
433
|
-
msg_chain = MessageChain(Image(src=f'data:image/png;base64,{img_data}'))
|
|
434
|
-
if conf.quote:
|
|
435
|
-
msg_chain = MessageChain(Quote(session.event.message.id)) + msg_chain
|
|
436
|
-
|
|
437
|
-
await session.send(msg_chain)
|
|
438
|
-
os.remove(output_path)
|
|
439
|
-
else:
|
|
440
|
-
await session.send(response.content[:500])
|
|
441
|
-
else:
|
|
442
|
-
await session.send(f"总结失败: {response.error or 'Unknown error'}")
|
|
443
|
-
|
|
444
|
-
search_cache.cleanup()
|
|
445
|
-
return
|
|
446
|
-
|
|
447
|
-
# === URL Mode: Direct URL Screenshot + Summarize ===
|
|
448
|
-
# Detect URL in query and handle directly without Agent
|
|
449
|
-
url_match = re.search(r'https?://\S+', query_text)
|
|
450
|
-
if url_match:
|
|
451
|
-
url = url_match.group(0)
|
|
452
|
-
# Extract user intent (text before/after URL)
|
|
453
|
-
user_intent = query_text.replace(url, '').strip()
|
|
454
|
-
if not user_intent:
|
|
455
|
-
user_intent = "总结这个页面的内容"
|
|
456
|
-
|
|
457
|
-
if conf.reaction:
|
|
458
|
-
asyncio.create_task(react(session, "✨"))
|
|
459
|
-
|
|
460
|
-
core = get_hyw_core()
|
|
461
|
-
local_renderer = await get_content_renderer()
|
|
462
|
-
|
|
463
|
-
# Run screenshot and prepare tab in parallel
|
|
464
|
-
screenshot_task = asyncio.create_task(core.screenshot(url))
|
|
465
|
-
tab_task = asyncio.create_task(local_renderer.prepare_tab())
|
|
466
|
-
|
|
467
|
-
# Send notification
|
|
468
|
-
short_url = url[:40] + "..." if len(url) > 40 else url
|
|
469
|
-
await session.send(f"📸 正在截图: {short_url}")
|
|
470
|
-
|
|
471
|
-
b64_img = await screenshot_task
|
|
472
|
-
|
|
473
|
-
if not b64_img:
|
|
474
|
-
try: await tab_task
|
|
475
|
-
except: pass
|
|
476
|
-
await session.send(f"❌ 截图失败: {url}")
|
|
477
|
-
return
|
|
478
|
-
|
|
479
|
-
# Summarize with screenshot
|
|
480
|
-
request = QueryRequest(
|
|
481
|
-
user_input=f"{user_intent}\n\nURL: {url}",
|
|
482
|
-
images=[b64_img],
|
|
483
|
-
conversation_history=[],
|
|
484
|
-
model_name=None,
|
|
485
|
-
)
|
|
486
|
-
|
|
487
|
-
with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as tf:
|
|
488
|
-
output_path = tf.name
|
|
489
|
-
|
|
490
|
-
response = await core.query(request, output_path=None)
|
|
491
|
-
|
|
492
|
-
try:
|
|
493
|
-
tab_id = await tab_task
|
|
494
|
-
except Exception:
|
|
495
|
-
tab_id = None
|
|
496
|
-
|
|
497
|
-
if response.success and response.content:
|
|
498
|
-
render_ok = await core.render(
|
|
499
|
-
markdown_content=response.content,
|
|
500
|
-
output_path=output_path,
|
|
501
|
-
stats={"total_time": response.total_time},
|
|
502
|
-
references=[],
|
|
503
|
-
page_references=[{"url": url, "title": "截图页面", "raw_screenshot_b64": b64_img}],
|
|
504
|
-
tab_id=tab_id
|
|
505
|
-
)
|
|
506
|
-
|
|
507
|
-
if render_ok and os.path.exists(output_path):
|
|
508
|
-
with open(output_path, "rb") as f:
|
|
509
|
-
img_data = base64.b64encode(f.read()).decode()
|
|
510
|
-
|
|
511
|
-
msg_chain = MessageChain(Image(src=f'data:image/png;base64,{img_data}'))
|
|
512
|
-
if conf.quote:
|
|
513
|
-
msg_chain = MessageChain(Quote(session.event.message.id)) + msg_chain
|
|
514
|
-
|
|
515
|
-
await session.send(msg_chain)
|
|
516
|
-
os.remove(output_path)
|
|
517
|
-
else:
|
|
518
|
-
await session.send(response.content[:500])
|
|
519
|
-
else:
|
|
520
|
-
await session.send(f"总结失败: {response.error or 'Unknown error'}")
|
|
521
|
-
|
|
522
|
-
return
|
|
523
|
-
|
|
524
|
-
# === Filter Mode: Search + Find matching links + Summarize ===
|
|
525
|
-
# Only trigger filter syntax for short queries (≤20 chars, excluding URLs), otherwise use Agent
|
|
526
|
-
filters = []
|
|
527
|
-
filter_error = None
|
|
528
|
-
# Calculate length excluding URLs
|
|
529
|
-
query_without_urls = re.sub(r'https?://\S+', '', query_text).strip()
|
|
530
|
-
if len(query_without_urls) <= 20:
|
|
531
|
-
filters, search_query, filter_error = parse_filter_syntax(query_text, max_count=3)
|
|
532
|
-
|
|
533
|
-
if filter_error:
|
|
534
|
-
await session.send(filter_error)
|
|
535
|
-
return
|
|
536
|
-
|
|
537
|
-
if filters:
|
|
538
|
-
if conf.reaction:
|
|
539
|
-
asyncio.create_task(react(session, "✨"))
|
|
540
|
-
|
|
541
|
-
core = get_hyw_core()
|
|
542
|
-
local_renderer = await get_content_renderer()
|
|
543
|
-
|
|
544
|
-
# Send pre-notification BEFORE search
|
|
545
|
-
filter_desc = ", ".join([f[1] if f[0] != 'index' else f"第{f[1]}个" for f in filters])
|
|
546
|
-
await session.send(f"🔍 正在搜索 \"{search_query}\" 并匹配 [{filter_desc}]...")
|
|
547
|
-
|
|
548
|
-
# Run search and prepare tab in parallel
|
|
549
|
-
search_task = asyncio.create_task(core.search([search_query]))
|
|
550
|
-
tab_task = asyncio.create_task(local_renderer.prepare_tab())
|
|
551
|
-
|
|
552
|
-
results = await search_task
|
|
553
|
-
flat_results = results[0] if results else []
|
|
554
|
-
|
|
555
|
-
if not flat_results:
|
|
556
|
-
try: await tab_task
|
|
557
|
-
except: pass
|
|
558
|
-
await session.send("Search returned no results.")
|
|
559
|
-
return
|
|
560
|
-
|
|
561
|
-
visible = [r for r in flat_results if not r.get("_hidden", False)]
|
|
562
|
-
|
|
563
|
-
# Collect URLs to screenshot
|
|
564
|
-
urls_to_screenshot = []
|
|
565
|
-
for filter_type, filter_value, count in filters:
|
|
566
|
-
if filter_type == 'index':
|
|
567
|
-
idx = filter_value - 1
|
|
568
|
-
if 0 <= idx < len(visible):
|
|
569
|
-
url = visible[idx].get("url", "")
|
|
570
|
-
if url and url not in urls_to_screenshot:
|
|
571
|
-
urls_to_screenshot.append(url)
|
|
400
|
+
# Extract current user query
|
|
401
|
+
if all_param:
|
|
402
|
+
if isinstance(all_param, MessageChain):
|
|
403
|
+
current_query = str(all_param.get(Text)).strip()
|
|
572
404
|
else:
|
|
573
|
-
|
|
574
|
-
except: pass
|
|
575
|
-
await session.send(f"⚠️ 序号 {filter_value} 超出范围 (1-{len(visible)})")
|
|
576
|
-
return
|
|
405
|
+
current_query = str(all_param).strip()
|
|
577
406
|
else:
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
text_contents.append(f"## 来源: {result.get('title', url)}\n\n{content}")
|
|
623
|
-
|
|
624
|
-
if not page_references:
|
|
625
|
-
try: await tab_task
|
|
626
|
-
except: pass
|
|
627
|
-
await session.send("无法截图页面")
|
|
628
|
-
return
|
|
629
|
-
|
|
630
|
-
# Send result notification
|
|
631
|
-
await session.send(f"🔍 搜索 \"{search_query}\" 并截图 {successful_count} 个匹配结果")
|
|
632
|
-
|
|
633
|
-
# Pass TEXT content to LLM for summarization (not images)
|
|
634
|
-
combined_content = "\n\n---\n\n".join(text_contents) if text_contents else "无法提取网页内容"
|
|
635
|
-
user_query = f"总结关于 \"{search_query}\" 的内容。\n\n网页内容:\n{combined_content[:8000]}"
|
|
636
|
-
|
|
637
|
-
request = QueryRequest(
|
|
638
|
-
user_input=user_query,
|
|
639
|
-
images=[], # No images, use text content instead
|
|
640
|
-
conversation_history=[],
|
|
641
|
-
model_name=None,
|
|
642
|
-
)
|
|
643
|
-
|
|
644
|
-
with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as tf:
|
|
645
|
-
output_path = tf.name
|
|
646
|
-
|
|
647
|
-
response = await core.query(request, output_path=None)
|
|
648
|
-
|
|
649
|
-
try:
|
|
650
|
-
tab_id = await tab_task
|
|
651
|
-
except Exception:
|
|
652
|
-
tab_id = None
|
|
653
|
-
|
|
654
|
-
if response.success and response.content:
|
|
655
|
-
render_ok = await core.render(
|
|
656
|
-
markdown_content=response.content,
|
|
657
|
-
output_path=output_path,
|
|
658
|
-
stats={"total_time": response.total_time},
|
|
659
|
-
references=[],
|
|
660
|
-
page_references=page_references, # Pass screenshots for rendering
|
|
661
|
-
tab_id=tab_id
|
|
662
|
-
)
|
|
663
|
-
|
|
664
|
-
if render_ok and os.path.exists(output_path):
|
|
665
|
-
with open(output_path, "rb") as f:
|
|
666
|
-
img_data = base64.b64encode(f.read()).decode()
|
|
667
|
-
|
|
668
|
-
msg_chain = MessageChain(Image(src=f'data:image/png;base64,{img_data}'))
|
|
669
|
-
if conf.quote:
|
|
670
|
-
msg_chain = MessageChain(Quote(session.event.message.id)) + msg_chain
|
|
671
|
-
|
|
672
|
-
await session.send(msg_chain)
|
|
673
|
-
os.remove(output_path)
|
|
674
|
-
else:
|
|
675
|
-
await session.send(response.content[:500])
|
|
676
|
-
else:
|
|
677
|
-
await session.send(f"总结失败: {response.error or 'Unknown error'}")
|
|
678
|
-
|
|
679
|
-
return
|
|
680
|
-
|
|
681
|
-
# Normal query mode (no cache context)
|
|
682
|
-
await process_request(session, all_param)
|
|
407
|
+
current_query = ""
|
|
408
|
+
|
|
409
|
+
# If empty query, assume request for summary
|
|
410
|
+
if not current_query:
|
|
411
|
+
current_query = "请详细总结上述搜索结果"
|
|
412
|
+
|
|
413
|
+
# Build full context from cached results
|
|
414
|
+
context_parts = []
|
|
415
|
+
for i, res in enumerate(cached.results):
|
|
416
|
+
title = res.get("title", f"Result {i+1}")
|
|
417
|
+
url = res.get("url", "")
|
|
418
|
+
content = res.get("content", "") or res.get("snippet", "")
|
|
419
|
+
context_parts.append(f"## [{i+1}] {title}\nURL: {url}\n\n{content}")
|
|
420
|
+
|
|
421
|
+
full_context = "\n\n".join(context_parts)
|
|
422
|
+
|
|
423
|
+
# Construct augmented prompt
|
|
424
|
+
new_prompt = f"基于以下搜索结果回答问题:\n\n【搜索上下文】\nSearch Query: {cached.query}\n\n{full_context}\n\n【用户问题】\n{current_query}"
|
|
425
|
+
|
|
426
|
+
# Use MessageChain with Text for compatibility
|
|
427
|
+
# This injects the search context into the prompt while maintaining the 'reply' link in history
|
|
428
|
+
all_param = MessageChain(Text(new_prompt))
|
|
429
|
+
|
|
430
|
+
# Log for debug
|
|
431
|
+
logger.info(f"Injecting search context from message {reply_msg_id} into query")
|
|
432
|
+
|
|
433
|
+
# Normal query mode (Standard Agentic Chat)
|
|
434
|
+
# Register task for cancellation
|
|
435
|
+
msg_id = str(session.event.message.id) if hasattr(session.event, 'message') else str(session.event.id)
|
|
436
|
+
task = asyncio.create_task(process_request(session, all_param))
|
|
437
|
+
|
|
438
|
+
# Define cleanup to close potential tabs (handled inside process_request but good to have backup)
|
|
439
|
+
# process_request handles its own cleanup, but we need to track the task itself
|
|
440
|
+
_task_manager.register(msg_id, task)
|
|
441
|
+
|
|
442
|
+
try:
|
|
443
|
+
await task
|
|
444
|
+
except asyncio.CancelledError:
|
|
445
|
+
logger.info(f"Task {msg_id} cancelled by user")
|
|
446
|
+
await session.send("❌ 任务已停止")
|
|
447
|
+
except Exception as e:
|
|
448
|
+
logger.error(f"Task failed: {e}")
|
|
449
|
+
finally:
|
|
450
|
+
_task_manager.unregister(msg_id)
|
|
683
451
|
|
|
684
452
|
|
|
685
453
|
# Search/Web Command (/w)
|
|
@@ -975,13 +743,8 @@ async def handle_help_command(session: Session[MessageCreatedEvent], result: Arp
|
|
|
975
743
|
Question Agent:
|
|
976
744
|
• {conf.question_command} tell me...
|
|
977
745
|
• {conf.question_command} [picture] tell me...
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
• {conf.question_command} github: fastapi
|
|
981
|
-
• {conf.question_command} 1,2: minecraft
|
|
982
|
-
• {conf.question_command} mcmod=2: forge mod
|
|
983
|
-
Question Context:
|
|
984
|
-
• [quote: question] + {conf.question_command} tell me more...
|
|
746
|
+
Stop Task:
|
|
747
|
+
• {conf.stop_command} (reply to the question/web command)
|
|
985
748
|
Web_tool Search:
|
|
986
749
|
• {conf.web_command} query
|
|
987
750
|
Web_tool Screenshot:
|
|
@@ -993,13 +756,32 @@ Web_tool Filter(search and screenshot):
|
|
|
993
756
|
Web_tool Context(screenshot):
|
|
994
757
|
• [quote: web_tool search] + {conf.web_command} 1
|
|
995
758
|
• [quote: web_tool search] + {conf.web_command} 1, 3
|
|
996
|
-
Web_tool Context(question):
|
|
997
|
-
• [quote: web_tool screenshot] + {conf.question_command} tell me...
|
|
998
|
-
• [quote: web_tool search] + {conf.question_command} tell me...
|
|
999
759
|
"""
|
|
1000
|
-
|
|
760
|
+
|
|
1001
761
|
await session.send(help_text)
|
|
1002
762
|
|
|
763
|
+
# Stop command (/x)
|
|
764
|
+
alc_stop = Alconna(conf.stop_command)
|
|
765
|
+
|
|
766
|
+
@command.on(alc_stop)
|
|
767
|
+
async def handle_stop_command(session: Session[MessageCreatedEvent], result: Arparma):
|
|
768
|
+
"""Stop a running task by replying to the original command message."""
|
|
769
|
+
if not session.reply or not hasattr(session.reply.origin, 'id'):
|
|
770
|
+
await session.send("请回复正在执行的任务消息以停止它")
|
|
771
|
+
return
|
|
772
|
+
|
|
773
|
+
target_msg_id = str(session.reply.origin.id)
|
|
774
|
+
|
|
775
|
+
if await _task_manager.cancel(target_msg_id):
|
|
776
|
+
# Determine notification based on reaction config
|
|
777
|
+
if conf.reaction:
|
|
778
|
+
asyncio.create_task(react(session, "🛑"))
|
|
779
|
+
else:
|
|
780
|
+
await session.send("正在停止任务...")
|
|
781
|
+
else:
|
|
782
|
+
await session.send("未找到可停止的任务或任务已结束")
|
|
783
|
+
|
|
784
|
+
|
|
1003
785
|
@listen(CommandReceive)
|
|
1004
786
|
async def remove_at(content: MessageChain):
|
|
1005
787
|
return content.lstrip(At)
|
|
@@ -1,24 +1,23 @@
|
|
|
1
|
-
entari_plugin_hyw/
|
|
2
|
-
entari_plugin_hyw/__init__.py,sha256=0q-i4F_xm--klgtp__r7cx_xPK7rV164aRzISEmVO7o,39053
|
|
1
|
+
entari_plugin_hyw/__init__.py,sha256=Ro4nryYC5WXfPvC125ds39M00QZ1bsbADCLmZlZk1UU,29868
|
|
3
2
|
entari_plugin_hyw/filters.py,sha256=sQnLaiqqZ2NkykcH4QgzFImP-JW3uVU7l6iuAAyUsJg,3080
|
|
4
3
|
entari_plugin_hyw/history.py,sha256=0XJwbfvXH5T1EPt4G1J5wWMJsKi0FfmajY5cvw8CQWE,12065
|
|
5
4
|
entari_plugin_hyw/misc.py,sha256=5IqF5Z2C_6Ufy5TI89uX5hX5fVYcXOTZIQUIu_tvf54,6855
|
|
6
5
|
entari_plugin_hyw/search_cache.py,sha256=7MIhTm5_YnZjc0aBaX7AE4AJp0VT8eU6ObR6mTkoerc,4285
|
|
7
6
|
hyw_core/__init__.py,sha256=Jlr9Ic-BLOPTnff6OctUCdjDMdK4nssTF_vHie4QKTo,1958
|
|
8
|
-
hyw_core/agent.py,sha256=
|
|
7
|
+
hyw_core/agent.py,sha256=ArpcvlzZZH73s3T4qu8gb-Kik05wCAP07BOucD-uu-o,37165
|
|
9
8
|
hyw_core/config.py,sha256=DHxwToUVLm1nT88gG05e3hVzSLxXMk9BjgjAnhGCADk,4918
|
|
10
9
|
hyw_core/core.py,sha256=_jN4831OeHQ_aM7sIlzcwYb5_Lp82kp2XmqpJD_tsLA,16097
|
|
11
|
-
hyw_core/definitions.py,sha256=
|
|
10
|
+
hyw_core/definitions.py,sha256=fod0RlZwWzK8kSKYr5kIsSa8CM5DL5p3ADTexgnN-o4,6568
|
|
12
11
|
hyw_core/image_cache.py,sha256=t8pr1kgH2ngK9IhrBAhzUqhBWERNztUywMzgCFZEtQk,9899
|
|
13
12
|
hyw_core/pipeline.py,sha256=ZWwF0DHa29-65lUMU1_Fem3xQmxl7X_vgeni0ErOb8Q,22826
|
|
14
|
-
hyw_core/search.py,sha256=
|
|
13
|
+
hyw_core/search.py,sha256=cLx1rPpy8BH6WiOKudH_XcO-DhqEU-CHANRDlyEB3MY,7612
|
|
15
14
|
hyw_core/browser_control/__init__.py,sha256=IeMErRC6fbq1PJWNK3klSbarSrUwOM4yyd_kJ6uWCPM,1406
|
|
16
15
|
hyw_core/browser_control/landing.html,sha256=wgqldumdylz69T83pvOkrigT1Mdb9GY0_KU0ceLGwdY,4642
|
|
17
16
|
hyw_core/browser_control/manager.py,sha256=-dHb0FamRsLfuU3jqX5cKaDo8DOOFV32zY912GuMdXU,6048
|
|
18
17
|
hyw_core/browser_control/renderer.py,sha256=s-QNIU-NMVQGLd_drLmeERgHsTm6C9XYm78CObt2KXc,17409
|
|
19
|
-
hyw_core/browser_control/service.py,sha256=
|
|
18
|
+
hyw_core/browser_control/service.py,sha256=Wq_alwsQ_c_Tpn2TrjJuIosnOaMjnTvCKvfqxj0Gi0o,45448
|
|
20
19
|
hyw_core/browser_control/assets/index.html,sha256=BpbM0vD9OYicE5MBHSVLo3j_y-MpULI82PMqmBKpWT8,2328623
|
|
21
|
-
hyw_core/browser_control/assets/card-dist/index.html,sha256=
|
|
20
|
+
hyw_core/browser_control/assets/card-dist/index.html,sha256=4SA4QA2aaUwHGETtCcdUGEMjjoHaw7wJVACZr4VC70Q,2213449
|
|
22
21
|
hyw_core/browser_control/assets/card-dist/vite.svg,sha256=SnSK_UQ5GLsWWRyDTEAdrjPoeGGrXbrQgRw6O0qSFPs,1497
|
|
23
22
|
hyw_core/browser_control/assets/card-dist/logos/anthropic.svg,sha256=ASsy1ypo3osNc3n-B0R81tk_dIFsVgg7qQORrd5T2kA,558
|
|
24
23
|
hyw_core/browser_control/assets/card-dist/logos/cerebras.svg,sha256=bpmiiYTODwc06knTmPj3GQ7NNtosMog5lkggvB_Z-7M,44166
|
|
@@ -65,8 +64,8 @@ hyw_core/crawling/completeness.py,sha256=OKdS8XlYYWDU1Vl1k-u7yEFqppukuJv-YQB0Px5
|
|
|
65
64
|
hyw_core/crawling/models.py,sha256=pCKe0k9xT3taSAlTlh0PazcLV0xYsm8p3XIkLHGf-LM,2353
|
|
66
65
|
hyw_core/stages/__init__.py,sha256=W89cWpq-HBLi2FprtJQSjQNLzpbhM8ZCkqPG61D_imE,521
|
|
67
66
|
hyw_core/stages/base.py,sha256=EfnTkISXbBNxjARykqIhmMrVqw2tqZl7ozJbJEbRnhI,2806
|
|
68
|
-
hyw_core/stages/summary.py,sha256=
|
|
69
|
-
entari_plugin_hyw-4.0.
|
|
70
|
-
entari_plugin_hyw-4.0.
|
|
71
|
-
entari_plugin_hyw-4.0.
|
|
72
|
-
entari_plugin_hyw-4.0.
|
|
67
|
+
hyw_core/stages/summary.py,sha256=D0XwhqmtoyovSXUWoa-_RxeoKWEkdjqMyoJV-gWQwvQ,8342
|
|
68
|
+
entari_plugin_hyw-4.0.0rc15.dist-info/METADATA,sha256=398WlR_HKU96zAm_aYAvC2XIas1HjdpPXixbKB4dCHs,3845
|
|
69
|
+
entari_plugin_hyw-4.0.0rc15.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
70
|
+
entari_plugin_hyw-4.0.0rc15.dist-info/top_level.txt,sha256=ah76OrufRX0okOl4Fv8MO6PXiT0IaZ1oG0eDrdAPoNo,27
|
|
71
|
+
entari_plugin_hyw-4.0.0rc15.dist-info/RECORD,,
|