Commit Graph

286 Commits

Author SHA1 Message Date
Teknium fc70d05bb1 fix: arm primary cooldown once while walking fallback candidates 2026-09-07 07:08:25 -07:00
Teknium 68b16aa7e3 fix: retain exhausted-chain cooldown classification after extraction 2026-09-07 07:08:25 -07:00
Teknium fb2f66f586 fix: describe remaining retry eligibility without promising recovery 2026-09-07 07:08:25 -07:00
Halldrix ae4f777c69 fix(agent): surface armed rate-limit cooldown in fallback notice (#104120)
_arm_rate_limit_cooldown now returns the armed backoff seconds so
try_activate_fallback can append them to the user-facing notice
(Primary retried in ~N min/h) instead of discarding the one number
that decides whether the user waits or re-plans. Duration, never
wall-clock: the stored deadline is monotonic-based. Non-rate-limit
reasons and chain-switches from an active fallback arm nothing, so
no suffix is printed there.
2026-09-07 07:08:25 -07:00
Teknium fd3565deec fix: remove dedicated user-facing output cap controls 2026-09-07 06:15:43 -07:00
Teknium d0081d9597 refactor: colocate fallback handoff with client lifecycle 2026-09-07 06:07:16 -07:00
Teknium ba395af50f refactor(agent): isolate streaming wait monitor phase 2026-09-07 02:37:13 -07:00
Teknium 47d73afeb1 refactor(agent): isolate non-stream request polling 2026-09-07 02:37:13 -07:00
liuhao1024 3c9c225425 fix(agent): tolerate schema-valued "type" keys in the watchdog payload estimator (#104793)
The image-part membership test in _payload_chars() ran on every dict in
the request, so a tool JSON Schema with a parameter named "type" (its
value is the sub-schema dict) or a multi-type value like
["string", "null"] raised TypeError: unhashable type before the
request reached the provider. Guard the membership test with
isinstance(str): structured "type" values are payload data, not
content parts, and keep the legacy walk for them.
2026-09-07 00:49:51 -07:00
crdesign8 922c0d670c fix(agent): stale-call watchdog prices images at the learned per-image cost, not base64 length (salvage #76471)
estimate_request_context_tokens (the stale-stream / non-stream watchdog and Codex TTFB floor
estimator) did len(str(payload)) // 4 over the wire payload, so one native screenshot read as
~100K tokens and selected the 1200s giant-conversation floor while the provider's real prompt
was ~2K (#63871, #76411). Image content parts on both wire shapes (Chat image_url / Responses
input_image) now cost the per-image price learned from provider usage (agent/image_token_cost,
bound per turn; worker threads inherit it via copy_context); base64-looking STRINGS stay text
and tool schemas mentioning image are not images.

A/B, one 300KB screenshot: main estimate 100,545 -> codex floor 1200s / stale 300s; now 2,025
-> floor 0s / stale 180s.

Design and boundary (structured parts only, tools/instructions opaque, plain data-URL strings
stay text) from #76471 by @crdesign8; re-authored to reuse the learned cost instead of a second
fixed constant, and trimmed to 2 invariant tests.
2026-09-06 22:34:20 -07:00
sylvainCDA 693641aa8b fix(chat-completions): strip name from tool-result messages for strict providers
The Chat Completions schema has no `name` field on `role: tool` (only on
the long-removed `role: function`), but Hermes carries the tool name over
onto the result message. Permissive providers ignore it; strict ones
(aki.io) reject the whole payload with `contains item with unknown key
name`, which breaks every tool call in the session.

Follow-up to the review on #51365:

- The strip now goes through the copy-on-write `mutable_msg()` path in
  `convert_messages()`, preserving the identity/copy-on-write contract
  instead of mutating `msg` in place.
- `handle_max_iterations()` hand-builds its summary payload and calls
  `chat.completions.create()` directly, bypassing the transport, so it
  leaked `name` even with the transport fixed. It now mirrors the same
  role-qualified removal, next to the existing tool_name/codex_*/timestamp
  strips.

The removal is role-qualified: `name` stays on user/assistant messages,
where it is schema-valid.

Regression coverage on both paths; both tests fail without the fix.
2026-09-07 02:50:45 +05:30
Teknium 6d2645e64e feat(logging): the API call line carries cache write count, the provider's response id, and the serving upstream
Diagnosing the 1,393-agent run's cache misses took a state.db join and a live
probe because none of the three were on the one line we log per call:

  write=<n>    cache_creation tokens; a write costs 50x a read, so this is the
               money, and "read stuck, write large" on consecutive lines is a
               routing miss visible without a probe
  id=<id>      the provider's response id (Anthropic msg_..., OpenRouter/Nous
               gen-...): what a provider needs to look a request up
  upstream=<n> who actually served, when the route reports it (OpenRouter's
               `provider`); how we learned GMI was not serving

Fields are appended to the existing line and omitted when absent, so every
existing parser (evals/postmortem, the two cache_prefix probes) keeps
matching; the forensics parser reads them when present.

The streamed chat path fabricated `id="stream-<uuid>"` and dropped the
chunks' id/provider on the floor; it now keeps the first chunk's id and
provider, falling back to the fabricated id only when the stream never sent
one. Nothing depended on the prefix.

Live (Nous, Fable 5.1, both wires): chat -> `write=3759
id=gen-1788727882-... upstream=Anthropic`; native -> `id=gen-1788727891-...`
(the Anthropic Message object's id was already real). Tests: fields present
and omitted, prefix unchanged; forensics parser reads new and old lines.
2026-09-06 13:52:15 -07:00
kshitijk4poor 3ac671dbba refactor(agent): drop the write-only agent-level codex watchdog timestamps
With a request-local watchdog state on every codex request, agent._codex_stream_last_event_ts / _last_progress_ts had no remaining reader; the state-None snapshot only runs for non-codex requests where no watchdog consults it.
2026-09-07 00:41:09 +05:30
kshitijk4poor 1ad8b5dd4a refactor(agent): trim the watchdog phase split to what the tests pin
- Explicit-vs-implicit idle timeout detection uses env_float with a sentinel
  instead of a hand-rolled parse (unset and unparseable both mean implicit).
- _on_event writes the agent-level timestamps only when there is no
  request-local watchdog state; with state present they were dual bookkeeping
  nobody read (the snapshot prefers the state).
- _abort_request keeps main's close-then-retire order: the reorder had no
  test teeth and is a separate concern from #90449.
2026-09-07 00:41:09 +05:30
PeaceMaker-best 463d4bcd15 fix(agent): defer codex idle watchdog until progress
Signed-off-by: PeaceMaker-best <221849497+PeaceMaker-best@users.noreply.github.com>
2026-09-07 00:41:09 +05:30
Benjamin Brumbaugh cd71ee0708 fix(compression): defer local preflight after native checkpoint
A native Responses compaction checkpoint is opaque ciphertext; the rough
preflight estimator counts it as text (5.17M chars -> ~1.29M tokens against
a 204K trigger) and fires local compression on a request whose real prompt is
~116K. Arm the existing one-response real-usage latch when a replayable
checkpoint is captured (build_assistant_message) or restored into a fresh
agent (_hydrate_from_history), honor it in the post-tool gate and idle
compaction, and require non-empty encrypted_content for a checkpoint.

Squash of the author's source commits from #100642 (0e3c234ea0, 771e1b3365,
bb1505a119) plus the fdf140c81d test refresh, re-based onto current main by
patch application. Source delta is byte-identical to the PR head d6ce3e236d.

Fixes #100611
2026-09-06 09:09:00 -07:00
Teknium 12871bd01e feat(openrouter): per-model provider_routing.models.<id> overrides
`provider_routing.models.<model-id>` now takes the same only/ignore/order/sort/
require_parameters/data_collection keys and overlays the flat provider_routing
values whenever the agent is on that model. Resolution lives in the one
chokepoint every request path already uses (_provider_preferences_for_agent),
so CLI, gateway, TUI/Desktop, cron, /model switches, fallback activation and
delegated children on another model all honour it with no per-surface plumbing.
Matching is spelling-tolerant, sharing _canonical_model_variants with
agent.reasoning_overrides.

The OpenRouter profile's speed-tier pin no longer overwrites an explicit user
`only` on the BASE gpt-6-astra slug: the pin exists to keep default routing off
flex/fast, and a user pin is the stronger intent (only: [openai] stays [openai]
instead of becoming [openai, azure, azure/us]). Tier slugs (-fast/-flex) keep
owning `only`.

Live A/B (config only: {gpt-6-astra: [openai], claude-fable-5.1: [anthropic]}):
main sent {"sort":"price"} for fable and OpenRouter served it from Azure; with
this change it sends {"only":["anthropic"],"sort":"price"} and Anthropic serves it.

Schema proposed in #24495 (samplesabotage) and #100711 (Artemonim); this is a
slim chokepoint implementation of that design.

Co-authored-by: samplesabotage <samplesabotage@users.noreply.github.com>
2026-09-06 02:18:14 -07:00
RelaxJonh 4ee2c78324 fix(agent): Kimi Code fallbacks use the Anthropic Messages wire (#77256)
`try_activate_fallback` derived the fallback api_mode with its own host
heuristic (api.anthropic.com / trailing `/anthropic`) that lagged the
primary path's `_detect_api_mode_for_url()`: a `fallback_providers` entry
pointing at `api.kimi.com/coding` activated but POSTed OpenAI-shaped
`/chat/completions`, which that endpoint 404s, so every fallback turn
failed after the chain had correctly switched.

Route `_is_anthropic_wire_url` through `providers.host_mandated_api_mode`
— the single Messages-only host table the primary path already uses — so
the fallback path can never drift from it again. Exact-hostname match
rejects `api.kimi.com.attacker.test`.

Salvaged from #77304 by @RelaxJonh (original detection branch).
2026-09-05 15:54:14 +05:30
kshitijk4poor 74de0fd4fe fix(relay): build the chat-completions Relay response from collector-observed chunks
Relay invokes its finalizer as soon as the provider stream ends, concurrently with
Hermes' consumer thread, so any finalizer that reads the consumer loop's closures
races the last chunk. #103104 moved `usage` onto the collector; the same race still
truncated the final tool-call `arguments` delta, dropped the last content delta, and
downgraded `finish_reason` to "stop" in Relay's annotated response (reproduced ~50%
of runs). Every other Relay integration (Anthropic, Bedrock, Codex) already
rebuilds from collector-observed chunks; this makes chat_completions match via
`_RelayChatAccumulator` and removes the single-field nonlocal stopgap.

Test: the ordering harness is shared, and a second invariant test pins the
tool-call/finish_reason case (fails on the stopgap, passes here). Hermes' own
returned response was never affected.
2026-09-05 10:54:19 +05:30
mnajafian-nv 2cf5e68c5d fix(relay): retain usage from trailing chat completion chunks
Signed-off-by: mnajafian-nv <mnajafian@nvidia.com>
2026-09-05 10:54:19 +05:30
Teknium 912a497ce2 simplify(compat): tools/browser_tool — repoint run_agent cleanup_browser + chat_completion_helpers _is_headed_mode to the defining browser_tool_* siblings 2026-09-03 14:17:11 -07:00
Teknium 98c140bc4b simplify(compat): code_execution_tool/environments.local — drop 36 re-exports, repoint 5 callers + 14 test files 2026-09-03 13:24:04 -07:00
Teknium d179f28307 simplify(compat): anthropic_adapter — drop 30 re-exports + 1 alias, repoint 22 caller files (32 sites), 38 test files (~125 sites) 2026-09-03 13:16:47 -07:00
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 022785a541 Merge origin/main (63279301bc): reasoning-mandatory 400 recovery folded into turn_recovery/error_classifier/models_reasoning_caps 2026-09-03 05:36:21 -07:00
Teknium 63279301bc fix(reasoning): retry after a mandatory-reasoning 400 resends the user's own effort
The retry must land on the same provider cache key as every prior request
in the session. Discard only the one-shot continuation disable and send
agent.reasoning_config verbatim; a config that is itself a disable is
omitted (that session never sent anything else, so nothing warm is lost).

Live: user effort=high, ephemeral disable → 400 → retry carries
{enabled: true, effort: high}.
2026-09-03 05:20:11 -07:00
Teknium f6bd1633f7 fix(reasoning): GLM-5.3 on Nous/OpenRouter no longer 400s when thinking is disabled
Reasoning-mandatory routes answer reasoning: {enabled: false} with HTTP 400
"Reasoning is mandatory for this endpoint and cannot be disabled". Hermes
sends that disable for /reasoning none, agent.reasoning_effort: none, and the
one-shot thinking-exhaustion continuation override (which GLM-5.3-flash
triggers on its own). The Nous profile's catalog guard swallows the disable
only when its per-process capability cache already says mandatory; a gateway
that warmed the cache before the route flipped kept sending it, and the 400
was classified as a non-retryable format_error that aborted the turn.

- error_classifier: new reasoning_mandatory reason (retryable, no fallback,
  no compression), matched before the request-validation branch.
- conversation_loop: one-shot recovery — set agent._reasoning_disable_rejected,
  queue a catalog refresh for the provider, retry.
- chat_completion_helpers: _reasoning_config_for_wire drops every disable
  (configured or ephemeral) once the route has rejected one.
- hermes_cli/models: refresh_reasoning_caps_async(provider) forces a
  background re-fetch of the Nous/OpenRouter catalog.
- openrouter profile: omit a disable when the catalog marks the route
  mandatory (parity with the Nous profile).

Live: z-ai/glm-5.3-flash on the Portal with a poisoned mandatory:false cache.
Before: turn aborted with the 400. After: one retry, thinking stays on, turn
completes.
2026-09-03 05:20:11 -07:00
Teknium 0071ba9965 Merge origin/main (561b053f79) into simp/forwardport: forward-port 220 main commits into the simplified tree 2026-09-03 03:31:03 -07:00
Teknium 24a6b8d202 refactor(agent/chat_completion_helpers): fold silent-hang hint and exhausted-cooldown helpers 2026-09-02 23:35:58 -07:00
Teknium b2b0e34038 refactor(agent/chat_completion_helpers): fold fallback api-mode/credential branches and load-notice early returns 2026-09-02 23:34:14 -07:00
Teknium 21e50b7249 refactor(agent/chat_completion_helpers): share cloud stale-timeout derivation, Bedrock first-delta wrapper, summary text normalizer 2026-09-02 23:30:52 -07:00
Teknium 185a0c9037 refactor(agent/chat_completion_helpers): AST-identical — drop blank lines after in-function imports 2026-09-02 23:23:41 -07:00
Teknium e5758d1593 refactor(agent/chat_completion_helpers): unify Bedrock converse/converse_stream recovery, fold role ladder, reflow comments 2026-09-02 23:22:23 -07:00
fangliquan 390c27c6db fix(agent): accumulate streamed tool arguments linearly 2026-09-03 11:43:00 +05:30
Teknium b2ab212a57 refactor(agent/chat_completion_helpers): compact narrative docstrings (keep every invariant/why) 2026-09-02 23:11:00 -07:00
Teknium a341b7cc27 refactor(agent/chat_completion_helpers): move direct_api_call heartbeat/stale-timer lifecycle onto _InlineRequest (direct_api_call 103 -> 60 LOC) 2026-09-02 23:07:06 -07:00
Teknium afaf61c521 refactor(agent/chat_completion_helpers): share stream start/end bracket, lift chat-stream closures to methods, unify writer fence 2026-09-02 22:59:27 -07:00
Teknium 6ee2ed773e refactor(agent/chat_completion_helpers): collapse single-statement try/pass to suppress, fold single-use locals, share stale-base resolver 2026-09-02 22:47:34 -07:00
Teknium 10c023101c refactor(agent/chat_completion_helpers): AST-identical repack of wrapped calls/collections (3947 -> 3773 LOC) 2026-09-02 22:28:13 -07:00
Teknium 139396995a feat(opencode): send x-opencode-session on every OpenCode request for backend affinity
OpenCode pins requests sharing an x-opencode-session value to one upstream
backend, which is what keeps its prompt cache warm across a conversation.
Hermes never sent it, so cache ratios on OpenCode traffic were poor.

- agent/opencode_affinity.py: single owner of the header — target detection
  (built-in zen/go/free, custom opencode-* providers, any opencode.ai URL)
  and the key (affinity scope → conversation root → session id, cron
  timestamp stripped), same resolution as OpenRouter/xAI affinity hints.
- build_api_kwargs: merged once after the per-mode builder, so
  chat_completions, codex_responses and anthropic_messages all carry it.
- auxiliary _build_call_kwargs: same key from the runtime-main session so
  compression/title/vision calls stay on the conversation's backend; the
  aux Codex and Anthropic adapters now forward extra_headers.

Closes #81584, #81832 (deepseek-v4-flash 400 without the header).
2026-09-02 21:46:36 -07:00
Teknium 836fa5e08e refactor(agent/chat_completion_helpers): compact streaming call sites and tool-call accumulator 2026-09-02 19:43:52 -07:00
Teknium a0e0705ae7 refactor(agent/chat_completion_helpers): compact bedrock/streaming helpers and relay metadata 2026-09-02 19:27:15 -07:00
Teknium c205b41a52 refactor(agent/chat_completion_helpers): drop _env_float duplicate of utils.env_float; compact kwargs builders and assistant-message helpers 2026-09-02 19:23:26 -07:00
Teknium de1567095a refactor(agent/chat_completion_helpers): collapse defensive layers and compact call sites across the non-streaming region 2026-09-02 19:17:28 -07:00
Teknium 032ac76293 refactor(agent/chat_completion_helpers): drop stray section header, fix blank-line spacing 2026-09-02 18:59:41 -07:00
Teknium aabb4ddf44 Merge branch 'simp/r3-04-w1b' into simp/r3-04-w1 2026-09-02 18:57:25 -07:00
Teknium 6ea0fbcc0b refactor(agent/chat_completion_helpers): compact fallback/max-iterations region; hoist _FALLBACK_REASON_LABELS 2026-09-02 18:51:59 -07:00
Teknium 28d2e85a23 Merge branch 'simp/r3-04-w1a' into simp/r3-04-w1 2026-09-02 18:51:26 -07:00
Teknium d5341ab1c9 refactor(agent/chat_completion_helpers): compact helpers, docstrings and comments in the non-streaming region 2026-09-02 18:50:52 -07:00
Teknium de570c0099 refactor(agent/chat_completion_helpers): compact _StreamingCall comments/docstrings and fold single-use locals 2026-09-02 18:45:17 -07:00