Commit Graph

11 Commits

Author SHA1 Message Date
teknium1
607dff390a feat(telemetry): v5 efficiency metrics — turn cost, wasted tokens, tool output truncation, tool overhead, prompt-cache breaks
Six opt-in, bucketed shared-metrics counters (schema + contract + docs in `v5 efficiency` blocks):

- hermes.task_cost.count {provider, model, tokens_bucket, tool_calls_bucket, api_calls_bucket,
  outcome}: one row per user turn the user saw end (pre_llm_call-started, attended; forks, delegated
  children and session-close aborts excluded). Tokens = prompt+completion over the turn's primary
  calls. Tool/API call buckets reach gte_101 (TURN_ACTIVITY_BUCKETS; COUNT_BUCKETS untouched).
- hermes.wasted_tokens.count {provider, model, reason in undo/retry/interrupt, tokens_bucket}:
  emitted from the v4 friction sites (no re-detection). /undo N counts N turns; an interrupted turn
  later undone counts once; turns this process never saw read unknown.
- hermes.tool_output_truncation.count {tool, truncated, original_size_bucket}: one row per tool
  result, judged after the per-turn budget; covers the per-result spill, the turn budget, and the
  shared head/tail notice tools write when they cut their own output (original size reported).
- hermes.tool_overhead.count {enabled_tool_count_bucket, tool_schema_tokens_bucket,
  execution_surface} + hermes.tool_enabled_unused.count {toolset (shipped TOOLSETS else custom),
  used}: once per closed interactive conversation (merged across compression lineage).
- hermes.cache_break.count {provider, model, cause}: compression (committed), model_switch /
  system_prompt_rebuild (continuing conversation rebuilt its prompt), toolset_change (tool array
  changed mid-conversation, Bot Chat capability rebuild), provider_reported_miss (cold read after a
  warm read on the same route with no Hermes-known cause), cache_expired (same after >=5 min idle).
  A Hermes-known break suppresses the miss it causes. No prompt hashing.

Live-proven against a fake OpenAI-compatible server (chat -q, --resume -m, TUI gateway JSON-RPC
undo/retry/interrupt); disabled => zero telemetry files.
2026-09-28 12:43:03 -07:00
kshitijk4poor
1e04c64ef3 fix(stream_diag): feed the existing chunk-body upstream_provider into diag + hook payload (#90216)
Drop the duplicate chunk-reading helper, run_agent facade forward and
_last_serving_provider agent state; the chat-completions loop already captures
chunk.provider, so stamp it on the per-attempt diag there and read the hook's
upstream_provider from the assembled response.provider.
2026-09-24 22:25:06 +05:30
kouyichi
0370beae2e fix(agent): record the serving downstream provider in stream-drop diagnostics
Relay routing is re-rolled per request and the winning downstream is reported only
inside the delta chunk bodies, so the header snapshot in agent/stream_diag.py could
not attribute a mid-stream drop to a provider. The per-attempt diag now carries
serving_provider (first non-empty chunk-body provider), log_stream_retry prints it,
and the post_api_request payload exposes it as upstream_provider for plugins auditing
route compliance. Fixes #90216.

(cherry picked from commit f0cf4fe4f4b47e384532008b1bf56ad259c87dc2)
2026-09-24 22:25:06 +05:30
kshitijk4poor
a955e90422 refactor: hoist strip_images_for_rejecting_model import to module level
turn_api_request already imports from agent.message_sanitization at the
top of the module, so the function-local import added by the salvage is
an inconsistency, not a cycle guard. Hoist it onto the existing import
line; `python -c 'import agent.turn_api_request'` confirms no cycle.
2026-09-21 21:43:51 +05:30
rodricksz4h5
1ef306f68d fix(agent): an image rejection strips the request, never the session history
When a provider 4xx's on image content, recover_before_classification
ran _strip_images_from_messages on the canonical `messages` list and
reset _db_flush_scan_prefix. Since #117569 that function pops
_db_persisted on every rewritten dict, so the next flush rewrote those
rows: every image in the session — and every image-only message, which
the stripper deletes outright — was removed from state.db for good.

The rejection describes what the CURRENT model accepts, not what the
conversation holds. An automatic fallback to a text-only provider, or a
single /model switch, was enough to erase images the user had sent to a
vision model, and switching back found them gone. It is the same failure
as the ASCII strip in #117802, on the image path; neither open fix for
that issue touches this branch.

Keep the repair on the send path, where the per-call copy already lives
(_clone_message_for_send exists so send-path rewrites never reach the
persisted transcript, #80498):

- record the rejecting (provider, model) on the agent, a session-scoped
  flag initialised beside _force_ascii_payload;
- strip the in-flight api_messages copy for the immediate retry;
- build_api_request calls strip_images_for_rejecting_model() on each
  attempt's api_messages BEFORE provider conversion. The stripper knows
  Hermes's own part types; a converted payload would slip past it
  (Bedrock Converse image blocks carry no `type`). Keyed on the model,
  so one that accepts images gets them again.

_strip_images_from_messages itself is unchanged, so its role-alternation
and sidecar guarantees still hold on the wire copy. The notice no longer
claims "text-only mode for this session" (_vision_supported resets every
turn) or that images were stripped from history.

(cherry picked from commit cbdb184c27f915ab138b2087f878aed7fcc7c76b)
2026-09-21 21:43:51 +05:30
kshitijk4poor
a19160bbf0 refactor(agent): one outbound-kwargs sanitizer seam for the main loop and the summary
The summary path had grown a verbatim copy of turn_api_request's 3-line
surrogate/ASCII chokepoint — the same drift class this PR removes for the
hand-rolled kwargs builder. Move the two lines and the #50959 rationale into
`message_sanitization.sanitize_outbound_kwargs` and call it from both sites,
so the next sanitizer step added to the main loop cannot miss the summary.
Tighten two comments: "same kwargs builder" (cache_control redecoration is
not re-applied here) and a `_summary_text` note that is true for all three
summary branches, not just the chat one.
2026-09-14 20:35:28 +05:30
Teknium
e83816a4d1 review-fix(comments): restore lost #NNNN rationale comments across non-test source (mechanical sweep, condensed, code unchanged)
For each issue anchor present in BASE 63279301bc non-test .py and absent on HEAD, the BASE comment/docstring block was re-attached at the HEAD location of the code it explained (matched by the distinctive code line / enclosing def). Sentences already covered by an existing HEAD comment were deduped; the issue number always survives. Insert-only: no code lines changed.
2026-09-03 09:44:26 -07:00
Teknium
863fc9afa8 refactor(agent/turn): lift hook-firing, MoA context/prepare, empty-retry and terminal-empty phases into helpers 2026-09-02 18:40:01 -07:00
Teknium
c94ced6225 refactor(agent/turn): AST-neutral bracket/signature packing across r3-08 slice 2026-09-02 18:01:27 -07:00
Teknium
2769937936 refactor(turn): lift iteration entry/announce, Nous rate guard, API interrupt, retry-restart consumer and preflight-timeout result out of run_conversation 2026-09-02 16:37:14 -07:00
Teknium
9fda4e5bac refactor(turn): extract retry-loop API error handler, request build, provider call and response check into agent/turn_api_*.py + agent/turn_response_check.py 2026-09-02 16:07:51 -07:00