entari-plugin-hyw 4.0.0rc5__py3-none-any.whl → 4.0.0rc7__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 +71 -9
- entari_plugin_hyw/assets/card-dist/index.html +26 -26
- entari_plugin_hyw/browser/engines/default.py +166 -0
- entari_plugin_hyw/browser/engines/{searxng.py → duckduckgo.py} +4 -4
- entari_plugin_hyw/browser/engines/google.py +155 -0
- entari_plugin_hyw/browser/manager.py +1 -1
- entari_plugin_hyw/browser/service.py +323 -53
- entari_plugin_hyw/card-ui/src/App.vue +32 -1
- entari_plugin_hyw/definitions.py +55 -11
- entari_plugin_hyw/history.py +34 -44
- entari_plugin_hyw/misc.py +34 -0
- entari_plugin_hyw/modular_pipeline.py +177 -50
- entari_plugin_hyw/search.py +67 -25
- entari_plugin_hyw/stage_base.py +7 -0
- entari_plugin_hyw/stage_instruct.py +34 -7
- entari_plugin_hyw/stage_instruct_deepsearch.py +104 -0
- entari_plugin_hyw/stage_summary.py +6 -0
- entari_plugin_hyw/stage_vision.py +113 -0
- {entari_plugin_hyw-4.0.0rc5.dist-info → entari_plugin_hyw-4.0.0rc7.dist-info}/METADATA +1 -1
- {entari_plugin_hyw-4.0.0rc5.dist-info → entari_plugin_hyw-4.0.0rc7.dist-info}/RECORD +22 -19
- entari_plugin_hyw/stage_instruct_review.py +0 -92
- {entari_plugin_hyw-4.0.0rc5.dist-info → entari_plugin_hyw-4.0.0rc7.dist-info}/WHEEL +0 -0
- {entari_plugin_hyw-4.0.0rc5.dist-info → entari_plugin_hyw-4.0.0rc7.dist-info}/top_level.txt +0 -0
entari_plugin_hyw/__init__.py
CHANGED
|
@@ -22,7 +22,7 @@ from arclet.entari.event.command import CommandReceive
|
|
|
22
22
|
from .modular_pipeline import ModularPipeline
|
|
23
23
|
from .history import HistoryManager
|
|
24
24
|
from .render_vue import ContentRenderer, get_content_renderer
|
|
25
|
-
from .misc import process_onebot_json, process_images, resolve_model_name, render_refuse_answer, REFUSE_ANSWER_MARKDOWN
|
|
25
|
+
from .misc import process_onebot_json, process_images, resolve_model_name, render_refuse_answer, render_image_unsupported, REFUSE_ANSWER_MARKDOWN
|
|
26
26
|
from arclet.entari.event.lifespan import Cleanup
|
|
27
27
|
|
|
28
28
|
import os
|
|
@@ -93,6 +93,7 @@ class ModelConfig:
|
|
|
93
93
|
model_provider: Optional[str] = None
|
|
94
94
|
input_price: Optional[float] = None
|
|
95
95
|
output_price: Optional[float] = None
|
|
96
|
+
image_input: bool = True
|
|
96
97
|
|
|
97
98
|
|
|
98
99
|
@dataclass
|
|
@@ -117,11 +118,10 @@ class HywConfig(BasicConfModel):
|
|
|
117
118
|
instruct: Optional[ModelConfig] = None
|
|
118
119
|
qa: Optional[ModelConfig] = None
|
|
119
120
|
main: Optional[ModelConfig] = None # Summary stage
|
|
121
|
+
vision: Optional[ModelConfig] = None # Vision description stage
|
|
120
122
|
|
|
121
123
|
# Search/Fetch Settings
|
|
122
|
-
search_engine: str = "
|
|
123
|
-
enable_domain_blocking: bool = True
|
|
124
|
-
page_content_mode: str = "text"
|
|
124
|
+
search_engine: str = "google"
|
|
125
125
|
|
|
126
126
|
# Rendering Settings
|
|
127
127
|
headless: bool = False
|
|
@@ -131,10 +131,10 @@ class HywConfig(BasicConfModel):
|
|
|
131
131
|
# Bot Behavior
|
|
132
132
|
save_conversation: bool = False
|
|
133
133
|
reaction: bool = False
|
|
134
|
-
quote: bool =
|
|
134
|
+
quote: bool = False
|
|
135
135
|
|
|
136
136
|
# UI Theme
|
|
137
|
-
theme_color: str = "#
|
|
137
|
+
theme_color: str = "#ff0000"
|
|
138
138
|
|
|
139
139
|
def __post_init__(self):
|
|
140
140
|
"""Parse and normalize theme color after initialization."""
|
|
@@ -146,6 +146,8 @@ class HywConfig(BasicConfModel):
|
|
|
146
146
|
self.qa = ModelConfig(**self.qa)
|
|
147
147
|
if isinstance(self.main, dict):
|
|
148
148
|
self.main = ModelConfig(**self.main)
|
|
149
|
+
if isinstance(self.vision, dict):
|
|
150
|
+
self.vision = ModelConfig(**self.vision)
|
|
149
151
|
|
|
150
152
|
def get_model_config(self, stage: str) -> Dict[str, Any]:
|
|
151
153
|
"""
|
|
@@ -170,6 +172,9 @@ class HywConfig(BasicConfModel):
|
|
|
170
172
|
secondary = self.main
|
|
171
173
|
elif stage == "main":
|
|
172
174
|
primary = self.main
|
|
175
|
+
elif stage == "vision":
|
|
176
|
+
primary = self.vision
|
|
177
|
+
secondary = self.main # Fallback to main if vision not fully configured
|
|
173
178
|
|
|
174
179
|
# Build result with fallback logic
|
|
175
180
|
def resolve(field_name: str, is_essential: bool = True):
|
|
@@ -315,14 +320,66 @@ async def process_request(
|
|
|
315
320
|
|
|
316
321
|
images, err = await process_images(mc, vision_model)
|
|
317
322
|
|
|
318
|
-
#
|
|
323
|
+
# Check image input support
|
|
324
|
+
model_cfg_dict = next((m for m in conf.models if m.get("name") == model), None)
|
|
325
|
+
image_input_supported = True
|
|
326
|
+
if model_cfg_dict:
|
|
327
|
+
image_input_supported = model_cfg_dict.get("image_input", True)
|
|
328
|
+
|
|
329
|
+
# Log inferenced content mode
|
|
330
|
+
inferred_content_mode = "image" if image_input_supported else "text"
|
|
331
|
+
logger.info(f"Process Request: Model '{model}' Image Input: {image_input_supported} -> Mode: {inferred_content_mode}")
|
|
332
|
+
|
|
333
|
+
if images and not image_input_supported:
|
|
334
|
+
logger.warning(f"Model '{model}' does not support images, but user sent {len(images)} images.")
|
|
335
|
+
|
|
336
|
+
# Start renderer for the unsupported card
|
|
337
|
+
renderer = await get_content_renderer()
|
|
338
|
+
render_tab_task = asyncio.create_task(renderer.prepare_tab())
|
|
339
|
+
|
|
340
|
+
# Wait for tab and render unsupported
|
|
341
|
+
try:
|
|
342
|
+
tab_id = await render_tab_task
|
|
343
|
+
except Exception as e:
|
|
344
|
+
tab_id = None
|
|
345
|
+
|
|
346
|
+
import tempfile
|
|
347
|
+
with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as tf:
|
|
348
|
+
output_path = tf.name
|
|
349
|
+
|
|
350
|
+
render_ok = await render_image_unsupported(
|
|
351
|
+
renderer=renderer,
|
|
352
|
+
output_path=output_path,
|
|
353
|
+
theme_color=conf.theme_color,
|
|
354
|
+
tab_id=tab_id
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
if render_ok:
|
|
358
|
+
with open(output_path, "rb") as f:
|
|
359
|
+
img_data = base64.b64encode(f.read()).decode()
|
|
360
|
+
await session.send(MessageChain(Image(src=f'data:image/png;base64,{img_data}')))
|
|
361
|
+
if os.path.exists(output_path):
|
|
362
|
+
os.remove(output_path)
|
|
363
|
+
return
|
|
364
|
+
|
|
319
365
|
renderer = await get_content_renderer()
|
|
320
366
|
render_tab_task = asyncio.create_task(renderer.prepare_tab())
|
|
321
367
|
tab_id = None
|
|
322
368
|
|
|
323
369
|
# Call Pipeline directly
|
|
324
370
|
safe_input = msg_text
|
|
325
|
-
|
|
371
|
+
|
|
372
|
+
async def send_noti(msg: str):
|
|
373
|
+
try:
|
|
374
|
+
# Send simple text notification
|
|
375
|
+
if conf.quote:
|
|
376
|
+
await session.send([Quote(session.event.message.id), msg])
|
|
377
|
+
else:
|
|
378
|
+
await session.send(msg)
|
|
379
|
+
except Exception as e:
|
|
380
|
+
logger.warning(f"Failed to send notification: {e}")
|
|
381
|
+
|
|
382
|
+
pipeline = ModularPipeline(conf, send_func=send_noti)
|
|
326
383
|
try:
|
|
327
384
|
resp = await pipeline.execute(
|
|
328
385
|
safe_input,
|
|
@@ -385,6 +442,7 @@ async def process_request(
|
|
|
385
442
|
tab_id=tab_id,
|
|
386
443
|
)
|
|
387
444
|
else:
|
|
445
|
+
logger.info(f"Rendering card with {len(structured.get('references', []))} references...")
|
|
388
446
|
render_ok = await renderer.render(
|
|
389
447
|
markdown_content=content,
|
|
390
448
|
output_path=output_path,
|
|
@@ -396,6 +454,7 @@ async def process_request(
|
|
|
396
454
|
stages_used=final_resp.get("stages_used", []),
|
|
397
455
|
theme_color=conf.theme_color,
|
|
398
456
|
)
|
|
457
|
+
logger.info(f"Render completed: {render_ok}")
|
|
399
458
|
|
|
400
459
|
# Send & Save
|
|
401
460
|
if not render_ok:
|
|
@@ -442,10 +501,13 @@ async def process_request(
|
|
|
442
501
|
if conf.save_conversation and sent_id:
|
|
443
502
|
try:
|
|
444
503
|
# Pass web_results to save fetched pages as markdown, and output image
|
|
504
|
+
# Also pass vision_trace and instruct_traces for dedicated logs
|
|
445
505
|
history_manager.save_to_disk(
|
|
446
506
|
sent_id,
|
|
447
507
|
web_results=final_resp.get("web_results"),
|
|
448
|
-
image_path=output_path if 'output_path' in locals() else None
|
|
508
|
+
image_path=output_path if 'output_path' in locals() else None,
|
|
509
|
+
vision_trace=final_resp.get("vision_trace"),
|
|
510
|
+
instruct_traces=final_resp.get("instruct_traces"),
|
|
449
511
|
)
|
|
450
512
|
except Exception as e:
|
|
451
513
|
logger.warning(f"Failed to save conversation: {e}")
|