entari-plugin-hyw 4.0.0rc5__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.

Files changed (99) hide show
  1. entari_plugin_hyw/__init__.py +532 -0
  2. entari_plugin_hyw/assets/card-dist/index.html +387 -0
  3. entari_plugin_hyw/assets/card-dist/logos/anthropic.svg +1 -0
  4. entari_plugin_hyw/assets/card-dist/logos/cerebras.svg +9 -0
  5. entari_plugin_hyw/assets/card-dist/logos/deepseek.png +0 -0
  6. entari_plugin_hyw/assets/card-dist/logos/gemini.svg +1 -0
  7. entari_plugin_hyw/assets/card-dist/logos/google.svg +1 -0
  8. entari_plugin_hyw/assets/card-dist/logos/grok.png +0 -0
  9. entari_plugin_hyw/assets/card-dist/logos/huggingface.png +0 -0
  10. entari_plugin_hyw/assets/card-dist/logos/microsoft.svg +15 -0
  11. entari_plugin_hyw/assets/card-dist/logos/minimax.png +0 -0
  12. entari_plugin_hyw/assets/card-dist/logos/mistral.png +0 -0
  13. entari_plugin_hyw/assets/card-dist/logos/nvida.png +0 -0
  14. entari_plugin_hyw/assets/card-dist/logos/openai.svg +1 -0
  15. entari_plugin_hyw/assets/card-dist/logos/openrouter.png +0 -0
  16. entari_plugin_hyw/assets/card-dist/logos/perplexity.svg +24 -0
  17. entari_plugin_hyw/assets/card-dist/logos/qwen.png +0 -0
  18. entari_plugin_hyw/assets/card-dist/logos/xai.png +0 -0
  19. entari_plugin_hyw/assets/card-dist/logos/xiaomi.png +0 -0
  20. entari_plugin_hyw/assets/card-dist/logos/zai.png +0 -0
  21. entari_plugin_hyw/assets/card-dist/vite.svg +1 -0
  22. entari_plugin_hyw/assets/icon/anthropic.svg +1 -0
  23. entari_plugin_hyw/assets/icon/cerebras.svg +9 -0
  24. entari_plugin_hyw/assets/icon/deepseek.png +0 -0
  25. entari_plugin_hyw/assets/icon/gemini.svg +1 -0
  26. entari_plugin_hyw/assets/icon/google.svg +1 -0
  27. entari_plugin_hyw/assets/icon/grok.png +0 -0
  28. entari_plugin_hyw/assets/icon/huggingface.png +0 -0
  29. entari_plugin_hyw/assets/icon/microsoft.svg +15 -0
  30. entari_plugin_hyw/assets/icon/minimax.png +0 -0
  31. entari_plugin_hyw/assets/icon/mistral.png +0 -0
  32. entari_plugin_hyw/assets/icon/nvida.png +0 -0
  33. entari_plugin_hyw/assets/icon/openai.svg +1 -0
  34. entari_plugin_hyw/assets/icon/openrouter.png +0 -0
  35. entari_plugin_hyw/assets/icon/perplexity.svg +24 -0
  36. entari_plugin_hyw/assets/icon/qwen.png +0 -0
  37. entari_plugin_hyw/assets/icon/xai.png +0 -0
  38. entari_plugin_hyw/assets/icon/xiaomi.png +0 -0
  39. entari_plugin_hyw/assets/icon/zai.png +0 -0
  40. entari_plugin_hyw/browser/__init__.py +10 -0
  41. entari_plugin_hyw/browser/engines/base.py +13 -0
  42. entari_plugin_hyw/browser/engines/bing.py +95 -0
  43. entari_plugin_hyw/browser/engines/searxng.py +137 -0
  44. entari_plugin_hyw/browser/landing.html +172 -0
  45. entari_plugin_hyw/browser/manager.py +153 -0
  46. entari_plugin_hyw/browser/service.py +275 -0
  47. entari_plugin_hyw/card-ui/.gitignore +24 -0
  48. entari_plugin_hyw/card-ui/README.md +5 -0
  49. entari_plugin_hyw/card-ui/index.html +16 -0
  50. entari_plugin_hyw/card-ui/package-lock.json +2342 -0
  51. entari_plugin_hyw/card-ui/package.json +31 -0
  52. entari_plugin_hyw/card-ui/public/logos/anthropic.svg +1 -0
  53. entari_plugin_hyw/card-ui/public/logos/cerebras.svg +9 -0
  54. entari_plugin_hyw/card-ui/public/logos/deepseek.png +0 -0
  55. entari_plugin_hyw/card-ui/public/logos/gemini.svg +1 -0
  56. entari_plugin_hyw/card-ui/public/logos/google.svg +1 -0
  57. entari_plugin_hyw/card-ui/public/logos/grok.png +0 -0
  58. entari_plugin_hyw/card-ui/public/logos/huggingface.png +0 -0
  59. entari_plugin_hyw/card-ui/public/logos/microsoft.svg +15 -0
  60. entari_plugin_hyw/card-ui/public/logos/minimax.png +0 -0
  61. entari_plugin_hyw/card-ui/public/logos/mistral.png +0 -0
  62. entari_plugin_hyw/card-ui/public/logos/nvida.png +0 -0
  63. entari_plugin_hyw/card-ui/public/logos/openai.svg +1 -0
  64. entari_plugin_hyw/card-ui/public/logos/openrouter.png +0 -0
  65. entari_plugin_hyw/card-ui/public/logos/perplexity.svg +24 -0
  66. entari_plugin_hyw/card-ui/public/logos/qwen.png +0 -0
  67. entari_plugin_hyw/card-ui/public/logos/xai.png +0 -0
  68. entari_plugin_hyw/card-ui/public/logos/xiaomi.png +0 -0
  69. entari_plugin_hyw/card-ui/public/logos/zai.png +0 -0
  70. entari_plugin_hyw/card-ui/public/vite.svg +1 -0
  71. entari_plugin_hyw/card-ui/src/App.vue +756 -0
  72. entari_plugin_hyw/card-ui/src/assets/vue.svg +1 -0
  73. entari_plugin_hyw/card-ui/src/components/HelloWorld.vue +41 -0
  74. entari_plugin_hyw/card-ui/src/components/MarkdownContent.vue +382 -0
  75. entari_plugin_hyw/card-ui/src/components/SectionCard.vue +41 -0
  76. entari_plugin_hyw/card-ui/src/components/StageCard.vue +240 -0
  77. entari_plugin_hyw/card-ui/src/main.ts +5 -0
  78. entari_plugin_hyw/card-ui/src/style.css +29 -0
  79. entari_plugin_hyw/card-ui/src/test_regex.js +103 -0
  80. entari_plugin_hyw/card-ui/src/types.ts +61 -0
  81. entari_plugin_hyw/card-ui/tsconfig.app.json +16 -0
  82. entari_plugin_hyw/card-ui/tsconfig.json +7 -0
  83. entari_plugin_hyw/card-ui/tsconfig.node.json +26 -0
  84. entari_plugin_hyw/card-ui/vite.config.ts +16 -0
  85. entari_plugin_hyw/definitions.py +130 -0
  86. entari_plugin_hyw/history.py +248 -0
  87. entari_plugin_hyw/image_cache.py +274 -0
  88. entari_plugin_hyw/misc.py +135 -0
  89. entari_plugin_hyw/modular_pipeline.py +351 -0
  90. entari_plugin_hyw/render_vue.py +401 -0
  91. entari_plugin_hyw/search.py +116 -0
  92. entari_plugin_hyw/stage_base.py +88 -0
  93. entari_plugin_hyw/stage_instruct.py +328 -0
  94. entari_plugin_hyw/stage_instruct_review.py +92 -0
  95. entari_plugin_hyw/stage_summary.py +164 -0
  96. entari_plugin_hyw-4.0.0rc5.dist-info/METADATA +116 -0
  97. entari_plugin_hyw-4.0.0rc5.dist-info/RECORD +99 -0
  98. entari_plugin_hyw-4.0.0rc5.dist-info/WHEEL +5 -0
  99. entari_plugin_hyw-4.0.0rc5.dist-info/top_level.txt +1 -0
@@ -0,0 +1,275 @@
1
+ """
2
+ Browser Service (DrissionPage)
3
+
4
+ Provides page fetching and screenshot capabilities using DrissionPage.
5
+ """
6
+
7
+ import asyncio
8
+ import base64
9
+ from concurrent.futures import ThreadPoolExecutor
10
+ from typing import Optional, Dict, Any, List
11
+ from loguru import logger
12
+ import trafilatura
13
+
14
+ class ScreenshotService:
15
+ """
16
+ Browser Service using DrissionPage.
17
+ """
18
+
19
+ def __init__(self, headless: bool = True, auto_start: bool = True):
20
+ self.headless = headless
21
+ self._manager = None
22
+ self._executor = ThreadPoolExecutor(max_workers=10)
23
+
24
+ if auto_start:
25
+ self._ensure_ready()
26
+
27
+ def _ensure_ready(self):
28
+ """Ensure shared browser is ready."""
29
+ from .manager import get_shared_browser_manager
30
+ self._manager = get_shared_browser_manager(headless=self.headless)
31
+
32
+ async def fetch_page(self, url: str, timeout: float = 20.0, include_screenshot: bool = True) -> Dict[str, Any]:
33
+ """
34
+ Fetch page content (and optionally screenshot).
35
+ Runs in a thread executor to avoid blocking the async loop.
36
+ """
37
+ loop = asyncio.get_running_loop()
38
+ return await loop.run_in_executor(
39
+ self._executor,
40
+ self._fetch_page_sync,
41
+ url,
42
+ timeout,
43
+ include_screenshot
44
+ )
45
+
46
+ def _fetch_page_sync(self, url: str, timeout: float, include_screenshot: bool) -> Dict[str, Any]:
47
+ """Synchronous fetch logic."""
48
+ if not url:
49
+ return {"content": "Error: missing url", "title": "Error", "url": ""}
50
+
51
+ tab = None
52
+ try:
53
+ self._ensure_ready()
54
+ page = self._manager.page
55
+ if not page:
56
+ return {"content": "Error: Browser not available", "title": "Error", "url": url}
57
+
58
+ # New Tab
59
+ tab = page.new_tab(url)
60
+
61
+ # Wait logic
62
+ is_search_page = any(s in url.lower() for s in ['search', 'bing.com', 'duckduckgo', 'google.com/search', 'searx'])
63
+ if is_search_page:
64
+ # Quick check for results
65
+ result_selectors = ['#results', '#b_results', '#search', '#links', '.result']
66
+ for selector in result_selectors:
67
+ if tab.ele(selector, timeout=1):
68
+ break
69
+ else:
70
+ # 1. Wait for document to settle (Fast Dynamic Wait)
71
+ try:
72
+ tab.wait.doc_loaded(timeout=5)
73
+ # Brief check for loading overlays (fast skip if none)
74
+ tab.run_js("""
75
+ (async () => {
76
+ const isVisible = (el) => !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length);
77
+ for (let i = 0; i < 15; i++) {
78
+ const indicators = Array.from(document.querySelectorAll('*')).filter(el => {
79
+ try {
80
+ const text = (el.textContent || '').toLowerCase();
81
+ const id = (el.id || '').toLowerCase();
82
+ const cls = (el.getAttribute('class') || '').toLowerCase();
83
+ return (text.includes('loading') || id.includes('loading') || cls.includes('loading')) && isVisible(el);
84
+ } catch(e) { return false; }
85
+ });
86
+ if (indicators.length === 0) break;
87
+ await new Promise(r => setTimeout(r, 100));
88
+ }
89
+ })()
90
+ """, as_expr=True)
91
+ except: pass
92
+
93
+ html = tab.html
94
+ title = tab.title
95
+ final_url = tab.url
96
+
97
+ raw_screenshot_b64 = None
98
+ if include_screenshot:
99
+ try:
100
+ # Scrollbar Hiding Best Effort
101
+ from .manager import SharedBrowserManager
102
+ SharedBrowserManager.hide_scrollbars(tab)
103
+
104
+ # Inject CSS
105
+ tab.run_js("""
106
+ const style = document.createElement('style');
107
+ style.textContent = `
108
+ ::-webkit-scrollbar { display: none !important; }
109
+ html, body { -ms-overflow-style: none !important; scrollbar-width: none !important; }
110
+ `;
111
+ document.head.appendChild(style);
112
+ document.documentElement.style.overflow = 'hidden';
113
+ document.body.style.overflow = 'hidden';
114
+ """)
115
+
116
+ raw_screenshot_b64 = tab.get_screenshot(as_base64='jpg', full_page=False)
117
+ except Exception as e:
118
+ logger.warning(f"ScreenshotService: Failed to capture screenshot: {e}")
119
+
120
+ # Extract content
121
+ content = trafilatura.extract(
122
+ html, include_links=True, include_images=True, include_comments=False,
123
+ include_tables=True, favor_precision=False, output_format="markdown"
124
+ ) or ""
125
+
126
+ # 2. Extract Images via Parallelized JS (Gallery)
127
+ images_b64 = []
128
+ try:
129
+ images_b64 = tab.run_js("""
130
+ (async () => {
131
+ const blocklist = ['logo', 'icon', 'avatar', 'ad', 'pixel', 'tracker', 'button', 'menu', 'nav'];
132
+ const candidates = Array.from(document.querySelectorAll('img'));
133
+ const validCandidates = candidates.filter(img => {
134
+ if (!img.src || img.src.startsWith('data:')) return false;
135
+ if (img.naturalWidth < 200 || img.naturalHeight < 150) return false;
136
+ const alt = (img.alt || '').toLowerCase();
137
+ const cls = (typeof img.className === 'string' ? img.className : '').toLowerCase();
138
+ const src = img.src.toLowerCase();
139
+ if (blocklist.some(b => alt.includes(b) || cls.includes(b) || src.includes(b))) return false;
140
+ return true;
141
+ }).slice(0, 10);
142
+
143
+ const fetchImage = async (url) => {
144
+ try {
145
+ const controller = new AbortController();
146
+ const id = setTimeout(() => controller.abort(), 4000);
147
+ const resp = await fetch(url, { signal: controller.signal });
148
+ clearTimeout(id);
149
+ const blob = await resp.blob();
150
+ return new Promise(resolve => {
151
+ const reader = new FileReader();
152
+ reader.onloadend = () => resolve(reader.result.split(',')[1]);
153
+ reader.onerror = () => resolve(null);
154
+ reader.readAsDataURL(blob);
155
+ });
156
+ } catch(e) { return null; }
157
+ };
158
+
159
+ const results = await Promise.all(validCandidates.map(img => fetchImage(img.src)));
160
+ return results.filter(b64 => !!b64);
161
+ })()
162
+ """, as_expr=True) or []
163
+
164
+ if images_b64:
165
+ logger.info(f"ScreenshotService: Extracted {len(images_b64)} images for {url}")
166
+
167
+ except Exception as e:
168
+ logger.warning(f"ScreenshotService: Image extraction failed: {e}")
169
+
170
+ return {
171
+ "content": content,
172
+ "html": html,
173
+ "title": title,
174
+ "url": final_url,
175
+ "raw_screenshot_b64": raw_screenshot_b64,
176
+ "images": images_b64
177
+ }
178
+
179
+ except Exception as e:
180
+ logger.error(f"ScreenshotService: Failed to fetch {url}: {e}")
181
+ return {"content": f"Error: fetch failed ({e})", "title": "Error", "url": url}
182
+ finally:
183
+ if tab:
184
+ try: tab.close()
185
+ except: pass
186
+
187
+ async def fetch_pages_batch(self, urls: List[str], timeout: float = 20.0, include_screenshot: bool = True) -> List[Dict[str, Any]]:
188
+ """Fetch multiple pages concurrently."""
189
+ if not urls: return []
190
+ logger.info(f"ScreenshotService: Batch fetching {len(urls)} URLs (screenshots={include_screenshot})")
191
+ tasks = [self.fetch_page(url, timeout, include_screenshot) for url in urls]
192
+ return await asyncio.gather(*tasks, return_exceptions=True)
193
+
194
+ async def screenshot_url(self, url: str, wait_load: bool = True, timeout: float = 15.0, full_page: bool = False, quality: int = 80) -> Optional[str]:
195
+ """Screenshot URL (Async wrapper for sync)."""
196
+ loop = asyncio.get_running_loop()
197
+ return await loop.run_in_executor(
198
+ self._executor,
199
+ self._screenshot_sync,
200
+ url, wait_load, timeout, full_page, quality
201
+ )
202
+
203
+ def _screenshot_sync(self, url: str, wait_load: bool, timeout: float, full_page: bool, quality: int) -> Optional[str]:
204
+ """Synchronous screenshot."""
205
+ if not url: return None
206
+ tab = None
207
+ try:
208
+ self._ensure_ready()
209
+ page = self._manager.page
210
+ if not page: return None
211
+
212
+ tab = page.new_tab(url)
213
+ try:
214
+ if wait_load:
215
+ tab.wait.load_complete(timeout=timeout)
216
+ else:
217
+ tab.wait.doc_loaded(timeout=timeout)
218
+ except: pass
219
+
220
+ # Wait for main element
221
+ if tab.ele("#main-container"):
222
+ pass
223
+
224
+ # Scrollbar Hiding
225
+ from .manager import SharedBrowserManager
226
+ SharedBrowserManager.hide_scrollbars(tab)
227
+ tab.run_js("""
228
+ const style = document.createElement('style');
229
+ style.textContent = `
230
+ ::-webkit-scrollbar { display: none !important; }
231
+ html, body { -ms-overflow-style: none !important; scrollbar-width: none !important; }
232
+ `;
233
+ document.head.appendChild(style);
234
+ document.documentElement.style.overflow = 'hidden';
235
+ document.body.style.overflow = 'hidden';
236
+ """)
237
+
238
+ ele = tab.ele("#main-container")
239
+ if ele:
240
+ return ele.get_screenshot(as_base64='jpg', quality=quality)
241
+ else:
242
+ return tab.get_screenshot(as_base64='jpg', full_page=full_page, quality=quality)
243
+
244
+ except Exception as e:
245
+ logger.error(f"ScreenshotService: Screenshot URL failed: {e}")
246
+ return None
247
+ finally:
248
+ if tab:
249
+ try: tab.close()
250
+ except: pass
251
+
252
+ async def close(self):
253
+ self._executor.shutdown(wait=False)
254
+ logger.info("ScreenshotService: Closed.")
255
+
256
+ async def close_async(self):
257
+ await self.close()
258
+
259
+ # Singleton
260
+ _screenshot_service: Optional[ScreenshotService] = None
261
+
262
+ def get_screenshot_service(headless: bool = True) -> ScreenshotService:
263
+ global _screenshot_service
264
+ if _screenshot_service is None:
265
+ _screenshot_service = ScreenshotService(headless=headless, auto_start=True)
266
+ return _screenshot_service
267
+
268
+ async def close_screenshot_service():
269
+ global _screenshot_service
270
+ if _screenshot_service:
271
+ await _screenshot_service.close()
272
+ _screenshot_service = None
273
+
274
+ def prestart_browser(headless: bool = True):
275
+ get_screenshot_service(headless=headless)
@@ -0,0 +1,24 @@
1
+ # Logs
2
+ logs
3
+ *.log
4
+ npm-debug.log*
5
+ yarn-debug.log*
6
+ yarn-error.log*
7
+ pnpm-debug.log*
8
+ lerna-debug.log*
9
+
10
+ node_modules
11
+ dist
12
+ dist-ssr
13
+ *.local
14
+
15
+ # Editor directories and files
16
+ .vscode/*
17
+ !.vscode/extensions.json
18
+ .idea
19
+ .DS_Store
20
+ *.suo
21
+ *.ntvs*
22
+ *.njsproj
23
+ *.sln
24
+ *.sw?
@@ -0,0 +1,5 @@
1
+ # Vue 3 + TypeScript + Vite
2
+
3
+ This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
4
+
5
+ Learn more about the recommended Project Setup and IDE Support in the [Vue Docs TypeScript Guide](https://vuejs.org/guide/typescript/overview.html#project-setup).
@@ -0,0 +1,16 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+
4
+ <head>
5
+ <meta charset="UTF-8" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Card Renderer</title>
8
+ <script>window.RENDER_DATA = {}</script>
9
+ </head>
10
+
11
+ <body>
12
+ <div id="app"></div>
13
+ <script type="module" src="/src/main.ts"></script>
14
+ </body>
15
+
16
+ </html>