entari-plugin-hyw 4.0.0rc4__tar.gz → 4.0.0rc6__tar.gz
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-4.0.0rc4/src/entari_plugin_hyw.egg-info → entari_plugin_hyw-4.0.0rc6}/PKG-INFO +4 -4
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/pyproject.toml +4 -4
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/__init__.py +216 -75
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/card-dist/index.html +70 -79
- entari_plugin_hyw-4.0.0rc6/src/entari_plugin_hyw/browser/__init__.py +10 -0
- entari_plugin_hyw-4.0.0rc6/src/entari_plugin_hyw/browser/engines/base.py +13 -0
- entari_plugin_hyw-4.0.0rc6/src/entari_plugin_hyw/browser/engines/bing.py +95 -0
- entari_plugin_hyw-4.0.0rc6/src/entari_plugin_hyw/browser/engines/duckduckgo.py +137 -0
- entari_plugin_hyw-4.0.0rc6/src/entari_plugin_hyw/browser/engines/google.py +155 -0
- entari_plugin_hyw-4.0.0rc6/src/entari_plugin_hyw/browser/landing.html +172 -0
- entari_plugin_hyw-4.0.0rc6/src/entari_plugin_hyw/browser/manager.py +153 -0
- entari_plugin_hyw-4.0.0rc6/src/entari_plugin_hyw/browser/service.py +304 -0
- entari_plugin_hyw-4.0.0rc6/src/entari_plugin_hyw/card-ui/src/App.vue +756 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/src/components/MarkdownContent.vue +7 -11
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/src/components/StageCard.vue +33 -30
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/src/types.ts +9 -0
- entari_plugin_hyw-4.0.0rc6/src/entari_plugin_hyw/definitions.py +155 -0
- entari_plugin_hyw-4.0.0rc6/src/entari_plugin_hyw/history.py +248 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/misc.py +34 -0
- entari_plugin_hyw-4.0.0rc6/src/entari_plugin_hyw/modular_pipeline.py +384 -0
- entari_plugin_hyw-4.0.0rc6/src/entari_plugin_hyw/render_vue.py +401 -0
- entari_plugin_hyw-4.0.0rc6/src/entari_plugin_hyw/search.py +122 -0
- entari_plugin_hyw-4.0.0rc6/src/entari_plugin_hyw/stage_base.py +92 -0
- entari_plugin_hyw-4.0.0rc6/src/entari_plugin_hyw/stage_instruct.py +345 -0
- entari_plugin_hyw-4.0.0rc6/src/entari_plugin_hyw/stage_instruct_deepsearch.py +104 -0
- entari_plugin_hyw-4.0.0rc6/src/entari_plugin_hyw/stage_summary.py +164 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6/src/entari_plugin_hyw.egg-info}/PKG-INFO +4 -4
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw.egg-info/SOURCES.txt +14 -2
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw.egg-info/requires.txt +3 -3
- entari_plugin_hyw-4.0.0rc4/src/entari_plugin_hyw/card-ui/src/App.vue +0 -412
- entari_plugin_hyw-4.0.0rc4/src/entari_plugin_hyw/history.py +0 -170
- entari_plugin_hyw-4.0.0rc4/src/entari_plugin_hyw/pipeline.py +0 -1219
- entari_plugin_hyw-4.0.0rc4/src/entari_plugin_hyw/prompts.py +0 -47
- entari_plugin_hyw-4.0.0rc4/src/entari_plugin_hyw/render_vue.py +0 -314
- entari_plugin_hyw-4.0.0rc4/src/entari_plugin_hyw/search.py +0 -735
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/MANIFEST.in +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/README.md +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/setup.cfg +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/card-dist/logos/anthropic.svg +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/card-dist/logos/cerebras.svg +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/card-dist/logos/deepseek.png +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/card-dist/logos/gemini.svg +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/card-dist/logos/google.svg +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/card-dist/logos/grok.png +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/card-dist/logos/huggingface.png +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/card-dist/logos/microsoft.svg +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/card-dist/logos/minimax.png +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/card-dist/logos/mistral.png +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/card-dist/logos/nvida.png +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/card-dist/logos/openai.svg +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/card-dist/logos/openrouter.png +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/card-dist/logos/perplexity.svg +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/card-dist/logos/qwen.png +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/card-dist/logos/xai.png +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/card-dist/logos/xiaomi.png +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/card-dist/logos/zai.png +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/card-dist/vite.svg +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/icon/anthropic.svg +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/icon/cerebras.svg +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/icon/deepseek.png +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/icon/gemini.svg +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/icon/google.svg +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/icon/grok.png +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/icon/huggingface.png +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/icon/microsoft.svg +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/icon/minimax.png +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/icon/mistral.png +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/icon/nvida.png +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/icon/openai.svg +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/icon/openrouter.png +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/icon/perplexity.svg +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/icon/qwen.png +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/icon/xai.png +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/icon/xiaomi.png +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/assets/icon/zai.png +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/.gitignore +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/README.md +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/index.html +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/package-lock.json +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/package.json +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/public/logos/anthropic.svg +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/public/logos/cerebras.svg +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/public/logos/deepseek.png +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/public/logos/gemini.svg +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/public/logos/google.svg +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/public/logos/grok.png +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/public/logos/huggingface.png +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/public/logos/microsoft.svg +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/public/logos/minimax.png +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/public/logos/mistral.png +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/public/logos/nvida.png +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/public/logos/openai.svg +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/public/logos/openrouter.png +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/public/logos/perplexity.svg +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/public/logos/qwen.png +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/public/logos/xai.png +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/public/logos/xiaomi.png +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/public/logos/zai.png +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/public/vite.svg +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/src/assets/vue.svg +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/src/components/HelloWorld.vue +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/src/components/SectionCard.vue +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/src/main.ts +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/src/style.css +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/src/test_regex.js +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/tsconfig.app.json +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/tsconfig.json +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/tsconfig.node.json +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/card-ui/vite.config.ts +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw/image_cache.py +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw.egg-info/dependency_links.txt +0 -0
- {entari_plugin_hyw-4.0.0rc4 → entari_plugin_hyw-4.0.0rc6}/src/entari_plugin_hyw.egg-info/top_level.txt +0 -0
{entari_plugin_hyw-4.0.0rc4/src/entari_plugin_hyw.egg-info → entari_plugin_hyw-4.0.0rc6}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: entari_plugin_hyw
|
|
3
|
-
Version: 4.0.
|
|
3
|
+
Version: 4.0.0rc6
|
|
4
4
|
Summary: Use large language models to interpret chat messages
|
|
5
5
|
Author-email: kumoSleeping <zjr2992@outlook.com>
|
|
6
6
|
License: MIT
|
|
@@ -20,9 +20,9 @@ Requires-Dist: arclet-entari[full]>=0.16.5
|
|
|
20
20
|
Requires-Dist: openai
|
|
21
21
|
Requires-Dist: httpx
|
|
22
22
|
Requires-Dist: markdown>=3.10
|
|
23
|
-
Requires-Dist:
|
|
24
|
-
Requires-Dist:
|
|
25
|
-
Requires-Dist:
|
|
23
|
+
Requires-Dist: DrissionPage>=4.1.1.2
|
|
24
|
+
Requires-Dist: trafilatura>=1.6.0
|
|
25
|
+
Requires-Dist: json-repair>=0.55.0
|
|
26
26
|
Provides-Extra: dev
|
|
27
27
|
Requires-Dist: entari-plugin-server>=0.5.0; extra == "dev"
|
|
28
28
|
Requires-Dist: satori-python-adapter-onebot11>=0.2.5; extra == "dev"
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "entari_plugin_hyw"
|
|
7
|
-
version = "4.0.0-
|
|
7
|
+
version = "4.0.0-rc6"
|
|
8
8
|
description = "Use large language models to interpret chat messages"
|
|
9
9
|
authors = [{name = "kumoSleeping", email = "zjr2992@outlook.com"}]
|
|
10
10
|
dependencies = [
|
|
@@ -12,9 +12,9 @@ dependencies = [
|
|
|
12
12
|
"openai",
|
|
13
13
|
"httpx",
|
|
14
14
|
"markdown>=3.10",
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
15
|
+
"DrissionPage>=4.1.1.2",
|
|
16
|
+
"trafilatura>=1.6.0",
|
|
17
|
+
"json-repair>=0.55.0",
|
|
18
18
|
]
|
|
19
19
|
requires-python = ">=3.10,<3.13"
|
|
20
20
|
readme = "README.md"
|
|
@@ -19,10 +19,10 @@ from loguru import logger
|
|
|
19
19
|
import arclet.letoderea as leto
|
|
20
20
|
from arclet.entari.event.command import CommandReceive
|
|
21
21
|
|
|
22
|
-
from .
|
|
22
|
+
from .modular_pipeline import ModularPipeline
|
|
23
23
|
from .history import HistoryManager
|
|
24
|
-
from .render_vue import ContentRenderer
|
|
25
|
-
from .misc import process_onebot_json, process_images, resolve_model_name, render_refuse_answer, REFUSE_ANSWER_MARKDOWN
|
|
24
|
+
from .render_vue import ContentRenderer, get_content_renderer
|
|
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
|
|
@@ -83,86 +83,128 @@ class _RecentEventDeduper:
|
|
|
83
83
|
|
|
84
84
|
_event_deduper = _RecentEventDeduper()
|
|
85
85
|
|
|
86
|
+
@dataclass
|
|
87
|
+
class ModelConfig:
|
|
88
|
+
"""Model configuration for a specific stage."""
|
|
89
|
+
model_name: Optional[str] = None
|
|
90
|
+
api_key: Optional[str] = None
|
|
91
|
+
base_url: Optional[str] = None
|
|
92
|
+
extra_body: Optional[Dict[str, Any]] = None
|
|
93
|
+
model_provider: Optional[str] = None
|
|
94
|
+
input_price: Optional[float] = None
|
|
95
|
+
output_price: Optional[float] = None
|
|
96
|
+
image_input: bool = True
|
|
97
|
+
|
|
98
|
+
|
|
86
99
|
@dataclass
|
|
87
100
|
class HywConfig(BasicConfModel):
|
|
101
|
+
# Core Settings
|
|
88
102
|
admins: List[str] = field(default_factory=list)
|
|
89
103
|
models: List[Dict[str, Any]] = field(default_factory=list)
|
|
90
104
|
question_command: str = "/q"
|
|
105
|
+
language: str = "Simplified Chinese"
|
|
106
|
+
temperature: float = 0.4
|
|
107
|
+
|
|
108
|
+
# Root-level defaults (backward compatible)
|
|
91
109
|
model_name: Optional[str] = None
|
|
92
110
|
api_key: Optional[str] = None
|
|
93
111
|
base_url: str = "https://openrouter.ai/api/v1"
|
|
94
|
-
vision_model_name: Optional[str] = None
|
|
95
|
-
vision_api_key: Optional[str] = None
|
|
96
|
-
language: str = "Simplified Chinese"
|
|
97
|
-
vision_base_url: Optional[str] = None
|
|
98
|
-
instruct_model_name: Optional[str] = None
|
|
99
|
-
instruct_api_key: Optional[str] = None
|
|
100
|
-
instruct_base_url: Optional[str] = None
|
|
101
|
-
search_base_url: str = "https://lite.duckduckgo.com/lite/?q={query}"
|
|
102
|
-
image_search_base_url: str = "https://duckduckgo.com/?q={query}&iax=images&ia=images"
|
|
103
|
-
headless: bool = False
|
|
104
|
-
save_conversation: bool = False
|
|
105
|
-
icon: str = "openai"
|
|
106
|
-
render_timeout_ms: int = 6000
|
|
107
|
-
render_image_timeout_ms: int = 3000
|
|
108
112
|
extra_body: Optional[Dict[str, Any]] = None
|
|
109
|
-
vision_extra_body: Optional[Dict[str, Any]] = None
|
|
110
|
-
instruct_extra_body: Optional[Dict[str, Any]] = None
|
|
111
|
-
enable_browser_fallback: bool = False
|
|
112
|
-
reaction: bool = False
|
|
113
|
-
quote: bool = True
|
|
114
|
-
temperature: float = 0.4
|
|
115
|
-
# Billing configuration (price per million tokens)
|
|
116
|
-
input_price: Optional[float] = None # $ per 1M input tokens
|
|
117
|
-
output_price: Optional[float] = None # $ per 1M output tokens
|
|
118
|
-
# Vision model pricing overrides (defaults to main model pricing if not set)
|
|
119
|
-
vision_input_price: Optional[float] = None
|
|
120
|
-
vision_output_price: Optional[float] = None
|
|
121
|
-
# Instruct model pricing overrides (defaults to main model pricing if not set)
|
|
122
|
-
instruct_input_price: Optional[float] = None
|
|
123
|
-
instruct_output_price: Optional[float] = None
|
|
124
|
-
# Provider Names
|
|
125
|
-
search_name: str = "DuckDuckGo"
|
|
126
|
-
search_provider: str = "crawl4ai" # crawl4ai | httpx | ddgs
|
|
127
|
-
fetch_provider: str = "crawl4ai" # crawl4ai | jinaai
|
|
128
|
-
jina_api_key: Optional[str] = None # Optional API key for Jina AI
|
|
129
113
|
model_provider: Optional[str] = None
|
|
130
|
-
|
|
131
|
-
|
|
114
|
+
input_price: Optional[float] = None
|
|
115
|
+
output_price: Optional[float] = None
|
|
116
|
+
|
|
117
|
+
# Nested Stage Configs
|
|
118
|
+
instruct: Optional[ModelConfig] = None
|
|
119
|
+
qa: Optional[ModelConfig] = None
|
|
120
|
+
main: Optional[ModelConfig] = None # Summary stage
|
|
132
121
|
|
|
133
122
|
# Search/Fetch Settings
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
123
|
+
search_engine: str = "google"
|
|
124
|
+
|
|
125
|
+
# Rendering Settings
|
|
126
|
+
headless: bool = False
|
|
127
|
+
render_timeout_ms: int = 6000
|
|
128
|
+
render_image_timeout_ms: int = 3000
|
|
129
|
+
|
|
130
|
+
# Bot Behavior
|
|
131
|
+
save_conversation: bool = False
|
|
132
|
+
reaction: bool = False
|
|
133
|
+
quote: bool = False
|
|
139
134
|
|
|
140
|
-
# Fetch Model Config
|
|
141
|
-
fetch_model_name: Optional[str] = None
|
|
142
|
-
fetch_api_key: Optional[str] = None
|
|
143
|
-
fetch_base_url: Optional[str] = None
|
|
144
|
-
fetch_extra_body: Optional[Dict[str, Any]] = None
|
|
145
|
-
fetch_input_price: Optional[float] = None
|
|
146
|
-
fetch_output_price: Optional[float] = None
|
|
147
|
-
# Summary Model Config
|
|
148
|
-
summary_model_name: Optional[str] = None
|
|
149
|
-
summary_api_key: Optional[str] = None
|
|
150
|
-
summary_base_url: Optional[str] = None
|
|
151
|
-
summary_extra_body: Optional[Dict[str, Any]] = None
|
|
152
|
-
summary_input_price: Optional[float] = None
|
|
153
|
-
summary_output_price: Optional[float] = None
|
|
154
135
|
# UI Theme
|
|
155
|
-
theme_color: str = "#
|
|
136
|
+
theme_color: str = "#ff0000"
|
|
156
137
|
|
|
157
138
|
def __post_init__(self):
|
|
158
139
|
"""Parse and normalize theme color after initialization."""
|
|
159
140
|
self.theme_color = parse_color(self.theme_color)
|
|
160
|
-
|
|
141
|
+
# Convert dicts to ModelConfig if needed
|
|
142
|
+
if isinstance(self.instruct, dict):
|
|
143
|
+
self.instruct = ModelConfig(**self.instruct)
|
|
144
|
+
if isinstance(self.qa, dict):
|
|
145
|
+
self.qa = ModelConfig(**self.qa)
|
|
146
|
+
if isinstance(self.main, dict):
|
|
147
|
+
self.main = ModelConfig(**self.main)
|
|
148
|
+
|
|
149
|
+
def get_model_config(self, stage: str) -> Dict[str, Any]:
|
|
150
|
+
"""
|
|
151
|
+
Get resolved model config for a stage.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
stage: "instruct", "qa", or "main" (summary)
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
Dict with model_name, api_key, base_url, extra_body, etc.
|
|
158
|
+
"""
|
|
159
|
+
# Determine primary and secondary config sources
|
|
160
|
+
primary = None
|
|
161
|
+
secondary = None
|
|
162
|
+
|
|
163
|
+
if stage == "instruct":
|
|
164
|
+
primary = self.instruct
|
|
165
|
+
secondary = self.main # Fallback to main
|
|
166
|
+
elif stage == "qa":
|
|
167
|
+
# QA fallback to main as well if ever used
|
|
168
|
+
primary = self.qa
|
|
169
|
+
secondary = self.main
|
|
170
|
+
elif stage == "main":
|
|
171
|
+
primary = self.main
|
|
172
|
+
|
|
173
|
+
# Build result with fallback logic
|
|
174
|
+
def resolve(field_name: str, is_essential: bool = True):
|
|
175
|
+
"""Resolve a field with fallback: Primary -> Secondary -> Root."""
|
|
176
|
+
# 1. Try Primary
|
|
177
|
+
val = getattr(primary, field_name, None) if primary else None
|
|
178
|
+
|
|
179
|
+
# 2. Try Secondary (if value missing)
|
|
180
|
+
if val is None and secondary:
|
|
181
|
+
val = getattr(secondary, field_name, None)
|
|
182
|
+
|
|
183
|
+
# 3. Try Root (if value still missing)
|
|
184
|
+
if val is None:
|
|
185
|
+
val = getattr(self, field_name, None)
|
|
186
|
+
return val
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
"model_name": resolve("model_name"),
|
|
190
|
+
"api_key": resolve("api_key"),
|
|
191
|
+
"base_url": resolve("base_url"),
|
|
192
|
+
"extra_body": resolve("extra_body", is_essential=False),
|
|
193
|
+
"model_provider": resolve("model_provider", is_essential=False),
|
|
194
|
+
"input_price": resolve("input_price", is_essential=False),
|
|
195
|
+
"output_price": resolve("output_price", is_essential=False),
|
|
196
|
+
}
|
|
161
197
|
|
|
162
198
|
|
|
163
199
|
conf = plugin_config(HywConfig)
|
|
164
200
|
history_manager = HistoryManager()
|
|
165
|
-
renderer = ContentRenderer()
|
|
201
|
+
renderer = ContentRenderer(headless=conf.headless)
|
|
202
|
+
from .render_vue import set_global_renderer
|
|
203
|
+
set_global_renderer(renderer)
|
|
204
|
+
|
|
205
|
+
# Pre-start Crawl4AI browser for fast fetching/screenshots
|
|
206
|
+
from .browser.service import prestart_browser, close_screenshot_service
|
|
207
|
+
# prestart_browser(headless=conf.headless) # Removed to avoid RuntimeError: no running event loop
|
|
166
208
|
|
|
167
209
|
|
|
168
210
|
class GlobalCache:
|
|
@@ -170,6 +212,18 @@ class GlobalCache:
|
|
|
170
212
|
|
|
171
213
|
global_cache = GlobalCache()
|
|
172
214
|
|
|
215
|
+
|
|
216
|
+
@listen(Cleanup)
|
|
217
|
+
async def cleanup_screenshot_service():
|
|
218
|
+
"""Cleanup shared browser on shutdown."""
|
|
219
|
+
try:
|
|
220
|
+
await close_screenshot_service()
|
|
221
|
+
# Also close the shared browser manager
|
|
222
|
+
from .browser.manager import close_shared_browser
|
|
223
|
+
await close_shared_browser()
|
|
224
|
+
except Exception as e:
|
|
225
|
+
logger.warning(f"Failed to cleanup browser services: {e}")
|
|
226
|
+
|
|
173
227
|
async def react(session: Session, emoji: str):
|
|
174
228
|
if not conf.reaction: return
|
|
175
229
|
try:
|
|
@@ -185,9 +239,7 @@ async def process_request(
|
|
|
185
239
|
conversation_key_override: Optional[str] = None,
|
|
186
240
|
local_mode: bool = False,
|
|
187
241
|
) -> None:
|
|
188
|
-
logger.info(f"Processing request: {all_param}")
|
|
189
242
|
mc = MessageChain(all_param)
|
|
190
|
-
logger.info(f"reply: {session.reply}")
|
|
191
243
|
if session.reply:
|
|
192
244
|
try:
|
|
193
245
|
# Check if reply is from self (the bot)
|
|
@@ -197,12 +249,10 @@ async def process_request(
|
|
|
197
249
|
|
|
198
250
|
if reply_msg_id and history_manager.is_bot_message(reply_msg_id):
|
|
199
251
|
is_bot = True
|
|
200
|
-
logger.info(f"Reply target {reply_msg_id} identified as bot message via history")
|
|
201
252
|
|
|
202
253
|
if is_bot:
|
|
203
|
-
|
|
254
|
+
pass # Reply is from bot - ignoring
|
|
204
255
|
else:
|
|
205
|
-
logger.info(f"Reply is from user (or unknown) - including content")
|
|
206
256
|
mc.extend(MessageChain(" ") + session.reply.origin.message)
|
|
207
257
|
except Exception as e:
|
|
208
258
|
logger.warning(f"Failed to process reply origin: {e}")
|
|
@@ -211,7 +261,7 @@ async def process_request(
|
|
|
211
261
|
# Filter and reconstruct MessageChain
|
|
212
262
|
filtered_elements = mc.get(Text) + mc.get(Image) + mc.get(Custom)
|
|
213
263
|
mc = MessageChain(filtered_elements)
|
|
214
|
-
|
|
264
|
+
|
|
215
265
|
|
|
216
266
|
text_content = str(mc.get(Text)).strip()
|
|
217
267
|
# Remove HTML image tags from text content to prevent "unreasonable code behavior"
|
|
@@ -263,10 +313,56 @@ async def process_request(
|
|
|
263
313
|
logger.warning(f"Vision model resolution warning for {vision_model}: {err_v}")
|
|
264
314
|
|
|
265
315
|
images, err = await process_images(mc, vision_model)
|
|
316
|
+
|
|
317
|
+
# Check image input support
|
|
318
|
+
model_cfg_dict = next((m for m in conf.models if m.get("name") == model), None)
|
|
319
|
+
image_input_supported = True
|
|
320
|
+
if model_cfg_dict:
|
|
321
|
+
image_input_supported = model_cfg_dict.get("image_input", True)
|
|
322
|
+
|
|
323
|
+
# Log inferenced content mode
|
|
324
|
+
inferred_content_mode = "image" if image_input_supported else "text"
|
|
325
|
+
logger.info(f"Process Request: Model '{model}' Image Input: {image_input_supported} -> Mode: {inferred_content_mode}")
|
|
326
|
+
|
|
327
|
+
if images and not image_input_supported:
|
|
328
|
+
logger.warning(f"Model '{model}' does not support images, but user sent {len(images)} images.")
|
|
329
|
+
|
|
330
|
+
# Start renderer for the unsupported card
|
|
331
|
+
renderer = await get_content_renderer()
|
|
332
|
+
render_tab_task = asyncio.create_task(renderer.prepare_tab())
|
|
333
|
+
|
|
334
|
+
# Wait for tab and render unsupported
|
|
335
|
+
try:
|
|
336
|
+
tab_id = await render_tab_task
|
|
337
|
+
except Exception as e:
|
|
338
|
+
tab_id = None
|
|
339
|
+
|
|
340
|
+
import tempfile
|
|
341
|
+
with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as tf:
|
|
342
|
+
output_path = tf.name
|
|
343
|
+
|
|
344
|
+
render_ok = await render_image_unsupported(
|
|
345
|
+
renderer=renderer,
|
|
346
|
+
output_path=output_path,
|
|
347
|
+
theme_color=conf.theme_color,
|
|
348
|
+
tab_id=tab_id
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
if render_ok:
|
|
352
|
+
with open(output_path, "rb") as f:
|
|
353
|
+
img_data = base64.b64encode(f.read()).decode()
|
|
354
|
+
await session.send(MessageChain(Image(src=f'data:image/png;base64,{img_data}')))
|
|
355
|
+
if os.path.exists(output_path):
|
|
356
|
+
os.remove(output_path)
|
|
357
|
+
return
|
|
358
|
+
|
|
359
|
+
renderer = await get_content_renderer()
|
|
360
|
+
render_tab_task = asyncio.create_task(renderer.prepare_tab())
|
|
361
|
+
tab_id = None
|
|
266
362
|
|
|
267
363
|
# Call Pipeline directly
|
|
268
364
|
safe_input = msg_text
|
|
269
|
-
pipeline =
|
|
365
|
+
pipeline = ModularPipeline(conf)
|
|
270
366
|
try:
|
|
271
367
|
resp = await pipeline.execute(
|
|
272
368
|
safe_input,
|
|
@@ -294,6 +390,13 @@ async def process_request(
|
|
|
294
390
|
content = final_resp.get("llm_response", "")
|
|
295
391
|
structured = final_resp.get("structured_response", {})
|
|
296
392
|
|
|
393
|
+
# Wait for tab preparation if needed (should be ready by now)
|
|
394
|
+
try:
|
|
395
|
+
tab_id = await render_tab_task
|
|
396
|
+
except Exception as e:
|
|
397
|
+
logger.warning(f"Failed to prepare render tab: {e}")
|
|
398
|
+
tab_id = None
|
|
399
|
+
|
|
297
400
|
# Render
|
|
298
401
|
import tempfile
|
|
299
402
|
with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as tf:
|
|
@@ -319,23 +422,24 @@ async def process_request(
|
|
|
319
422
|
output_path=output_path,
|
|
320
423
|
reason=final_resp.get('refuse_reason', 'Instruct 专家分配此任务流程失败,请尝试提出其他问题~'),
|
|
321
424
|
theme_color=conf.theme_color,
|
|
425
|
+
tab_id=tab_id,
|
|
322
426
|
)
|
|
323
427
|
else:
|
|
324
428
|
render_ok = await renderer.render(
|
|
325
429
|
markdown_content=content,
|
|
326
430
|
output_path=output_path,
|
|
431
|
+
tab_id=tab_id,
|
|
327
432
|
stats=stats_to_render,
|
|
328
433
|
references=structured.get("references", []),
|
|
329
434
|
page_references=structured.get("page_references", []),
|
|
330
435
|
image_references=structured.get("image_references", []),
|
|
331
436
|
stages_used=final_resp.get("stages_used", []),
|
|
332
|
-
image_timeout=conf.render_image_timeout_ms,
|
|
333
437
|
theme_color=conf.theme_color,
|
|
334
438
|
)
|
|
335
439
|
|
|
336
440
|
# Send & Save
|
|
337
441
|
if not render_ok:
|
|
338
|
-
logger.error("Render failed; skipping reply.
|
|
442
|
+
logger.error("Render failed; skipping reply.")
|
|
339
443
|
if os.path.exists(output_path):
|
|
340
444
|
try:
|
|
341
445
|
os.remove(output_path)
|
|
@@ -375,6 +479,24 @@ async def process_request(
|
|
|
375
479
|
code=display_session_id,
|
|
376
480
|
)
|
|
377
481
|
|
|
482
|
+
if conf.save_conversation and sent_id:
|
|
483
|
+
try:
|
|
484
|
+
# Pass web_results to save fetched pages as markdown, and output image
|
|
485
|
+
history_manager.save_to_disk(
|
|
486
|
+
sent_id,
|
|
487
|
+
web_results=final_resp.get("web_results"),
|
|
488
|
+
image_path=output_path if 'output_path' in locals() else None
|
|
489
|
+
)
|
|
490
|
+
except Exception as e:
|
|
491
|
+
logger.warning(f"Failed to save conversation: {e}")
|
|
492
|
+
|
|
493
|
+
# Cleanup temp image
|
|
494
|
+
if 'output_path' in locals() and output_path and os.path.exists(output_path):
|
|
495
|
+
try:
|
|
496
|
+
os.remove(output_path)
|
|
497
|
+
except Exception:
|
|
498
|
+
pass
|
|
499
|
+
|
|
378
500
|
|
|
379
501
|
|
|
380
502
|
|
|
@@ -391,9 +513,29 @@ async def process_request(
|
|
|
391
513
|
try:
|
|
392
514
|
# Use a temporary ID for error cases
|
|
393
515
|
error_id = f"error_{int(time.time())}_{secrets.token_hex(4)}"
|
|
394
|
-
|
|
395
|
-
#
|
|
396
|
-
|
|
516
|
+
|
|
517
|
+
# Try to salvage history
|
|
518
|
+
partial_hist = []
|
|
519
|
+
if 'resp' in locals() and resp:
|
|
520
|
+
partial_hist = resp.get("conversation_history", [])
|
|
521
|
+
elif 'context' in locals() and context and hasattr(context, 'instruct_history'):
|
|
522
|
+
partial_hist = context.instruct_history
|
|
523
|
+
|
|
524
|
+
related_ids = []
|
|
525
|
+
if 'session' in locals():
|
|
526
|
+
msg_id = str(session.event.message.id) if hasattr(session.event, 'message') else str(session.event.id)
|
|
527
|
+
related_ids = [msg_id]
|
|
528
|
+
|
|
529
|
+
history_manager.remember(error_id, partial_hist, related_ids, {"model": "error", "error": str(e)}, context_id, code=display_session_id if 'display_session_id' in locals() else None)
|
|
530
|
+
|
|
531
|
+
# Save debug data on error
|
|
532
|
+
web_res = context.web_results if 'context' in locals() and context else []
|
|
533
|
+
|
|
534
|
+
history_manager.save_to_disk(
|
|
535
|
+
error_id,
|
|
536
|
+
web_results=web_res
|
|
537
|
+
)
|
|
538
|
+
|
|
397
539
|
except Exception as save_err:
|
|
398
540
|
logger.error(f"Failed to save error conversation: {save_err}")
|
|
399
541
|
|
|
@@ -419,9 +561,8 @@ async def handle_question_command(session: Session[MessageCreatedEvent], result:
|
|
|
419
561
|
logger.info(f"Question Command Triggered. Message: {session.event.message}")
|
|
420
562
|
|
|
421
563
|
args = result.all_matched_args
|
|
422
|
-
logger.info(f"Matched Args: {args}")
|
|
423
564
|
|
|
424
|
-
await process_request(session, args.get("all_param"), selected_model=None, selected_vision_model=None, conversation_key_override=None
|
|
565
|
+
await process_request(session, args.get("all_param"), selected_model=None, selected_vision_model=None, conversation_key_override=None)
|
|
425
566
|
|
|
426
567
|
metadata("hyw", author=[{"name": "kumoSleeping", "email": "zjr2992@outlook.com"}], version=__version__, config=HywConfig)
|
|
427
568
|
|