hermes-plugin-tinyfish 0.2.4__tar.gz → 0.3.1__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.
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/CHANGELOG.md +26 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/PKG-INFO +59 -5
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/README.md +57 -3
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/hermes_plugin_tinyfish/__init__.py +13 -3
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/hermes_plugin_tinyfish/config.py +12 -0
- hermes_plugin_tinyfish-0.3.1/hermes_plugin_tinyfish/routing_context.py +75 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/hermes_plugin_tinyfish/setup_cli.py +34 -8
- hermes_plugin_tinyfish-0.3.1/hermes_plugin_tinyfish/update_check.py +290 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/plugin.yaml +2 -2
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/pyproject.toml +2 -2
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/tests/test_register.py +6 -1
- hermes_plugin_tinyfish-0.3.1/tests/test_routing_context.py +136 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/tests/test_setup_cli.py +34 -3
- hermes_plugin_tinyfish-0.3.1/tests/test_update_check.py +336 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/.agents/skills/skill-creator/scripts/__init__.py +0 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/.agents/skills/skill-creator/scripts/aggregate_benchmark.py +0 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/.agents/skills/skill-creator/scripts/generate_report.py +0 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/.agents/skills/skill-creator/scripts/improve_description.py +0 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/.agents/skills/skill-creator/scripts/package_skill.py +0 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/.agents/skills/skill-creator/scripts/quick_validate.py +0 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/.agents/skills/skill-creator/scripts/run_eval.py +0 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/.agents/skills/skill-creator/scripts/run_loop.py +0 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/.agents/skills/skill-creator/scripts/utils.py +0 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/.gitignore +0 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/CODE_OF_CONDUCT.md +0 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/CONTRIBUTING.md +0 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/LICENSE +0 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/SECURITY.md +0 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/__init__.py +0 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/hermes_plugin_tinyfish/browser_provider.py +0 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/hermes_plugin_tinyfish/credit_policy.py +0 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/hermes_plugin_tinyfish/health.py +0 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/hermes_plugin_tinyfish/normalize.py +0 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/hermes_plugin_tinyfish/provider.py +0 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/hermes_plugin_tinyfish/rest_client.py +0 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/scripts/prepare_release.py +0 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/scripts/validate_release.py +0 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/tests/conftest.py +0 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/tests/test_browser_provider.py +0 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/tests/test_credit_policy.py +0 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/tests/test_health.py +0 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/tests/test_live.py +0 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/tests/test_normalize.py +0 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/tests/test_prepare_release.py +0 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/tests/test_provider.py +0 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/tests/test_rest_client.py +0 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/tests/test_validate_release.py +0 -0
- {hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/tests/test_version.py +0 -0
|
@@ -7,6 +7,32 @@ and this project uses semantic versioning.
|
|
|
7
7
|
|
|
8
8
|
## Unreleased
|
|
9
9
|
|
|
10
|
+
## [0.3.1] - 2026-08-04
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Added the required Hermes plugin enable step to the Python package install
|
|
15
|
+
instructions before running TinyFish setup.
|
|
16
|
+
|
|
17
|
+
## [0.3.0] - 2026-08-04
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- Added marked, once-per-active-context guidance so Hermes can infer when
|
|
22
|
+
ordinary web requests should use generic provider tools and when
|
|
23
|
+
TinyFish-specific controls require native MCP Search or Fetch without
|
|
24
|
+
accumulating one new copy on every turn.
|
|
25
|
+
- Added nonblocking, source-aware update checks with a 24-hour profile-local
|
|
26
|
+
cache, one-time conversational notices, install-appropriate update guidance,
|
|
27
|
+
opt-outs, and non-networked diagnostics.
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
|
|
31
|
+
- Expanded diagnostics to schema version 3 with routing state, installed and
|
|
32
|
+
cached-latest versions, release channel, and update availability.
|
|
33
|
+
- Clarified that persistent Search/Fetch settings are REST fallback defaults,
|
|
34
|
+
not required parity configuration for plain-language requests.
|
|
35
|
+
|
|
10
36
|
## [0.2.4] - 2026-07-22
|
|
11
37
|
|
|
12
38
|
### Fixed
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hermes-plugin-tinyfish
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: TinyFish Search, Fetch, and optional Browser
|
|
3
|
+
Version: 0.3.1
|
|
4
|
+
Summary: TinyFish Search, Fetch, plain-language routing, update awareness, and optional Browser for Hermes Agent
|
|
5
5
|
Project-URL: Homepage, https://github.com/gabeosx/hermes-plugin-tinyfish
|
|
6
6
|
Project-URL: Documentation, https://github.com/gabeosx/hermes-plugin-tinyfish#readme
|
|
7
7
|
Project-URL: Issues, https://github.com/gabeosx/hermes-plugin-tinyfish/issues
|
|
@@ -38,8 +38,8 @@ Description-Content-Type: text/markdown
|
|
|
38
38
|
[](LICENSE)
|
|
39
39
|
[](SECURITY.md)
|
|
40
40
|
|
|
41
|
-
TinyFish Search and Fetch providers
|
|
42
|
-
|
|
41
|
+
TinyFish Search and Fetch providers with plain-language tool routing, update
|
|
42
|
+
awareness, and optional TinyFish Browser infrastructure for
|
|
43
43
|
[Hermes Agent](https://hermes-agent.nousresearch.com/docs/).
|
|
44
44
|
|
|
45
45
|
Hermes remains the agent: it plans, chooses tools, and controls browser
|
|
@@ -67,6 +67,7 @@ Python package install:
|
|
|
67
67
|
|
|
68
68
|
```bash
|
|
69
69
|
pip install hermes-plugin-tinyfish
|
|
70
|
+
hermes plugins enable web-tinyfish
|
|
70
71
|
hermes tinyfish setup
|
|
71
72
|
```
|
|
72
73
|
|
|
@@ -88,6 +89,20 @@ hermes tinyfish status
|
|
|
88
89
|
hermes tinyfish doctor
|
|
89
90
|
```
|
|
90
91
|
|
|
92
|
+
The plugin also performs a nonblocking update check when it loads. Git installs
|
|
93
|
+
are compared with the latest stable GitHub Release; Python package installs are
|
|
94
|
+
compared with the latest stable PyPI version. Results are cached for 24 hours
|
|
95
|
+
at `$HERMES_HOME/cache/web-tinyfish-update.json` in the active profile. If an
|
|
96
|
+
update is available, Hermes mentions it once on the first conversational turn
|
|
97
|
+
where the cached or background result is available. The plugin never installs
|
|
98
|
+
the update itself.
|
|
99
|
+
|
|
100
|
+
The check sends only the installed plugin version in its HTTP User-Agent. It
|
|
101
|
+
does not send Hermes configuration, TinyFish credentials, searches, fetched
|
|
102
|
+
URLs, or conversation content. Network and cache failures are silent. Disable
|
|
103
|
+
the check with `tinyfish.update_check: false` or the conventional
|
|
104
|
+
`NO_UPDATE_NOTIFIER=1` environment variable.
|
|
105
|
+
|
|
91
106
|
## Safe Default Setup
|
|
92
107
|
|
|
93
108
|
`hermes tinyfish setup` configures TinyFish for Hermes `web_search` and
|
|
@@ -108,6 +123,8 @@ web:
|
|
|
108
123
|
extract_backend: tinyfish
|
|
109
124
|
|
|
110
125
|
tinyfish:
|
|
126
|
+
routing_context: true
|
|
127
|
+
update_check: true
|
|
111
128
|
credit_policy:
|
|
112
129
|
browser: deny
|
|
113
130
|
```
|
|
@@ -165,6 +182,35 @@ For browser authorization from a headless or remote terminal:
|
|
|
165
182
|
- Treat the MCP-only doctor as authoritative. Some Hermes versions can print
|
|
166
183
|
an authentication failure while still returning shell status 0.
|
|
167
184
|
|
|
185
|
+
## Automatic Tool Routing
|
|
186
|
+
|
|
187
|
+
Talk to Hermes normally; you do not need to choose “the plugin” or “MCP.” When
|
|
188
|
+
an active context does not already contain the current routing version, the
|
|
189
|
+
plugin adds a short routing note that helps Hermes choose the narrowest tool
|
|
190
|
+
surface:
|
|
191
|
+
|
|
192
|
+
- “Find recent TinyFish documentation” uses Hermes `web_search`.
|
|
193
|
+
- “Read this page and summarize it” uses Hermes `web_extract`.
|
|
194
|
+
- “Search only these domains, in English, after this date, and return page 2”
|
|
195
|
+
uses TinyFish MCP's native `search` tool because the generic search schema
|
|
196
|
+
cannot express all of those controls.
|
|
197
|
+
- “Fetch these URLs using this selector, include image links, and use this
|
|
198
|
+
cache TTL” uses TinyFish MCP's native `fetch_content` tool.
|
|
199
|
+
|
|
200
|
+
Hermes keeps the visible stored user message clean but persists the exact
|
|
201
|
+
API-bound message in an `api_content` sidecar for prompt-cache replay. The note
|
|
202
|
+
contains a versioned marker, so the hook does not add another copy while that
|
|
203
|
+
version remains in the active context. If compression removes the marked
|
|
204
|
+
message, the hook adds one fresh copy. Multiple tool calls inside a turn do not
|
|
205
|
+
run the hook again. Hermes still sees the real tool schemas and makes the final
|
|
206
|
+
choice; the plugin does not keyword-match the prompt or force a tool.
|
|
207
|
+
|
|
208
|
+
Routing guidance defaults on only when the plugin-managed TinyFish MCP server
|
|
209
|
+
is configured. Set `tinyfish.routing_context: false` to disable the guidance
|
|
210
|
+
without disabling the provider. This prevents new injections; a copy already
|
|
211
|
+
present in the active API context may remain until that context is reset or
|
|
212
|
+
compressed.
|
|
213
|
+
|
|
168
214
|
## Search and Fetch Options
|
|
169
215
|
|
|
170
216
|
Optional REST fallback defaults can be configured in `config.yaml`:
|
|
@@ -185,7 +231,10 @@ tinyfish:
|
|
|
185
231
|
```
|
|
186
232
|
|
|
187
233
|
MCP remains the preferred path when configured. These options apply only to
|
|
188
|
-
REST fallback calls.
|
|
234
|
+
REST fallback calls. They are fixed fallback defaults, not required settings
|
|
235
|
+
for ordinary requests and not a substitute for plain-language, per-request MCP
|
|
236
|
+
controls. The plugin does not add persistent include-domain, exclude-selector,
|
|
237
|
+
or similar parity settings.
|
|
189
238
|
|
|
190
239
|
## Optional Browser Provider
|
|
191
240
|
|
|
@@ -243,6 +292,9 @@ hermes tinyfish doctor --live-paid
|
|
|
243
292
|
|
|
244
293
|
- `status` is non-secret and reports ignored `0.2.x` policy keys under
|
|
245
294
|
`retired_credit_policy_keys`.
|
|
295
|
+
- Status also reports whether routing and update checks are enabled, whether
|
|
296
|
+
routing is active, the installed version, the cached latest version, and
|
|
297
|
+
cached update availability. Reading status never performs an update request.
|
|
246
298
|
- `mcp_token_cached` reports only whether Hermes's expected cache file is
|
|
247
299
|
present. It does not mean the access or refresh token is valid.
|
|
248
300
|
- `/tinyfish-status` shows non-networked status inside CLI or gateway sessions;
|
|
@@ -263,6 +315,8 @@ This plugin uses public Hermes extension points:
|
|
|
263
315
|
- `ctx.register_web_search_provider(...)`
|
|
264
316
|
- `ctx.register_browser_provider(...)`
|
|
265
317
|
- `ctx.register_hook("pre_tool_call", ...)` for Browser credit policy
|
|
318
|
+
- `ctx.register_hook("pre_llm_call", ...)` for context-aware tool routing and
|
|
319
|
+
update notices
|
|
266
320
|
- `ctx.register_cli_command(...)`
|
|
267
321
|
- `ctx.register_command(...)` for `/tinyfish-status`
|
|
268
322
|
- `ctx.dispatch_tool(...)` for registered TinyFish MCP calls
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
[](SECURITY.md)
|
|
8
8
|
|
|
9
|
-
TinyFish Search and Fetch providers
|
|
10
|
-
|
|
9
|
+
TinyFish Search and Fetch providers with plain-language tool routing, update
|
|
10
|
+
awareness, and optional TinyFish Browser infrastructure for
|
|
11
11
|
[Hermes Agent](https://hermes-agent.nousresearch.com/docs/).
|
|
12
12
|
|
|
13
13
|
Hermes remains the agent: it plans, chooses tools, and controls browser
|
|
@@ -35,6 +35,7 @@ Python package install:
|
|
|
35
35
|
|
|
36
36
|
```bash
|
|
37
37
|
pip install hermes-plugin-tinyfish
|
|
38
|
+
hermes plugins enable web-tinyfish
|
|
38
39
|
hermes tinyfish setup
|
|
39
40
|
```
|
|
40
41
|
|
|
@@ -56,6 +57,20 @@ hermes tinyfish status
|
|
|
56
57
|
hermes tinyfish doctor
|
|
57
58
|
```
|
|
58
59
|
|
|
60
|
+
The plugin also performs a nonblocking update check when it loads. Git installs
|
|
61
|
+
are compared with the latest stable GitHub Release; Python package installs are
|
|
62
|
+
compared with the latest stable PyPI version. Results are cached for 24 hours
|
|
63
|
+
at `$HERMES_HOME/cache/web-tinyfish-update.json` in the active profile. If an
|
|
64
|
+
update is available, Hermes mentions it once on the first conversational turn
|
|
65
|
+
where the cached or background result is available. The plugin never installs
|
|
66
|
+
the update itself.
|
|
67
|
+
|
|
68
|
+
The check sends only the installed plugin version in its HTTP User-Agent. It
|
|
69
|
+
does not send Hermes configuration, TinyFish credentials, searches, fetched
|
|
70
|
+
URLs, or conversation content. Network and cache failures are silent. Disable
|
|
71
|
+
the check with `tinyfish.update_check: false` or the conventional
|
|
72
|
+
`NO_UPDATE_NOTIFIER=1` environment variable.
|
|
73
|
+
|
|
59
74
|
## Safe Default Setup
|
|
60
75
|
|
|
61
76
|
`hermes tinyfish setup` configures TinyFish for Hermes `web_search` and
|
|
@@ -76,6 +91,8 @@ web:
|
|
|
76
91
|
extract_backend: tinyfish
|
|
77
92
|
|
|
78
93
|
tinyfish:
|
|
94
|
+
routing_context: true
|
|
95
|
+
update_check: true
|
|
79
96
|
credit_policy:
|
|
80
97
|
browser: deny
|
|
81
98
|
```
|
|
@@ -133,6 +150,35 @@ For browser authorization from a headless or remote terminal:
|
|
|
133
150
|
- Treat the MCP-only doctor as authoritative. Some Hermes versions can print
|
|
134
151
|
an authentication failure while still returning shell status 0.
|
|
135
152
|
|
|
153
|
+
## Automatic Tool Routing
|
|
154
|
+
|
|
155
|
+
Talk to Hermes normally; you do not need to choose “the plugin” or “MCP.” When
|
|
156
|
+
an active context does not already contain the current routing version, the
|
|
157
|
+
plugin adds a short routing note that helps Hermes choose the narrowest tool
|
|
158
|
+
surface:
|
|
159
|
+
|
|
160
|
+
- “Find recent TinyFish documentation” uses Hermes `web_search`.
|
|
161
|
+
- “Read this page and summarize it” uses Hermes `web_extract`.
|
|
162
|
+
- “Search only these domains, in English, after this date, and return page 2”
|
|
163
|
+
uses TinyFish MCP's native `search` tool because the generic search schema
|
|
164
|
+
cannot express all of those controls.
|
|
165
|
+
- “Fetch these URLs using this selector, include image links, and use this
|
|
166
|
+
cache TTL” uses TinyFish MCP's native `fetch_content` tool.
|
|
167
|
+
|
|
168
|
+
Hermes keeps the visible stored user message clean but persists the exact
|
|
169
|
+
API-bound message in an `api_content` sidecar for prompt-cache replay. The note
|
|
170
|
+
contains a versioned marker, so the hook does not add another copy while that
|
|
171
|
+
version remains in the active context. If compression removes the marked
|
|
172
|
+
message, the hook adds one fresh copy. Multiple tool calls inside a turn do not
|
|
173
|
+
run the hook again. Hermes still sees the real tool schemas and makes the final
|
|
174
|
+
choice; the plugin does not keyword-match the prompt or force a tool.
|
|
175
|
+
|
|
176
|
+
Routing guidance defaults on only when the plugin-managed TinyFish MCP server
|
|
177
|
+
is configured. Set `tinyfish.routing_context: false` to disable the guidance
|
|
178
|
+
without disabling the provider. This prevents new injections; a copy already
|
|
179
|
+
present in the active API context may remain until that context is reset or
|
|
180
|
+
compressed.
|
|
181
|
+
|
|
136
182
|
## Search and Fetch Options
|
|
137
183
|
|
|
138
184
|
Optional REST fallback defaults can be configured in `config.yaml`:
|
|
@@ -153,7 +199,10 @@ tinyfish:
|
|
|
153
199
|
```
|
|
154
200
|
|
|
155
201
|
MCP remains the preferred path when configured. These options apply only to
|
|
156
|
-
REST fallback calls.
|
|
202
|
+
REST fallback calls. They are fixed fallback defaults, not required settings
|
|
203
|
+
for ordinary requests and not a substitute for plain-language, per-request MCP
|
|
204
|
+
controls. The plugin does not add persistent include-domain, exclude-selector,
|
|
205
|
+
or similar parity settings.
|
|
157
206
|
|
|
158
207
|
## Optional Browser Provider
|
|
159
208
|
|
|
@@ -211,6 +260,9 @@ hermes tinyfish doctor --live-paid
|
|
|
211
260
|
|
|
212
261
|
- `status` is non-secret and reports ignored `0.2.x` policy keys under
|
|
213
262
|
`retired_credit_policy_keys`.
|
|
263
|
+
- Status also reports whether routing and update checks are enabled, whether
|
|
264
|
+
routing is active, the installed version, the cached latest version, and
|
|
265
|
+
cached update availability. Reading status never performs an update request.
|
|
214
266
|
- `mcp_token_cached` reports only whether Hermes's expected cache file is
|
|
215
267
|
present. It does not mean the access or refresh token is valid.
|
|
216
268
|
- `/tinyfish-status` shows non-networked status inside CLI or gateway sessions;
|
|
@@ -231,6 +283,8 @@ This plugin uses public Hermes extension points:
|
|
|
231
283
|
- `ctx.register_web_search_provider(...)`
|
|
232
284
|
- `ctx.register_browser_provider(...)`
|
|
233
285
|
- `ctx.register_hook("pre_tool_call", ...)` for Browser credit policy
|
|
286
|
+
- `ctx.register_hook("pre_llm_call", ...)` for context-aware tool routing and
|
|
287
|
+
update notices
|
|
234
288
|
- `ctx.register_cli_command(...)`
|
|
235
289
|
- `ctx.register_command(...)` for `/tinyfish-status`
|
|
236
290
|
- `ctx.dispatch_tool(...)` for registered TinyFish MCP calls
|
{hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/hermes_plugin_tinyfish/__init__.py
RENAMED
|
@@ -39,22 +39,28 @@ __version__ = _resolve_version()
|
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
def register(ctx: Any) -> None:
|
|
42
|
-
"""Register TinyFish providers,
|
|
42
|
+
"""Register TinyFish providers, lifecycle hooks, and CLI commands."""
|
|
43
43
|
|
|
44
44
|
from .browser_provider import TinyFishBrowserProvider
|
|
45
45
|
from .credit_policy import pre_tool_call_policy
|
|
46
46
|
from .provider import TinyFishWebSearchProvider
|
|
47
|
+
from .routing_context import TinyFishTurnContext
|
|
47
48
|
from .setup_cli import dispatch_tinyfish_cli, setup_tinyfish_cli, tinyfish_status_command
|
|
49
|
+
from .update_check import UpdateChecker, resolve_install_info
|
|
48
50
|
|
|
49
51
|
provider = TinyFishWebSearchProvider(dispatch_tool=getattr(ctx, "dispatch_tool", None))
|
|
52
|
+
update_checker = UpdateChecker(resolve_install_info(ctx, __version__))
|
|
53
|
+
turn_context = TinyFishTurnContext(update_checker)
|
|
50
54
|
ctx.register_web_search_provider(provider)
|
|
51
55
|
if hasattr(ctx, "register_browser_provider"):
|
|
52
56
|
ctx.register_browser_provider(TinyFishBrowserProvider())
|
|
53
57
|
if hasattr(ctx, "register_hook"):
|
|
54
58
|
ctx.register_hook("pre_tool_call", pre_tool_call_policy)
|
|
59
|
+
ctx.register_hook("pre_llm_call", turn_context)
|
|
60
|
+
update_checker.start()
|
|
55
61
|
|
|
56
62
|
def _dispatch_cli(args: Any) -> int:
|
|
57
|
-
exit_code = dispatch_tinyfish_cli(args, provider=provider)
|
|
63
|
+
exit_code = dispatch_tinyfish_cli(args, provider=provider, update_checker=update_checker)
|
|
58
64
|
if exit_code:
|
|
59
65
|
raise SystemExit(exit_code)
|
|
60
66
|
return 0
|
|
@@ -69,7 +75,11 @@ def register(ctx: Any) -> None:
|
|
|
69
75
|
if hasattr(ctx, "register_command"):
|
|
70
76
|
ctx.register_command(
|
|
71
77
|
name="tinyfish-status",
|
|
72
|
-
handler=lambda raw_args: tinyfish_status_command(
|
|
78
|
+
handler=lambda raw_args: tinyfish_status_command(
|
|
79
|
+
raw_args,
|
|
80
|
+
provider=provider,
|
|
81
|
+
update_checker=update_checker,
|
|
82
|
+
),
|
|
73
83
|
description="Show TinyFish provider status; pass 'live' to test Search and Fetch.",
|
|
74
84
|
args_hint="[live]",
|
|
75
85
|
)
|
{hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/hermes_plugin_tinyfish/config.py
RENAMED
|
@@ -4,6 +4,8 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from typing import Any, Literal
|
|
6
6
|
|
|
7
|
+
TINYFISH_MCP_URL = "https://agent.tinyfish.ai/mcp"
|
|
8
|
+
|
|
7
9
|
CreditFeature = Literal["browser"]
|
|
8
10
|
CreditPolicy = Literal["deny", "request", "allow"]
|
|
9
11
|
|
|
@@ -58,6 +60,16 @@ def tinyfish_config(config: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
|
58
60
|
return section if isinstance(section, dict) else {}
|
|
59
61
|
|
|
60
62
|
|
|
63
|
+
def routing_context_enabled(config: dict[str, Any] | None = None) -> bool:
|
|
64
|
+
value = _bool_option(tinyfish_config(config).get("routing_context"))
|
|
65
|
+
return True if value is None else value
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def update_check_enabled(config: dict[str, Any] | None = None) -> bool:
|
|
69
|
+
value = _bool_option(tinyfish_config(config).get("update_check"))
|
|
70
|
+
return True if value is None else value
|
|
71
|
+
|
|
72
|
+
|
|
61
73
|
def credit_policy(feature: CreditFeature | str, config: dict[str, Any] | None = None) -> CreditPolicy:
|
|
62
74
|
normalized = normalize_feature(str(feature))
|
|
63
75
|
section = tinyfish_config(config)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""Context-bounded TinyFish tool-routing guidance for Hermes turns."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Mapping, Sequence
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from .config import TINYFISH_MCP_URL, load_config, routing_context_enabled
|
|
9
|
+
from .update_check import UpdateChecker
|
|
10
|
+
|
|
11
|
+
ROUTING_CONTEXT_MARKER = '<tinyfish-routing-context version="1">'
|
|
12
|
+
ROUTING_GUIDANCE = f"""{ROUTING_CONTEXT_MARKER}
|
|
13
|
+
TinyFish tool-routing guidance:
|
|
14
|
+
- For ordinary web discovery or reading a page, use Hermes `web_search` or `web_extract`; the TinyFish provider already keeps those calls MCP-first.
|
|
15
|
+
- When the request needs TinyFish-specific controls that the generic schemas cannot express—domain/date/language/location/purpose/pagination filters, selectors, output formats, link or image extraction, cache TTL, or per-URL timeouts—use the native `search` or `fetch_content` tool registered by the `tinyfish` MCP server. Use the exact names Hermes exposes, commonly `mcp__tinyfish__search` and `mcp__tinyfish__fetch_content`.
|
|
16
|
+
- Infer the choice from the user's plain language. Do not ask them to choose MCP versus the plugin, and do not persist per-request controls as configuration."""
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def tinyfish_mcp_configured(config: dict[str, Any]) -> bool:
|
|
20
|
+
servers = config.get("mcp_servers") or {}
|
|
21
|
+
if not isinstance(servers, dict):
|
|
22
|
+
return False
|
|
23
|
+
tinyfish = servers.get("tinyfish") or {}
|
|
24
|
+
return bool(
|
|
25
|
+
isinstance(tinyfish, dict)
|
|
26
|
+
and tinyfish.get("url") == TINYFISH_MCP_URL
|
|
27
|
+
and tinyfish.get("auth") == "oauth"
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _contains_routing_marker(value: object) -> bool:
|
|
32
|
+
if isinstance(value, str):
|
|
33
|
+
return ROUTING_CONTEXT_MARKER in value
|
|
34
|
+
if isinstance(value, Mapping):
|
|
35
|
+
return any(_contains_routing_marker(item) for item in value.values())
|
|
36
|
+
if isinstance(value, Sequence) and not isinstance(value, (str, bytes, bytearray)):
|
|
37
|
+
return any(_contains_routing_marker(item) for item in value)
|
|
38
|
+
return False
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def routing_guidance_present(conversation_history: object) -> bool:
|
|
42
|
+
"""Return whether Hermes already carries this routing version in context."""
|
|
43
|
+
|
|
44
|
+
if not isinstance(conversation_history, Sequence) or isinstance(
|
|
45
|
+
conversation_history, (str, bytes, bytearray)
|
|
46
|
+
):
|
|
47
|
+
return False
|
|
48
|
+
for message in conversation_history:
|
|
49
|
+
if not isinstance(message, Mapping):
|
|
50
|
+
continue
|
|
51
|
+
if _contains_routing_marker(message.get("api_content")):
|
|
52
|
+
return True
|
|
53
|
+
if _contains_routing_marker(message.get("content")):
|
|
54
|
+
return True
|
|
55
|
+
return False
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class TinyFishTurnContext:
|
|
59
|
+
"""Compose once-per-active-context routing with a maintenance notice."""
|
|
60
|
+
|
|
61
|
+
def __init__(self, update_checker: UpdateChecker) -> None:
|
|
62
|
+
self.update_checker = update_checker
|
|
63
|
+
|
|
64
|
+
def __call__(self, **kwargs: Any) -> dict[str, str] | None:
|
|
65
|
+
config = load_config()
|
|
66
|
+
context: list[str] = []
|
|
67
|
+
routing_needed = not routing_guidance_present(kwargs.get("conversation_history"))
|
|
68
|
+
if routing_context_enabled(config) and tinyfish_mcp_configured(config) and routing_needed:
|
|
69
|
+
context.append(ROUTING_GUIDANCE)
|
|
70
|
+
update_notice = self.update_checker.notice_context()
|
|
71
|
+
if update_notice:
|
|
72
|
+
context.append(update_notice)
|
|
73
|
+
if not context:
|
|
74
|
+
return None
|
|
75
|
+
return {"context": "\n\n".join(context)}
|
{hermes_plugin_tinyfish-0.2.4 → hermes_plugin_tinyfish-0.3.1}/hermes_plugin_tinyfish/setup_cli.py
RENAMED
|
@@ -14,11 +14,13 @@ from .config import (
|
|
|
14
14
|
CREDIT_FEATURES,
|
|
15
15
|
CREDIT_POLICIES,
|
|
16
16
|
RETIRED_CREDIT_POLICY_KEYS,
|
|
17
|
+
TINYFISH_MCP_URL,
|
|
17
18
|
credit_policy_summary,
|
|
18
19
|
normalize_feature,
|
|
19
20
|
normalize_policy,
|
|
20
21
|
reset_credit_policies,
|
|
21
22
|
retired_credit_policy_keys,
|
|
23
|
+
routing_context_enabled,
|
|
22
24
|
set_credit_policy,
|
|
23
25
|
)
|
|
24
26
|
from .credit_policy import INDEPENDENT_NOTICE, PRICING_NOTICE, request_credit_approval
|
|
@@ -28,8 +30,9 @@ from .provider import (
|
|
|
28
30
|
MCP_SEARCH_TOOLS,
|
|
29
31
|
TinyFishWebSearchProvider,
|
|
30
32
|
)
|
|
33
|
+
from .routing_context import tinyfish_mcp_configured
|
|
34
|
+
from .update_check import InstallInfo, UpdateChecker
|
|
31
35
|
|
|
32
|
-
TINYFISH_MCP_URL = "https://agent.tinyfish.ai/mcp"
|
|
33
36
|
MCP_LOGIN_COMMAND = ("hermes", "mcp", "login", "tinyfish")
|
|
34
37
|
|
|
35
38
|
|
|
@@ -90,14 +93,15 @@ def dispatch_tinyfish_cli(
|
|
|
90
93
|
args: argparse.Namespace,
|
|
91
94
|
*,
|
|
92
95
|
provider: TinyFishWebSearchProvider | None = None,
|
|
96
|
+
update_checker: UpdateChecker | None = None,
|
|
93
97
|
) -> int:
|
|
94
98
|
command = getattr(args, "tinyfish_command", None) or "status"
|
|
95
99
|
if command == "setup":
|
|
96
|
-
return cmd_setup(args, provider=provider)
|
|
100
|
+
return cmd_setup(args, provider=provider, update_checker=update_checker)
|
|
97
101
|
if command == "doctor":
|
|
98
|
-
return cmd_doctor(args, provider=provider)
|
|
102
|
+
return cmd_doctor(args, provider=provider, update_checker=update_checker)
|
|
99
103
|
if command == "status":
|
|
100
|
-
return cmd_status(args, provider=provider)
|
|
104
|
+
return cmd_status(args, provider=provider, update_checker=update_checker)
|
|
101
105
|
if command == "reauth":
|
|
102
106
|
return cmd_reauth(args)
|
|
103
107
|
if command == "credits":
|
|
@@ -207,6 +211,7 @@ def cmd_setup(
|
|
|
207
211
|
args: argparse.Namespace,
|
|
208
212
|
*,
|
|
209
213
|
provider: TinyFishWebSearchProvider | None = None,
|
|
214
|
+
update_checker: UpdateChecker | None = None,
|
|
210
215
|
) -> int:
|
|
211
216
|
config = _load_config()
|
|
212
217
|
|
|
@@ -254,7 +259,7 @@ def cmd_setup(
|
|
|
254
259
|
|
|
255
260
|
if getattr(args, "live", False):
|
|
256
261
|
doctor_args = argparse.Namespace(json=False, live=True, live_paid=False, transport="auto")
|
|
257
|
-
return cmd_doctor(doctor_args, provider=provider)
|
|
262
|
+
return cmd_doctor(doctor_args, provider=provider, update_checker=update_checker)
|
|
258
263
|
|
|
259
264
|
print("Run `hermes tinyfish doctor --live` to verify the setup.")
|
|
260
265
|
browser_policy = credit_policy_summary(config)["browser"]
|
|
@@ -346,6 +351,7 @@ def collect_status(
|
|
|
346
351
|
live: bool = False,
|
|
347
352
|
transport: Transport = "auto",
|
|
348
353
|
provider: TinyFishWebSearchProvider | None = None,
|
|
354
|
+
update_checker: UpdateChecker | None = None,
|
|
349
355
|
) -> dict[str, Any]:
|
|
350
356
|
config = _load_config()
|
|
351
357
|
mcp_cfg = (config.get("mcp_servers") or {}).get("tinyfish") or {}
|
|
@@ -358,9 +364,17 @@ def collect_status(
|
|
|
358
364
|
api_key_configured = bool(_get_env("TINYFISH_API_KEY"))
|
|
359
365
|
provider = provider or TinyFishWebSearchProvider()
|
|
360
366
|
health = _health_status(provider)
|
|
367
|
+
if update_checker is None:
|
|
368
|
+
from . import __version__
|
|
369
|
+
|
|
370
|
+
update_checker = UpdateChecker(
|
|
371
|
+
InstallInfo(current_version=__version__, channel="github", update_command=None),
|
|
372
|
+
home=_hermes_home(),
|
|
373
|
+
)
|
|
374
|
+
update_status = update_checker.status(config)
|
|
361
375
|
|
|
362
376
|
checks: dict[str, Any] = {
|
|
363
|
-
"diagnostics_schema_version":
|
|
377
|
+
"diagnostics_schema_version": 3,
|
|
364
378
|
"plugin_loaded": True,
|
|
365
379
|
"provider_available": provider.is_available(),
|
|
366
380
|
"mcp_configured": bool(mcp_cfg.get("url") == TINYFISH_MCP_URL and mcp_cfg.get("auth") == "oauth"),
|
|
@@ -371,6 +385,8 @@ def collect_status(
|
|
|
371
385
|
"api_key_fallback_configured": api_key_configured,
|
|
372
386
|
"credit_policy": credit_policy_summary(config),
|
|
373
387
|
"retired_credit_policy_keys": retired_credit_policy_keys(config),
|
|
388
|
+
"routing_context_enabled": routing_context_enabled(config),
|
|
389
|
+
"routing_context_active": bool(routing_context_enabled(config) and tinyfish_mcp_configured(config)),
|
|
374
390
|
"independent_plugin_notice": INDEPENDENT_NOTICE,
|
|
375
391
|
"pricing_notice": PRICING_NOTICE,
|
|
376
392
|
"web_search_backend": web_cfg.get("search_backend") or web_cfg.get("backend") or "",
|
|
@@ -378,6 +394,7 @@ def collect_status(
|
|
|
378
394
|
"browser_cloud_provider": (config.get("browser") or {}).get("cloud_provider", "")
|
|
379
395
|
if isinstance(config.get("browser") or {}, dict)
|
|
380
396
|
else "",
|
|
397
|
+
**update_status,
|
|
381
398
|
**health,
|
|
382
399
|
}
|
|
383
400
|
|
|
@@ -483,8 +500,9 @@ def cmd_status(
|
|
|
483
500
|
args: argparse.Namespace,
|
|
484
501
|
*,
|
|
485
502
|
provider: TinyFishWebSearchProvider | None = None,
|
|
503
|
+
update_checker: UpdateChecker | None = None,
|
|
486
504
|
) -> int:
|
|
487
|
-
status = collect_status(live=False, provider=provider)
|
|
505
|
+
status = collect_status(live=False, provider=provider, update_checker=update_checker)
|
|
488
506
|
if getattr(args, "json", False):
|
|
489
507
|
print(json.dumps(status, indent=2, sort_keys=True))
|
|
490
508
|
else:
|
|
@@ -496,6 +514,7 @@ def cmd_doctor(
|
|
|
496
514
|
args: argparse.Namespace,
|
|
497
515
|
*,
|
|
498
516
|
provider: TinyFishWebSearchProvider | None = None,
|
|
517
|
+
update_checker: UpdateChecker | None = None,
|
|
499
518
|
) -> int:
|
|
500
519
|
live = bool(getattr(args, "live", False))
|
|
501
520
|
transport = str(getattr(args, "transport", "auto") or "auto")
|
|
@@ -510,6 +529,7 @@ def cmd_doctor(
|
|
|
510
529
|
live=live,
|
|
511
530
|
transport=cast(Transport, transport),
|
|
512
531
|
provider=provider,
|
|
532
|
+
update_checker=update_checker,
|
|
513
533
|
)
|
|
514
534
|
if getattr(args, "live_paid", False):
|
|
515
535
|
paid_ok = _run_live_paid_checks(status)
|
|
@@ -566,13 +586,19 @@ def tinyfish_status_command(
|
|
|
566
586
|
raw_args: str,
|
|
567
587
|
*,
|
|
568
588
|
provider: TinyFishWebSearchProvider,
|
|
589
|
+
update_checker: UpdateChecker | None = None,
|
|
569
590
|
) -> str:
|
|
570
591
|
"""Serve the in-session ``/tinyfish-status`` command."""
|
|
571
592
|
|
|
572
593
|
option = (raw_args or "").strip().lower()
|
|
573
594
|
if option not in {"", "live"}:
|
|
574
595
|
return "Usage: /tinyfish-status [live]"
|
|
575
|
-
status = collect_status(
|
|
596
|
+
status = collect_status(
|
|
597
|
+
live=option == "live",
|
|
598
|
+
transport="auto",
|
|
599
|
+
provider=provider,
|
|
600
|
+
update_checker=update_checker,
|
|
601
|
+
)
|
|
576
602
|
return "\n".join(_status_lines(status))
|
|
577
603
|
|
|
578
604
|
|