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.

@@ -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
- # Extract query text
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
- # Parse indices if provided
345
- indices = parse_multi_indices(query_text, max_count=3) if query_text else None
346
-
347
- # Check if too many indices requested (parse_multi_indices returns None if > max_count)
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
- try: await tab_task
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
- found_count = 0
579
- for res in visible:
580
- url = res.get("url", "")
581
- title = res.get("title", "")
582
- # Match filter against both URL and title
583
- if (filter_value in url.lower() or filter_value in title.lower()) and url not in urls_to_screenshot:
584
- urls_to_screenshot.append(url)
585
- found_count += 1
586
- if found_count >= count:
587
- break
588
-
589
- if found_count == 0:
590
- try: await tab_task
591
- except: pass
592
- await session.send(f"⚠️ 未找到包含 \"{filter_value}\" 的链接")
593
- return
594
-
595
- if not urls_to_screenshot:
596
- try: await tab_task
597
- except: pass
598
- await session.send("⚠️ 未找到匹配的链接")
599
- return
600
-
601
- # Take screenshots with content extraction
602
- screenshot_tasks = [core.screenshot_with_content(url) for url in urls_to_screenshot]
603
- screenshot_results = await asyncio.gather(*screenshot_tasks)
604
-
605
- # Build page references for rendering (with screenshots)
606
- # and collect text content for LLM
607
- page_references = []
608
- text_contents = []
609
- successful_count = 0
610
-
611
- for url, result in zip(urls_to_screenshot, screenshot_results):
612
- if isinstance(result, dict) and result.get("screenshot_b64"):
613
- successful_count += 1
614
- page_references.append({
615
- "url": url,
616
- "title": result.get("title", "截图页面"),
617
- "raw_screenshot_b64": result.get("screenshot_b64"),
618
- })
619
- # Collect text for LLM
620
- content = result.get("content", "")
621
- if content:
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
- [quote] {conf.question_command} tell me...
979
- Question Filter:
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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: entari_plugin_hyw
3
- Version: 4.0.0rc14
3
+ Version: 4.0.0rc15
4
4
  Summary: Use large language models to interpret chat messages
5
5
  Author-email: kumoSleeping <zjr2992@outlook.com>
6
6
  License: MIT
@@ -1,24 +1,23 @@
1
- entari_plugin_hyw/Untitled-1,sha256=wbsr5i9iorqBMIYK4aAnpNTek3mXbhvyo2YOYw38pE4,30187
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=lyGwJI0SiRGYdpPPL7mtzHj569UNIgm-s4P_xz1MNGc,33096
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=pH46L-N25pSuPaIiN7l7yfoD6oHK6BLHigE0eYLFmJQ,4270
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=VvfNSb9Hf7ZQWlNtnZfYe2eO9qPjYtwJxVlud6OdeCQ,7787
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=_TcRRQuSei8PCsZs8hYU_DJ4DRMQ6jAFNqWmwca35Tc,43984
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=Xw-hQ5ctdQkK-1jV8_gqMdgVGNZDwWZvIAqNrh2eK7g,2210054
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=sgHCm_Leq_pkJ4YcgQuf8croiOP1oKz171TnzJwRwVs,7080
69
- entari_plugin_hyw-4.0.0rc14.dist-info/METADATA,sha256=aOkMfm8FTTHNF_2ZkwNRw47sjLPlLNxu_MU8uoAFGX0,3845
70
- entari_plugin_hyw-4.0.0rc14.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
71
- entari_plugin_hyw-4.0.0rc14.dist-info/top_level.txt,sha256=ah76OrufRX0okOl4Fv8MO6PXiT0IaZ1oG0eDrdAPoNo,27
72
- entari_plugin_hyw-4.0.0rc14.dist-info/RECORD,,
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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.10.1)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5