entari-plugin-hyw 3.4.2__tar.gz → 3.5.0rc2__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.

Files changed (116) hide show
  1. {entari_plugin_hyw-3.4.2/src/entari_plugin_hyw.egg-info → entari_plugin_hyw-3.5.0rc2}/PKG-INFO +5 -2
  2. {entari_plugin_hyw-3.4.2 → entari_plugin_hyw-3.5.0rc2}/README.md +4 -1
  3. {entari_plugin_hyw-3.4.2 → entari_plugin_hyw-3.5.0rc2}/pyproject.toml +1 -1
  4. {entari_plugin_hyw-3.4.2 → entari_plugin_hyw-3.5.0rc2}/src/entari_plugin_hyw/__init__.py +78 -158
  5. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/card-dist/index.html +396 -0
  6. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/card-dist/vite.svg +1 -0
  7. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/icon/anthropic.svg +1 -0
  8. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/icon/cerebras.svg +9 -0
  9. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/icon/deepseek.png +0 -0
  10. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/icon/gemini.svg +1 -0
  11. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/icon/google.svg +1 -0
  12. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/icon/grok.png +0 -0
  13. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/icon/huggingface.png +0 -0
  14. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/icon/microsoft.svg +15 -0
  15. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/icon/minimax.png +0 -0
  16. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/icon/mistral.png +0 -0
  17. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/icon/nvida.png +0 -0
  18. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/icon/openai.svg +1 -0
  19. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/icon/openrouter.png +0 -0
  20. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/icon/perplexity.svg +24 -0
  21. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/icon/qwen.png +0 -0
  22. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/icon/xai.png +0 -0
  23. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/icon/xiaomi.png +0 -0
  24. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/icon/zai.png +0 -0
  25. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/.gitignore +24 -0
  26. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/README.md +5 -0
  27. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/index.html +16 -0
  28. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/package-lock.json +2342 -0
  29. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/package.json +31 -0
  30. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/public/logos/anthropic.svg +1 -0
  31. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/public/logos/cerebras.svg +9 -0
  32. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/public/logos/deepseek.png +0 -0
  33. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/public/logos/gemini.svg +1 -0
  34. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/public/logos/google.svg +1 -0
  35. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/public/logos/grok.png +0 -0
  36. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/public/logos/huggingface.png +0 -0
  37. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/public/logos/microsoft.svg +15 -0
  38. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/public/logos/minimax.png +0 -0
  39. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/public/logos/mistral.png +0 -0
  40. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/public/logos/nvida.png +0 -0
  41. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/public/logos/openai.svg +1 -0
  42. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/public/logos/openrouter.png +0 -0
  43. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/public/logos/perplexity.svg +24 -0
  44. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/public/logos/qwen.png +0 -0
  45. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/public/logos/xai.png +0 -0
  46. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/public/logos/xiaomi.png +0 -0
  47. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/public/logos/zai.png +0 -0
  48. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/public/vite.svg +1 -0
  49. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/src/App.vue +410 -0
  50. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/src/assets/vue.svg +1 -0
  51. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/src/components/HelloWorld.vue +41 -0
  52. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/src/components/MarkdownContent.vue +385 -0
  53. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/src/components/SectionCard.vue +41 -0
  54. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/src/components/StageCard.vue +183 -0
  55. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/src/main.ts +5 -0
  56. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/src/style.css +8 -0
  57. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/src/test_regex.js +103 -0
  58. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/src/types.ts +52 -0
  59. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/tsconfig.app.json +16 -0
  60. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/tsconfig.json +7 -0
  61. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/tsconfig.node.json +26 -0
  62. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/card-ui/vite.config.ts +16 -0
  63. {entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/core → entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw}/history.py +25 -1
  64. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/image_cache.py +283 -0
  65. {entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/utils → entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw}/misc.py +0 -3
  66. {entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/core → entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw}/pipeline.py +236 -86
  67. entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/utils/prompts_cn.py → entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/prompts.py +10 -25
  68. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/render_vue.py +314 -0
  69. {entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/utils → entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw}/search.py +227 -10
  70. {entari_plugin_hyw-3.4.2 → entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw.egg-info}/PKG-INFO +5 -2
  71. entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw.egg-info/SOURCES.txt +92 -0
  72. entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/assets/libs/highlight.css +0 -10
  73. entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/assets/libs/highlight.js +0 -1213
  74. entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/assets/libs/katex-auto-render.js +0 -1
  75. entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/assets/libs/katex.css +0 -1
  76. entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/assets/libs/katex.js +0 -1
  77. entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/assets/libs/tailwind.css +0 -1
  78. entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/assets/package-lock.json +0 -953
  79. entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/assets/package.json +0 -16
  80. entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/assets/tailwind.config.js +0 -12
  81. entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/assets/tailwind.input.css +0 -235
  82. entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/assets/template.html +0 -157
  83. entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/assets/template.html.bak +0 -157
  84. entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/assets/template.j2 +0 -400
  85. entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/core/__init__.py +0 -0
  86. entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/core/config.py +0 -38
  87. entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/core/hyw.py +0 -48
  88. entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/core/render.py +0 -630
  89. entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/utils/__init__.py +0 -2
  90. entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/utils/browser.py +0 -40
  91. entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/utils/playwright_tool.py +0 -36
  92. entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/utils/prompts.py +0 -119
  93. entari_plugin_hyw-3.4.2/src/entari_plugin_hyw.egg-info/SOURCES.txt +0 -53
  94. {entari_plugin_hyw-3.4.2 → entari_plugin_hyw-3.5.0rc2}/MANIFEST.in +0 -0
  95. {entari_plugin_hyw-3.4.2 → entari_plugin_hyw-3.5.0rc2}/setup.cfg +0 -0
  96. {entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/assets/icon → entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/card-dist/logos}/anthropic.svg +0 -0
  97. {entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/assets/icon → entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/card-dist/logos}/cerebras.svg +0 -0
  98. {entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/assets/icon → entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/card-dist/logos}/deepseek.png +0 -0
  99. {entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/assets/icon → entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/card-dist/logos}/gemini.svg +0 -0
  100. {entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/assets/icon → entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/card-dist/logos}/google.svg +0 -0
  101. {entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/assets/icon → entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/card-dist/logos}/grok.png +0 -0
  102. {entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/assets/icon → entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/card-dist/logos}/huggingface.png +0 -0
  103. {entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/assets/icon → entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/card-dist/logos}/microsoft.svg +0 -0
  104. {entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/assets/icon → entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/card-dist/logos}/minimax.png +0 -0
  105. {entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/assets/icon → entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/card-dist/logos}/mistral.png +0 -0
  106. {entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/assets/icon → entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/card-dist/logos}/nvida.png +0 -0
  107. {entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/assets/icon → entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/card-dist/logos}/openai.svg +0 -0
  108. {entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/assets/icon → entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/card-dist/logos}/openrouter.png +0 -0
  109. {entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/assets/icon → entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/card-dist/logos}/perplexity.svg +0 -0
  110. {entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/assets/icon → entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/card-dist/logos}/qwen.png +0 -0
  111. {entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/assets/icon → entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/card-dist/logos}/xai.png +0 -0
  112. {entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/assets/icon → entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/card-dist/logos}/xiaomi.png +0 -0
  113. {entari_plugin_hyw-3.4.2/src/entari_plugin_hyw/assets/icon → entari_plugin_hyw-3.5.0rc2/src/entari_plugin_hyw/assets/card-dist/logos}/zai.png +0 -0
  114. {entari_plugin_hyw-3.4.2 → entari_plugin_hyw-3.5.0rc2}/src/entari_plugin_hyw.egg-info/dependency_links.txt +0 -0
  115. {entari_plugin_hyw-3.4.2 → entari_plugin_hyw-3.5.0rc2}/src/entari_plugin_hyw.egg-info/requires.txt +0 -0
  116. {entari_plugin_hyw-3.4.2 → entari_plugin_hyw-3.5.0rc2}/src/entari_plugin_hyw.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: entari_plugin_hyw
3
- Version: 3.4.2
3
+ Version: 3.5.0rc2
4
4
  Summary: Use large language models to interpret chat messages
5
5
  Author-email: kumoSleeping <zjr2992@outlook.com>
6
6
  License: MIT
@@ -75,8 +75,11 @@ Configure the plugin in your `entari.yml`.
75
75
  ```yaml
76
76
  plugins:
77
77
  entari_plugin_hyw:
78
- model_name: google/gemini-3-flash-preview
78
+ model_name: google/gemini-2.0-flash-exp
79
79
  api_key: "your-or-api-key-here"
80
+ # Rendering Configuration
81
+ render_timeout_ms: 6000 # Browser wait timeout
82
+ render_image_timeout_ms: 3000 # Image load wait timeout
80
83
  ```
81
84
 
82
85
  ## Usage
@@ -46,8 +46,11 @@ Configure the plugin in your `entari.yml`.
46
46
  ```yaml
47
47
  plugins:
48
48
  entari_plugin_hyw:
49
- model_name: google/gemini-3-flash-preview
49
+ model_name: google/gemini-2.0-flash-exp
50
50
  api_key: "your-or-api-key-here"
51
+ # Rendering Configuration
52
+ render_timeout_ms: 6000 # Browser wait timeout
53
+ render_image_timeout_ms: 3000 # Image load wait timeout
51
54
  ```
52
55
 
53
56
  ## Usage
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "entari_plugin_hyw"
7
- version = "3.4.2"
7
+ version = "3.5.0-rc2"
8
8
  description = "Use large language models to interpret chat messages"
9
9
  authors = [{name = "kumoSleeping", email = "zjr2992@outlook.com"}]
10
10
  dependencies = [
@@ -1,19 +1,28 @@
1
1
  from dataclasses import dataclass, field
2
+ from importlib.metadata import version as get_version
2
3
  from typing import List, Dict, Any, Optional, Union
3
4
  import time
5
+ import asyncio
6
+
7
+ # 从 pyproject.toml 读取版本号,避免重复维护
8
+ try:
9
+ __version__ = get_version("entari_plugin_hyw")
10
+ except Exception:
11
+ __version__ = "0.0.0"
4
12
 
5
13
  from arclet.alconna import Alconna, Args, AllParam, CommandMeta, Option, Arparma, MultiVar, store_true
6
14
  from arclet.entari import metadata, listen, Session, plugin_config, BasicConfModel, plugin, command
15
+ from arclet.letoderea import on
7
16
  from arclet.entari import MessageChain, Text, Image, MessageCreatedEvent, Quote, At
8
17
  from satori.element import Custom
9
18
  from loguru import logger
10
19
  import arclet.letoderea as leto
11
20
  from arclet.entari.event.command import CommandReceive
12
21
 
13
- from .core.hyw import HYW
14
- from .core.history import HistoryManager
15
- from .core.render import ContentRenderer
16
- from .utils.misc import process_onebot_json, process_images, resolve_model_name
22
+ from .pipeline import ProcessingPipeline
23
+ from .history import HistoryManager
24
+ from .render_vue import ContentRenderer
25
+ from .misc import process_onebot_json, process_images, resolve_model_name
17
26
  from arclet.entari.event.lifespan import Cleanup
18
27
 
19
28
  import os
@@ -22,6 +31,32 @@ import base64
22
31
 
23
32
  import re
24
33
 
34
+
35
+ def parse_color(color: str) -> str:
36
+ """
37
+ Parse color from hex or RGB tuple to hex format.
38
+ Supports: #ff0000, ff0000, (255, 0, 0), 255,0,0
39
+ """
40
+ if not color:
41
+ return "#ef4444"
42
+
43
+ color = str(color).strip()
44
+
45
+ # Hex format: #fff or #ffffff or ffffff
46
+ if color.startswith('#') and len(color) in [4, 7]:
47
+ return color
48
+ if re.match(r'^[0-9a-fA-F]{6}$', color):
49
+ return f'#{color}'
50
+
51
+ # RGB tuple: (r, g, b) or r,g,b
52
+ rgb_match = re.match(r'^\(?(\d+)[,\s]+(\d+)[,\s]+(\d+)\)?$', color)
53
+ if rgb_match:
54
+ r, g, b = (max(0, min(255, int(x))) for x in rgb_match.groups())
55
+ return f'#{r:02x}{g:02x}{b:02x}'
56
+
57
+ logger.warning(f"Invalid color '{color}', using default #ef4444")
58
+ return "#ef4444"
59
+
25
60
  class _RecentEventDeduper:
26
61
  def __init__(self, ttl_seconds: float = 30.0, max_size: int = 2048):
27
62
  self.ttl_seconds = ttl_seconds
@@ -69,6 +104,7 @@ class HywConfig(BasicConfModel):
69
104
  save_conversation: bool = False
70
105
  icon: str = "openai"
71
106
  render_timeout_ms: int = 6000
107
+ render_image_timeout_ms: int = 3000
72
108
  extra_body: Optional[Dict[str, Any]] = None
73
109
  vision_extra_body: Optional[Dict[str, Any]] = None
74
110
  instruct_extra_body: Optional[Dict[str, Any]] = None
@@ -88,81 +124,45 @@ class HywConfig(BasicConfModel):
88
124
  # Provider Names
89
125
  search_name: str = "DuckDuckGo"
90
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
91
129
  model_provider: Optional[str] = None
92
130
  vision_model_provider: Optional[str] = None
93
131
  instruct_model_provider: Optional[str] = None
132
+ # UI Theme
133
+ theme_color: str = "#ef4444" # Tailwind red-500, supports hex/RGB/color names
134
+
135
+ def __post_init__(self):
136
+ """Parse and normalize theme color after initialization."""
137
+ self.theme_color = parse_color(self.theme_color)
94
138
 
95
139
 
96
140
 
97
141
  conf = plugin_config(HywConfig)
98
142
  history_manager = HistoryManager()
99
143
  renderer = ContentRenderer()
100
- hyw = HYW(config=conf)
101
-
102
-
103
144
 
104
145
 
105
- @listen(Cleanup, once=True)
106
- async def _hyw_cleanup():
107
- try:
108
- await hyw.close()
109
- except Exception as e:
110
- logger.warning(f"HYW cleanup error: {e}")
111
-
112
146
  class GlobalCache:
113
147
  models_image_path: Optional[str] = None
114
148
 
115
149
  global_cache = GlobalCache()
116
150
 
117
- from satori.exception import ActionFailed
118
- from satori.adapters.onebot11.reverse import _Connection
119
-
120
- # Monkeypatch to suppress ActionFailed for get_msg
121
- original_call_api = _Connection.call_api
122
-
123
- async def patched_call_api(self, action: str, params: dict = None):
124
- try:
125
- return await original_call_api(self, action, params)
126
- except ActionFailed as e:
127
- if action == "get_msg":
128
- logger.warning(f"Suppressed ActionFailed for get_msg: {e}")
129
- return None
130
- raise e
131
-
132
- _Connection.call_api = patched_call_api
133
-
134
- EMOJI_TO_CODE = {
135
- "✨": "10024",
136
- "✅": "10004",
137
- "❌": "10060"
138
- }
139
-
140
151
  async def react(session: Session, emoji: str):
141
152
  if not conf.reaction: return
142
153
  try:
143
- if session.event.login.platform == "onebot":
144
- code = EMOJI_TO_CODE.get(emoji, "10024")
145
- # OneBot specific reaction
146
- await session.account.protocol.call_api(
147
- "internal/set_group_reaction",
148
- {
149
- "group_id": str(session.guild.id),
150
- "message_id": str(session.event.message.id),
151
- "code": code,
152
- "is_add": True
153
- }
154
- )
155
- else:
156
- # Standard Satori reaction
157
- await session.reaction_create(emoji=emoji)
158
- except ActionFailed:
159
- pass
154
+ await session.reaction_create(emoji=emoji)
160
155
  except Exception as e:
161
156
  logger.warning(f"Reaction failed: {e}")
162
157
 
163
- async def process_request(session: Session[MessageCreatedEvent], all_param: Optional[MessageChain] = None,
164
- selected_model: Optional[str] = None, selected_vision_model: Optional[str] = None,
165
- conversation_key_override: Optional[str] = None, local_mode: bool = False):
158
+ async def process_request(
159
+ session: Session[MessageCreatedEvent],
160
+ all_param: Optional[MessageChain] = None,
161
+ selected_model: Optional[str] = None,
162
+ selected_vision_model: Optional[str] = None,
163
+ conversation_key_override: Optional[str] = None,
164
+ local_mode: bool = False,
165
+ ) -> None:
166
166
  logger.info(f"Processing request: {all_param}")
167
167
  mc = MessageChain(all_param)
168
168
  logger.info(f"reply: {session.reply}")
@@ -242,12 +242,19 @@ async def process_request(session: Session[MessageCreatedEvent], all_param: Opti
242
242
 
243
243
  images, err = await process_images(mc, vision_model)
244
244
 
245
- # Call Agent (Step 1)
246
- # Sanitize user_input: use extracted text only
245
+ # Call Pipeline directly
247
246
  safe_input = msg_text
248
-
249
- resp = await hyw.agent(safe_input, conversation_history=hist_payload, images=images,
250
- selected_model=model, selected_vision_model=vision_model, local_mode=local_mode)
247
+ pipeline = ProcessingPipeline(conf)
248
+ try:
249
+ resp = await pipeline.execute(
250
+ safe_input,
251
+ hist_payload,
252
+ model_name=model,
253
+ images=images,
254
+ selected_vision_model=vision_model,
255
+ )
256
+ finally:
257
+ await pipeline.close()
251
258
 
252
259
  # Step 1 Results
253
260
  step1_vision_model = resp.get("vision_model_used")
@@ -270,111 +277,28 @@ async def process_request(session: Session[MessageCreatedEvent], all_param: Opti
270
277
  with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as tf:
271
278
  output_path = tf.name
272
279
  model_used = final_resp.get("model_used")
273
- vision_model_used = final_resp.get("vision_model_used")
274
-
275
- # Helper to infer icon from model name
276
- def infer_icon_from_model(model_name: str) -> str:
277
- """Infer icon name from model name (e.g. 'google/gemini-3-flash' -> 'google' or 'gemini')"""
278
- if not model_name:
279
- return conf.icon
280
- name_lower = model_name.lower()
281
- # Check for known providers/models in the name
282
- known_icons = ["google", "gemini", "openai", "anthropic", "deepseek", "mistral",
283
- "qwen", "grok", "xai", "perplexity", "microsoft", "minimax", "nvidia"]
284
- for icon_name in known_icons:
285
- if icon_name in name_lower:
286
- return icon_name
287
- return conf.icon
288
-
289
- icon = conf.icon
290
- m_conf = None
291
- if model_used:
292
- m_conf = next((m for m in conf.models if m.get("name") == model_used), None)
293
- if m_conf:
294
- icon = m_conf.get("icon", infer_icon_from_model(model_used))
295
- else:
296
- # Model not in config list, infer from name
297
- icon = infer_icon_from_model(model_used)
298
280
 
299
281
  # Determine session short code
300
282
  if hist_key:
301
283
  display_session_id = history_manager.get_code_by_key(hist_key)
302
284
  if not display_session_id:
303
- # Should not happen if key exists, but fallback
304
285
  display_session_id = history_manager.generate_short_code()
305
286
  else:
306
- # New conversation, pre-generate code
307
287
  display_session_id = history_manager.generate_short_code()
308
288
 
309
- # Determine vision base url and icon
310
- vision_base_url = None
311
- vision_icon = None
312
-
313
- if vision_model_used:
314
- v_conf = next((m for m in conf.models if m.get("name") == vision_model_used), None)
315
- if v_conf:
316
- vision_base_url = v_conf.get("base_url")
317
- vision_icon = v_conf.get("icon", infer_icon_from_model(vision_model_used))
318
- else:
319
- vision_icon = infer_icon_from_model(vision_model_used)
320
-
321
- # Handle Vision Only Mode (suppress text model display)
322
- render_model_name = model_used or conf.model_name or "unknown"
323
- render_icon = icon
324
- render_base_url = m_conf.get("base_url", conf.base_url) if m_conf else conf.base_url
325
-
326
- if not model_used and vision_model_used:
327
- render_model_name = ""
328
- render_icon = ""
329
-
330
289
  # Use stats_list if available, otherwise standard stats
331
290
  stats_to_render = final_resp.get("stats_list", final_resp.get("stats", {}))
332
-
333
- # Determine Behavior Summary & Provider Name
334
-
335
- # 1. Behavior Summary
336
- behavior_summary = "Text Generation"
337
- if vision_model_used:
338
- behavior_summary = "Visual Analysis"
339
- elif any(s.get("name") == "Search" for s in final_resp.get("stages_used", []) or []):
340
- behavior_summary = "Search-Augmented"
341
-
342
- # 2. Provider Name
343
- # Try to get from m_conf (resolved above)
344
- provider_name = "Unknown Provider"
345
- if model_used and m_conf:
346
- provider_name = m_conf.get("provider", "Unknown Provider")
347
- elif not model_used and vision_model_used:
348
- # If only vision model used (unlikely but possible in code logic)
349
- if 'v_conf' in locals() and v_conf:
350
- provider_name = v_conf.get("provider", "Unknown Provider")
351
-
352
- # If still unknown and we have base_url, maybe use domain as last resort fallback?
353
- # User said: "provider does not automatically get from url if not filled"
354
- # So if it's "Unknown Provider", we leave it or maybe empty string?
355
- # Let's stick to "Unknown Provider" or just empty if we want to be clean.
356
- # But for UI validation it's better to show something if missing config.
357
291
 
358
292
  render_ok = await renderer.render(
359
293
  markdown_content=content,
360
294
  output_path=output_path,
361
- suggestions=[],
362
295
  stats=stats_to_render,
363
296
  references=structured.get("references", []),
364
297
  page_references=structured.get("page_references", []),
365
298
  image_references=structured.get("image_references", []),
366
- flow_steps=structured.get("flow_steps", []),
367
299
  stages_used=final_resp.get("stages_used", []),
368
- model_name=render_model_name,
369
- provider_name=provider_name,
370
- behavior_summary=behavior_summary,
371
- icon_config=render_icon,
372
- vision_model_name=vision_model_used,
373
- vision_base_url=vision_base_url,
374
- vision_icon_config=vision_icon,
375
- base_url=render_base_url,
376
- billing_info=final_resp.get("billing_info"),
377
- render_timeout_ms=conf.render_timeout_ms
300
+ image_timeout=conf.render_image_timeout_ms,
301
+ theme_color=conf.theme_color,
378
302
  )
379
303
 
380
304
  # Send & Save
@@ -443,18 +367,16 @@ async def process_request(session: Session[MessageCreatedEvent], all_param: Opti
443
367
  logger.error(f"Failed to save error conversation: {save_err}")
444
368
 
445
369
 
446
-
447
- # Main Command (Question)
448
370
  alc = Alconna(
449
371
  conf.question_command,
450
372
  Args["all_param;?", AllParam],
451
373
  )
452
374
 
453
- @command.on(alc)
375
+ @command.on(alc)
454
376
  async def handle_question_command(session: Session[MessageCreatedEvent], result: Arparma):
455
377
  """Handle main Question command"""
456
378
  try:
457
- # logger.info(f"Question Command Triggered. Message: {result}")
379
+ logger.info(f"Question Command Triggered. Message: {result}")
458
380
  mid = str(session.event.message.id) if getattr(session.event, "message", None) else str(session.event.id)
459
381
  dedupe_key = f"{getattr(session.account, 'id', 'account')}:{mid}"
460
382
  if _event_deduper.seen_recently(dedupe_key):
@@ -468,14 +390,12 @@ async def handle_question_command(session: Session[MessageCreatedEvent], result:
468
390
  args = result.all_matched_args
469
391
  logger.info(f"Matched Args: {args}")
470
392
 
471
- # Only all_param is supported now
472
- # Context ID for history lookup is automatically handled in process_request
473
-
474
393
  await process_request(session, args.get("all_param"), selected_model=None, selected_vision_model=None, conversation_key_override=None, local_mode=False)
475
394
 
476
- metadata("hyw", author=[{"name": "kumoSleeping", "email": "zjr2992@outlook.com"}], version="3.2.105", config=HywConfig)
395
+ metadata("hyw", author=[{"name": "kumoSleeping", "email": "zjr2992@outlook.com"}], version=__version__, config=HywConfig)
396
+
477
397
 
478
- @leto.on(CommandReceive)
398
+ @listen(CommandReceive)
479
399
  async def remove_at(content: MessageChain):
480
- content = content.lstrip(At)
481
- return content
400
+ logger.info(f"remove_at: {content}")
401
+ return content.lstrip(At)