hermes - ✅(Solved) Fix feat(display): declarative tool preview schema to eliminate per-tool hardcoding [1 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
NousResearch/hermes-agent#28621Fetched 2026-05-20 04:03:06
View on GitHub
Comments
0
Participants
1
Timeline
5
Reactions
0
Author
Participants
Timeline (top)
labeled ×3cross-referenced ×2

Fix Action

Fix / Workaround

# In tool definition (YAML config or tool decorator)
preview:
  field: action          # dispatch field
  templates:
    add: "+ \"{content:.30}\""
    search: "search: \"{query:.25}\""
    probe: "probe: {entity}"
    reason: "reason: {entities:.3}"
    update: "update: #{fact_id}"
    remove: "remove: #{fact_id}"
    "*": "{action}"       # fallback for unlisted actions
  truncate: 30

PR fix notes

PR #28719: feat(display): declarative tool-preview schema to eliminate per-tool hardcoding (#28621)

Description (problem / solution / changelog)

What does this PR do?

Adds a declarative tool-preview schema registry to agent/display.py so new tools — including plugin/third-party tools — can ship a one-line progress preview alongside their schema definition, instead of editing the hardcoded if-elif chain in build_tool_preview() every time.

Before, tools without a manual entry fell back to ⚙️ tool_name... on Feishu / Telegram bubbles and the CLI spinner. This is what prompted #28598 (fact_store / fact_feedback showing no useful context); #28621 generalises that observation: the if-elif chain is a structural oversight generator, and plugins have no path to preview support at all.

The new API is:

from agent.display import register_tool_preview

register_tool_preview(
    "fact_store",
    field="action",
    templates={
        "add":    '+ "{content:.30}"',
        "search": 'search: "{query:.25}"',
        "remove": "remove: #{fact_id}",
        "*":      "{action}",          # wildcard fallback
    },
    truncate=60,                        # per-tool cap
)

Design choices:

  • Backward compatible — registry is consulted before the legacy chain. Existing tools keep working; migration is incremental, no flag-day.
  • Plugin-friendly — plugins call register_tool_preview() at import time, next to their schema definition. No core code edit required.
  • Safe by default — missing keys render as "" (a partial tool call never crashes the spinner), string values are whitespace-collapsed, lists are joined with , , bad templates degrade to None + a warning log instead of raising.
  • Per-tool overridetruncate=N takes priority over the global _tool_preview_max_len, useful for plugins that know their content is naturally short.
  • Last-write-wins — plugins can override built-in previews if they need to.

Related Issue

Fixes #28621 Also closes #28598 (the user-visible bug that prompted this proposal — fact_store and fact_feedback now render meaningful previews).

Type of Change

  • ✨ New feature (non-breaking change that adds functionality)
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)

Changes Made

  • agent/display.py — adds register_tool_preview(), _unregister_tool_preview(), the _TOOL_PREVIEW_REGISTRY module-level dict, _SafePreviewArgs (a dict subclass that returns "" for missing keys and normalises values for str.format_map), and _render_registered_preview(). build_tool_preview() consults the registry first, applies the per-tool truncate (falling back to the global _tool_preview_max_len), and only then falls through to the existing hardcoded chain. Also hardens the legacy path against truthy non-dict args.
  • plugins/memory/holographic/__init__.py — registers preview templates for fact_store (10 actions, including add, search, probe, reason, update, remove, plus a * fallback) and fact_feedback (helpful / unhelpful / *). The registration sits right next to FACT_STORE_SCHEMA / FACT_FEEDBACK_SCHEMA so reviewers see UX + schema together.
  • tests/agent/test_display.py — adds TestRegisterToolPreview (15 unit tests covering single-template rendering, field dispatch with * fallback, missing-key safety, list-arg joining, per-field precision truncation, per-tool vs. global truncate precedence, whitespace collapsing, None"", bad-template fallback with log assertion, last-write-wins re-registration, registry-over-legacy priority, and rejection of malformed registrations) plus TestFactStorePluginPreview (7 smoke tests for the holographic plugin registrations) and one extra defensive test for truthy non-dict args.
  • website/docs/developer-guide/adding-tools.md — adds an "Optional: Tool Progress Previews" section showing the API and dispatch form, plus a checklist nudge so authors register a preview when relevant.

Memory and the other complex special cases (process, todo, send_message) are intentionally left on the legacy path in this PR — the issue's scope calls for 2–3 POC migrations with the rest deferred. memory in particular has a "<missing old_text>" sentinel that doesn't fit the minimal field/templates/truncate schema cleanly; if that's wanted, it should land in a follow-up PR (possibly extending the schema with an optional defaults mapping).

How to Test

# Full registry coverage + smoke tests for the plugin migration.
scripts/run_tests.sh tests/agent/test_display.py -q
# Expected: 55 passed (31 existing + 24 new).

# Manual: with the holographic-memory plugin enabled, watch a tool
# progress bubble while issuing a fact_store call from chat.
# Before this PR:  ⚙️ fact_store...
# After this PR:   ⚙️ fact_store + "user prefers tabs"

Checklist

Code

  • My commit messages follow Conventional Commits (feat(display):, feat(memory/holographic):, test(display)+docs:)
  • I searched for existing PRs — no duplicate
  • My PR contains only changes related to this feature
  • I've run scripts/run_tests.sh tests/agent/test_display.py -q (55 passed, 0 failed)
  • I've added tests for my changes (22 new tests covering the registry contract + plugin smoke tests)
  • I've tested on my platform: macOS 15.x (darwin 24.6.0)

Documentation & Housekeeping

  • I've updated relevant documentation (website/docs/developer-guide/adding-tools.md)
  • N/A — no config keys changed (cli-config.yaml.example)
  • N/A — no architecture / workflow change requiring CONTRIBUTING.md / AGENTS.md updates
  • I've considered cross-platform impact — pure Python, no platform-specific code paths
  • N/A — no tool description/schema changes (preview registration is additive metadata)

Changed files

  • agent/display.py (modified, +167/-0)
  • plugins/memory/holographic/__init__.py (modified, +35/-0)
  • tests/agent/test_display.py (modified, +209/-0)
  • website/docs/developer-guide/adding-tools.md (modified, +46/-0)

Code Example

# In tool definition (YAML config or tool decorator)
preview:
  field: action          # dispatch field
  templates:
    add: "+ \"{content:.30}\""
    search: "search: \"{query:.25}\""
    probe: "probe: {entity}"
    reason: "reason: {entities:.3}"
    update: "update: #{fact_id}"
    remove: "remove: #{fact_id}"
    "*": "{action}"       # fallback for unlisted actions
  truncate: 30
RAW_BUFFERClick to expand / collapse

Problem

build_tool_preview() in agent/display.py is a hardcoded if-elif chain — every tool needs a manual entry to show meaningful progress bubbles on chat platforms (Feishu, Telegram, etc.). When a tool is added without updating this function, users see unhelpful generic output like ⚙️ fact_store... instead of actionable information like + "user prefers dark mode".

This is a structural oversight generator: #28598 (fact_store/fact_feedback) is just the latest instance. Each time it happens, someone files an issue, someone adds an elif block, and the cycle repeats. Plugin/third-party tools have no path to preview support at all.

Proposal

Add a declarative preview schema so tools (including plugins) register their own preview format at definition time, eliminating the need for core code changes.

Option A: Tool-level preview config

# In tool definition (YAML config or tool decorator)
preview:
  field: action          # dispatch field
  templates:
    add: "+ \"{content:.30}\""
    search: "search: \"{query:.25}\""
    probe: "probe: {entity}"
    reason: "reason: {entities:.3}"
    update: "update: #{fact_id}"
    remove: "remove: #{fact_id}"
    "*": "{action}"       # fallback for unlisted actions
  truncate: 30

build_tool_preview() would then:

  1. Check if tool has a registered preview schema
  2. If yes → render template from schema
  3. If no → fallback to current hardcoded handlers (backward compat)
  4. If no match at all → None (existing behavior: show tool name)

Option B: Preview function in tool module

Each tool module exports an optional preview(args) -> str | None. build_tool_preview() calls it if present, falls back to current logic otherwise.

Benefits

  1. Eliminates structural forgetting — new tools include preview at registration, no display.py edit needed
  2. Plugin-friendly — third-party tools get preview support without core code changes
  3. Backward compatible — existing tools keep working, can migrate incrementally
  4. Self-documenting — tool definitions declare their UX contract

Scope

  • Refactor build_tool_preview() to support schema lookup
  • Migrate 2-3 existing tools (e.g. memory, fact_store) as proof of concept
  • Remaining tools migrate incrementally
  • Document preview schema in plugin developer guide

Related

  • #28598 — The issue that prompted this proposal (fact_store/fact_feedback had no preview)

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - ✅(Solved) Fix feat(display): declarative tool preview schema to eliminate per-tool hardcoding [1 pull requests, 1 participants]