fix(plugins): warn when required env vars are missing, before register() - #68703
fix(plugins): warn when required env vars are missing, before register()#68703ygd58 wants to merge 2 commits into
Conversation
Ports NousResearch#63050 forward onto current main per teknium1's review. _load_plugin() emitted only a DEBUG registration count, so a plugin whose required env vars were absent loaded silently (issue NousResearch#2768). - Normalize requires_env entries (str or dict, matching hermes_cli/plugins_cmd.py:307-314) before checking the process environment. - Emit a WARNING when any required env var is not set, naming each missing variable. - The empty-registration note stays DEBUG (not WARNING) to avoid false positives for provider-only plugins (e.g. image_gen/fal), whose registrations aren't tracked in LoadedPlugin's tools/hooks/middleware/ commands counts. Per review, fixes two real bugs in the original port: 1. The missing-env check ran AFTER register_fn(ctx). A plugin that reads the missing variable directly during registration raises straight into the generic 'Failed to load plugin' handler before the specific missing-variable diagnostic is ever reported. Moved the check before register_fn(ctx) so the WARNING fires regardless of whether registration itself succeeds. 2. The provider-only regression test used a no-op register(), which doesn't actually exercise a provider registration. Replaced with a real ImageGenProvider subclass registered via ctx.register_image_gen_provider(), matching how plugins/image_gen/fal actually registers. Scope note: this fix is in the general PluginManager loader (hermes_cli/plugins.py). The Hindsight memory plugin path uses a separate loader (plugins/memory/__init__.py) that general discovery skips entirely -- this PR does not touch that path. Added a raising-register() regression test per review, and the corrected real-provider test. 5/5 new tests pass; 121/121 in the full tests/hermes_cli/test_plugins.py file.
teknium1
left a comment
There was a problem hiding this comment.
Thanks for carrying the str/dict normalization and the pre-register() diagnostic forward. The core missing-env gap remains on current main (hermes_cli/plugins.py:1783-1811).
Problems
- The proposed check is still after module import: the PR loads a directory/entry-point module at
hermes_cli/plugins.py:1764-1767, then begins validation athermes_cli/plugins.py:1779. A plugin that accesses a declared variable at module scope raises into the generic loader handler before the specific diagnostic runs. - The provider test captures only WARNING records at
tests/hermes_cli/test_plugins.py:2533-2540, while the newly added empty-registration message is DEBUG. Itsloaded.enabledassertion does not verify that the provider was registered.
Suggested changes
- Validate and warn before module loading, and add a module-import failure regression.
- Assert the named test provider is retrievable from
agent.image_gen_registrybefore cleanup.
Automated hermes-sweeper review.
| else: | ||
| ctx = PluginContext(manifest, self) | ||
|
|
||
| # Warn when a declared required env var is missing, BEFORE |
There was a problem hiding this comment.
This check runs after _load_directory_module() / _load_entrypoint_module() have executed. Move it before module loading so a plugin whose module-level import accesses a missing declared variable receives this specific diagnostic rather than only the generic loader failure.
| assert not any("registered no tools" in m for m in warning_msgs), ( | ||
| f"False positive warning for provider-only plugin: {warning_msgs}" | ||
| ) | ||
| loaded = mgr._plugins.get("provider_only") |
There was a problem hiding this comment.
loaded.enabled only proves register() returned normally. Assert agent.image_gen_registry.get_provider('test-provider-2768') is non-null so this regression verifies the claimed provider-registration path; the WARNING assertion alone is vacuous because the new message is DEBUG.
…al provider registration Follow-up per review of NousResearch#68703. Two real gaps: 1. The missing-env check still ran after _load_directory_module()/_load_entrypoint_module() -- only before register_fn(ctx). A plugin that accesses a declared requires_env variable at MODULE scope (not just inside register()) still raises during the import itself, propagating straight to the generic "Failed to load plugin" handler before the specific diagnostic ever runs. Moved the check to run first, using manifest.requires_env directly (available before the module is ever imported, so no ordering dependency on the module existing at all). 2. The provider-only regression test's caplog capture only recorded WARNING records, while the empty-registration message it was guarding against is DEBUG -- so the WARNING-absence assertion never actually exercised anything, and loaded.enabled only proves register() returned without raising, not that the registration call inside it took effect. Added an assertion that the provider is actually retrievable via agent.image_gen_registry.get_provider(), which only succeeds if ctx.register_image_gen_provider() genuinely ran. Added the requested module-import-failure regression test: a plugin that raises accessing the missing variable at module scope (before register() is even an attribute on the module) must still produce the specific WARNING, proving the check now runs early enough to catch that case too. 122/122 tests pass in the full tests/hermes_cli/test_plugins.py file (2 new/fixed, no regression).
|
Both were real. Fixed in the latest commit:
Added the requested module-import-failure regression test. 122/122 pass in the full tests/hermes_cli/test_plugins.py file (no regression). |
SummaryTen PRs address or reference this two-issue complex. The diffs divide into generic plugin-loader diagnostics (#2768, #58692, #63050, #68703), systemd environment loading (#18606), unavailable-memory-provider diagnostics (#70257), and lifecycle-hook implementations for closed #2817 (#2820, #2823, #2930, #3542). Related pull requests
Duplicates#2768, #58692, #63050, and #68703 substantially overlap on generic required-env diagnostics; #63050 is explicitly superseded by #68703, while #58692 is a salvage of #2768. #2820, #2823, #2930, and #3542 overlap on lifecycle hooks, with merged #3542 as the shipped consolidation. #70257 is complementary, not a duplicate, because it changes the separate memory-provider availability path. Suggested consolidationAuthor action: rebase #2768 onto main, or split out the part that can close the generic-loader gap, using #68703's pre-import str/dict validation and registry-backed regressions while retaining any complete registration-surface accounting from #58692. This preserves #2768's recorded best-fix designation and addresses its keep_open verdict; keep #68703 open as the concrete salvage path until that transfer is complete, then #58692 and #68703 can close as duplicates of #2768, while closed #70257 remains preserved through successor #74379 and the lifecycle PRs remain consolidated in merged #3542. Complex graphflowchart LR
classDef open fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
classDef merged fill:#dcfce7,stroke:#15803d,color:#14532d
classDef closed fill:#e5e7eb,stroke:#6b7280,color:#1f2937
classDef unverified fill:#f3f4f6,stroke:#9ca3af,color:#374151
classDef best stroke-width:3px,stroke:#b45309
classDef target stroke-width:3px,stroke:#4338ca
I2765(["issue #2765 (open)"])
subgraph Dup63050 ["PRs duplicating each other"]
P63050["PR #63050 (closed)"]
P68703["PR #68703 (open)"]
end
P68703 -.->|partial| I2765
class I2765 open
class P63050 closed
class P68703 open
class P68703 target
click I2765 "https://github.com/NousResearch/hermes-agent/issues/2765"
click P63050 "https://github.com/NousResearch/hermes-agent/pull/63050"
click P68703 "https://github.com/NousResearch/hermes-agent/pull/68703"
Graph: solid arrow = fixes / best fix, dashed arrow = partial or unverified (see edge label); boxed group = PRs duplicating each other; amber border = best fix; indigo border = target; gray node = closed (state tag in the node label). Cross-PR triage: Reviewed 10 pull requests and 2 issues in this complex. Each diff was read against this issue; Assessment working set: 109 kB of PR diffs, 29 kB of issue/PR text, 20 kB of discussion (27 comments), 14 verify verdicts. verdicts reflect diff content, not PR titles. Part of an automated triage batch. |
Context
Ports #63050 forward onto current main per @teknium1's review.
Problem
_load_plugin() emitted only a DEBUG registration count, so a plugin whose required env vars were absent loaded silently (issue #2768).
Fix
Normalize requires_env entries (str or dict) before checking the process environment; emit a WARNING naming each missing variable.
Per review, fixes two real bugs in the original port:
Scope note: this fix is in the general PluginManager loader. The Hindsight memory plugin path uses a separate loader (plugins/memory/init.py) that general discovery skips entirely -- this PR does not touch that path.
Verification
Added a raising-register() regression test and the corrected real-provider test. 5/5 new tests pass; 121/121 in the full tests/hermes_cli/test_plugins.py file.