entari-plugin-hyw 4.0.0rc15__py3-none-any.whl → 4.0.0rc17__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 +142 -15
- entari_plugin_hyw/search_cache.py +110 -11
- {entari_plugin_hyw-4.0.0rc15.dist-info → entari_plugin_hyw-4.0.0rc17.dist-info}/METADATA +2 -1
- {entari_plugin_hyw-4.0.0rc15.dist-info → entari_plugin_hyw-4.0.0rc17.dist-info}/RECORD +11 -30
- hyw_core/agent.py +18 -7
- hyw_core/browser_control/assets/card-dist/index.html +23 -23
- hyw_core/browser_control/service.py +25 -6
- hyw_core/core.py +13 -21
- hyw_core/definitions.py +21 -4
- hyw_core/browser_control/assets/index.html +0 -5691
- hyw_core/browser_control/assets/logos/anthropic.svg +0 -1
- hyw_core/browser_control/assets/logos/cerebras.svg +0 -9
- hyw_core/browser_control/assets/logos/deepseek.png +0 -0
- hyw_core/browser_control/assets/logos/gemini.svg +0 -1
- hyw_core/browser_control/assets/logos/google.svg +0 -1
- hyw_core/browser_control/assets/logos/grok.png +0 -0
- hyw_core/browser_control/assets/logos/huggingface.png +0 -0
- hyw_core/browser_control/assets/logos/microsoft.svg +0 -15
- hyw_core/browser_control/assets/logos/minimax.png +0 -0
- hyw_core/browser_control/assets/logos/mistral.png +0 -0
- hyw_core/browser_control/assets/logos/nvida.png +0 -0
- hyw_core/browser_control/assets/logos/openai.svg +0 -1
- hyw_core/browser_control/assets/logos/openrouter.png +0 -0
- hyw_core/browser_control/assets/logos/perplexity.svg +0 -24
- hyw_core/browser_control/assets/logos/qwen.png +0 -0
- hyw_core/browser_control/assets/logos/xai.png +0 -0
- hyw_core/browser_control/assets/logos/xiaomi.png +0 -0
- hyw_core/browser_control/assets/logos/zai.png +0 -0
- {entari_plugin_hyw-4.0.0rc15.dist-info → entari_plugin_hyw-4.0.0rc17.dist-info}/WHEEL +0 -0
- {entari_plugin_hyw-4.0.0rc15.dist-info → entari_plugin_hyw-4.0.0rc17.dist-info}/top_level.txt +0 -0
|
@@ -12,6 +12,8 @@ from concurrent.futures import ThreadPoolExecutor
|
|
|
12
12
|
from typing import Optional, Dict, Any, List
|
|
13
13
|
from loguru import logger
|
|
14
14
|
import trafilatura
|
|
15
|
+
from PIL import Image
|
|
16
|
+
from io import BytesIO
|
|
15
17
|
|
|
16
18
|
# Import intelligent completeness checker
|
|
17
19
|
from ..crawling.completeness import CompletenessChecker, trigger_lazy_load
|
|
@@ -541,13 +543,13 @@ class ScreenshotService:
|
|
|
541
543
|
tasks = [self.screenshot_url(url, timeout=timeout, full_page=full_page) for url in urls]
|
|
542
544
|
return await asyncio.gather(*tasks, return_exceptions=True)
|
|
543
545
|
|
|
544
|
-
async def screenshot_url(self, url: str, wait_load: bool = True, timeout: float = 15.0, full_page: bool = False, quality: int =
|
|
546
|
+
async def screenshot_url(self, url: str, wait_load: bool = True, timeout: float = 15.0, full_page: bool = False, quality: int = 90, scale: int = 1) -> Optional[str]:
|
|
545
547
|
"""Screenshot URL (Async wrapper for sync). Returns base64 string only."""
|
|
546
548
|
loop = asyncio.get_running_loop()
|
|
547
549
|
result = await loop.run_in_executor(
|
|
548
550
|
self._executor,
|
|
549
551
|
self._screenshot_sync,
|
|
550
|
-
url, wait_load, timeout, full_page, quality, False # extract_content=False
|
|
552
|
+
url, wait_load, timeout, full_page, quality, scale, False # extract_content=False
|
|
551
553
|
)
|
|
552
554
|
# Backward compatible: return just the screenshot for old callers
|
|
553
555
|
if isinstance(result, dict):
|
|
@@ -569,7 +571,7 @@ class ScreenshotService:
|
|
|
569
571
|
result = await loop.run_in_executor(
|
|
570
572
|
self._executor,
|
|
571
573
|
self._screenshot_sync,
|
|
572
|
-
url, True, timeout, False,
|
|
574
|
+
url, True, timeout, False, 80, 2, True # quality=80 for balance, scale=2, extract_content=True
|
|
573
575
|
)
|
|
574
576
|
|
|
575
577
|
if not isinstance(result, dict):
|
|
@@ -584,12 +586,12 @@ class ScreenshotService:
|
|
|
584
586
|
return result
|
|
585
587
|
|
|
586
588
|
|
|
587
|
-
def _screenshot_sync(self, url: str, wait_load: bool, timeout: float, full_page: bool, quality: int, extract_content: bool = False) -> Any:
|
|
589
|
+
def _screenshot_sync(self, url: str, wait_load: bool, timeout: float, full_page: bool, quality: int, scale: int = 1, extract_content: bool = False) -> Any:
|
|
588
590
|
"""Synchronous screenshot. If extract_content=True, returns Dict else str."""
|
|
589
591
|
if not url:
|
|
590
592
|
return {"screenshot_b64": None, "content": "", "title": "", "url": url} if extract_content else None
|
|
591
593
|
tab = None
|
|
592
|
-
capture_width =
|
|
594
|
+
capture_width = 3000 # Increased for more comfortable page size while maintaining high resolution
|
|
593
595
|
|
|
594
596
|
try:
|
|
595
597
|
self._ensure_ready()
|
|
@@ -603,7 +605,7 @@ class ScreenshotService:
|
|
|
603
605
|
# This eliminates the need for post-load resize and reflow
|
|
604
606
|
try:
|
|
605
607
|
tab.run_cdp('Emulation.setDeviceMetricsOverride',
|
|
606
|
-
width=capture_width, height=900, deviceScaleFactor=
|
|
608
|
+
width=capture_width, height=900, deviceScaleFactor=scale, mobile=False)
|
|
607
609
|
except:
|
|
608
610
|
pass
|
|
609
611
|
|
|
@@ -876,6 +878,23 @@ class ScreenshotService:
|
|
|
876
878
|
# Capture screenshot
|
|
877
879
|
screenshot_b64 = tab.get_screenshot(as_base64='jpg', full_page=False)
|
|
878
880
|
|
|
881
|
+
# Use Pillow for intelligent compression
|
|
882
|
+
if screenshot_b64 and quality < 95: # Only compress if quality is not near maximum
|
|
883
|
+
try:
|
|
884
|
+
img_bytes = base64.b64decode(screenshot_b64)
|
|
885
|
+
img = Image.open(BytesIO(img_bytes))
|
|
886
|
+
|
|
887
|
+
# Convert to RGB if not already (some images might be RGBA, which JPEG doesn't support)
|
|
888
|
+
if img.mode in ("RGBA", "P"):
|
|
889
|
+
img = img.convert("RGB")
|
|
890
|
+
|
|
891
|
+
output_buffer = BytesIO()
|
|
892
|
+
img.save(output_buffer, format="WebP", quality=quality, optimize=True) # Output as WebP format
|
|
893
|
+
screenshot_b64 = base64.b64encode(output_buffer.getvalue()).decode()
|
|
894
|
+
logger.debug(f"ScreenshotService: Applied Pillow compression with quality={quality}")
|
|
895
|
+
except Exception as e:
|
|
896
|
+
logger.warning(f"ScreenshotService: Pillow compression failed: {e}")
|
|
897
|
+
|
|
879
898
|
# Extract content if requested
|
|
880
899
|
if extract_content:
|
|
881
900
|
try:
|
hyw_core/core.py
CHANGED
|
@@ -301,28 +301,20 @@ class HywCore:
|
|
|
301
301
|
|
|
302
302
|
# Build visible results list (excluding hidden items)
|
|
303
303
|
visible_results = [r for r in web_results if not r.get("_hidden")]
|
|
304
|
-
|
|
305
|
-
#
|
|
306
|
-
|
|
307
|
-
cited_ids = set()
|
|
308
|
-
for match in citation_pattern.finditer(content):
|
|
309
|
-
cited_ids.add(int(match.group(1)))
|
|
310
|
-
|
|
311
|
-
# Only include cited references, in order of first appearance
|
|
304
|
+
|
|
305
|
+
# Pass ALL visible results to frontend so [N] citations map correctly to N-th item
|
|
306
|
+
# App.vue handles reordering used vs unused citations
|
|
312
307
|
references = []
|
|
313
|
-
for
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
r
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
"raw_screenshot_b64": r.get("screenshot_b64"),
|
|
324
|
-
})
|
|
325
|
-
|
|
308
|
+
for r in visible_results:
|
|
309
|
+
references.append({
|
|
310
|
+
"title": r.get("title", ""),
|
|
311
|
+
"url": r.get("url", ""),
|
|
312
|
+
"snippet": r.get("content", "")[:300] if r.get("content") else "",
|
|
313
|
+
"images": r.get("images", []),
|
|
314
|
+
"is_fetched": r.get("_type") == "page",
|
|
315
|
+
"raw_screenshot_b64": r.get("screenshot_b64"),
|
|
316
|
+
})
|
|
317
|
+
|
|
326
318
|
# Build response
|
|
327
319
|
response = QueryResponse(
|
|
328
320
|
success=True,
|
hyw_core/definitions.py
CHANGED
|
@@ -120,6 +120,7 @@ AGENT_SYSTEM_PROMPT = """# 你是一个智能助手 (Agent), 你的职责是使
|
|
|
120
120
|
## 任务
|
|
121
121
|
理解用户意图分配给你的任务.
|
|
122
122
|
如果用户没有明确分配任务, 则默认任务为解释用户问题中的关键词.
|
|
123
|
+
分辨用户消息的语意, 提取出用户最想了解的核心内容, 作为任务的核心.
|
|
123
124
|
|
|
124
125
|
## 核心原则
|
|
125
126
|
最小限度使用自身知识, 尽可能使用 web_tool 获取信息.
|
|
@@ -128,16 +129,32 @@ AGENT_SYSTEM_PROMPT = """# 你是一个智能助手 (Agent), 你的职责是使
|
|
|
128
129
|
- 并行调用工具
|
|
129
130
|
- 网页搜索: 可以同时调用3次, 其中URL截图消耗较大, 最多同时调用1个
|
|
130
131
|
- 积极使用 web_tool 获取信息
|
|
131
|
-
- 搜索时,
|
|
132
|
-
|
|
132
|
+
- 搜索时, 关键词保证单一、简单、指向准确、利于传统搜索引擎, 通常只搜索1个词或短语.
|
|
133
|
+
- 建议搜索: "minecraft create"; 不搜索 "create 是什么 百科"
|
|
134
|
+
- 建议搜索: "opnecode"; 不搜索 "open code 怎么配置"
|
|
135
|
+
- 建议搜索: "Bypass permissions"; 不搜索 "Bypass permissions 软件 选项"
|
|
136
|
+
- 本搜索不支持高级搜索、不支持引号、不支持减号等复杂语法
|
|
137
|
+
- 不要尝试通过搜索引擎描述如何尼尔反推出角色、任务、地点, 搜索引擎没有这个能力
|
|
133
138
|
- 禁止搜索可能导致一切潜在推销广告的内容, 不出现“是什么”、“怎么办”等容易产生广告的内容
|
|
134
139
|
- 禁止搜索任何敏感内容(galgame之类的除外), 禁止搜索政治、成人色情、暴力等内容
|
|
135
|
-
- 获取页面截图时,
|
|
136
|
-
- 最多可调用
|
|
140
|
+
- 获取页面截图时, 只挑选官方性较强的 wiki、官方网站、资源站等等, 不使用第三方转载新闻网站.
|
|
141
|
+
- 最多可调用3轮工具, 但请适度保持快速, 3轮之后必须给出最终回答.
|
|
137
142
|
- 适当时候调用 `refuse_answer`
|
|
138
143
|
- 对于具体任务, 如果是转述、格式化、翻译等, 请直接给出最终回答, 不再调用工具
|
|
139
144
|
- 遇到计算、js代码、算法任务, 积极使用 js_executor 工具完成计算任务.
|
|
140
145
|
|
|
146
|
+
## 抓重点原则
|
|
147
|
+
搜索结果中往往混杂大量信息,你需要:
|
|
148
|
+
- 主动识别与用户问题最匹配的结果,大胆引用,不要因为信息混在众多结果中就忽略它
|
|
149
|
+
- 即使只有一条结果明确匹配,也要优先使用该结果,而非泛泛而谈
|
|
150
|
+
|
|
151
|
+
## 图文融合原则
|
|
152
|
+
当用户同时提供图片和文字时:
|
|
153
|
+
- 先理解用户真正想知道什么(识图?查资料?对比分析?)
|
|
154
|
+
- 图片是"锚点",搜索是"扩展"——围绕图片内容组织搜索信息
|
|
155
|
+
- 行文自然流畅,让图片分析和搜索结果无缝衔接
|
|
156
|
+
- 例如:"图中展示的是 XX(识别结果),这是一款...(搜索扩展)"
|
|
157
|
+
|
|
141
158
|
## 回答格式
|
|
142
159
|
- 字数: 尽可能少, 有多少获取到的信息、需要解释的内容, 就写多少, 减少无意义输出, 足够完成用户分配给你的任务 / 解释关键词即可.
|
|
143
160
|
- `# ` 大标题约 8-10 个字
|