Commit Graph

255 Commits

Author SHA1 Message Date
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
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 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
Teknium 2d52d645c3 refactor(agent/chat_completion_helpers): _stream_bedrock_converse -> _BedrockStream with phase methods 2026-09-02 18:39:10 -07:00
Teknium 7baf030bbd refactor(agent/chat_completion_helpers): split build_api_kwargs per api_mode; lift build_assistant_message phases 2026-09-02 18:32:33 -07:00
Teknium 497b38126f refactor(agent/chat_completion_helpers): split _StreamingCall god methods into phase helpers, unify delta/diag/callback duplicates 2026-09-02 18:30:28 -07:00
Teknium 6201b63d79 refactor(agent/chat_completion_helpers): direct_api_call lifecycle state -> _InlineRequest 2026-09-02 18:24:05 -07:00
Teknium bcf6fee21d refactor(agent/chat_completion_helpers): fold interruptible_api_call into _NonStreamRequest; compact SSE error parsing helpers 2026-09-02 18:21:41 -07:00
Teknium 8c7271b4bf refactor(agent/chat_completion_helpers): split try_activate_fallback/handle_max_iterations into phase helpers 2026-09-02 18:18:37 -07:00
Teknium 3e8977086f refactor(agent): compact Anthropic stream + retry-loop rationale comments/docstrings (AST-identical) 2026-09-02 13:29:36 -07:00
Teknium 9aee90335b refactor(agent): compact Bedrock and chat_completions stream-loop rationale comments (AST-identical) 2026-09-02 13:29:36 -07:00
Teknium d224e22507 refactor(agent): compact non-streaming call-path rationale comments/docstrings to their invariants (AST-identical) 2026-09-02 13:29:36 -07:00
Teknium 89a5ce12dd refactor(agent): build_api_kwargs — shared chat kwargs for profile/legacy paths; xAI tool_search alias rewrite helper 2026-09-02 13:29:36 -07:00
Teknium c5c6d7e566 refactor(agent): lift non-streaming stale/codex watchdog resolution into _resolve_nonstream_watchdogs 2026-09-02 13:29:36 -07:00
Teknium 3c80d4fe77 refactor(agent): compact build_assistant_message rationale comments to their invariants 2026-09-02 13:29:36 -07:00
Teknium 24b6b0e439 refactor(agent): compact streaming-path rationale comments to their invariants; drop no-op stale-kill if/else 2026-09-02 13:29:36 -07:00
Teknium 0e1875e859 refactor(agent): try_activate_fallback — lift api_mode hint/resolution and credential-pool rebinding into helpers 2026-09-02 13:29:36 -07:00
Teknium 3f497cf3f8 refactor(agent): interruptible_api_call — shared watchdog abort + post-kill worker wait helpers 2026-09-02 13:29:36 -07:00
Teknium 3e2ab5b382 refactor(agent): shared relay stream identity/metadata builders for the three streaming wires 2026-09-02 13:29:36 -07:00
Teknium ce9efccbaa refactor(agent): extract _ToolCallAccumulator from the chat_completions stream loop 2026-09-02 13:29:36 -07:00
Teknium 2d93a8b127 refactor(agent): share SSE connection-drop phrase check and codex silent-hang hint lookup 2026-09-02 13:29:36 -07:00
Teknium 3dc2403c45 refactor(agent): handle_max_iterations — single per-wire summary attempt with one retry pass; drop dead summary_request literal 2026-09-02 13:29:36 -07:00
Teknium e5e491ef5a refactor(agent): unify per-request client registry into _RequestClientRegistry (streaming + non-streaming) 2026-09-02 13:29:36 -07:00
Teknium ce276cd864 refactor(agent): decompose interruptible_streaming_api_call into _StreamingCall + per-wire branch functions
Closures -> _StreamingCall methods (shared worker/monitor state on the
instance), codex passthrough and Bedrock Converse branches -> free
functions, _stream_final_text/_emit_stream_* -> module helpers. Bodies
are AST-identical modulo the self.<name> renames (verified by script);
stream event handling and wire shapes unchanged.
2026-09-02 13:29:36 -07:00
Teknium c7e2e0b779 feat(fast): bounded /fast auto|cold windows behind one route-aware gate
Adds two bounded fast modes on top of the static /fast toggle, default OFF:

- `auto`: every user turn opens a `agent.fast_auto_seconds` (default 60s)
  window; requests inside it carry the provider fast param, later tool-loop
  requests fall back to standard pricing.
- `cold`: the same window, but only on the first turn of a session (no prior
  user/assistant/tool history).

agent/fast_mode.py holds the whole policy: `begin_turn()` at the
run_conversation ingress arms `agent._fast_until`; `effective_request_overrides()`
is consumed in the ONE place request_overrides feed the transports
(build_api_kwargs), so the fast param is a per-request kwarg only. System
prompt, tools and messages are untouched — the prompt cache is preserved.

resolve_fast_mode_overrides() is now the single gate for static and bounded
modes and accepts provider/base_url: OpenRouter, Nous, Copilot, Azure,
Bedrock and custom base_urls never receive service_tier/speed (#34308's
route gating). Both existing callers (CLI turn route, gateway turn route)
and the TUI config.set path pass the route.

Surfaces: config `agent.service_tier: auto|cold` + `agent.fast_auto_seconds`,
`/fast auto|cold` in CLI, gateway (picker gains both entries), TUI/desktop
config.set; status shows the mode; web dashboard select lists the real
values. Docs: configuration.md Fast Mode section with mode table + cost note,
slash-commands, cli-config.yaml.example, locale strings for the two picker
entries.

Salvages #89991 (bounded fast modes) and #34308 (route gating).
Fixes #64785, #74730.

Co-authored-by: Eva <239388517+100yenadmin@users.noreply.github.com>
Co-authored-by: kbaicai <kbaicai@qq.com>
2026-09-02 05:33:13 -07:00
teknium1 c83ea9bed7 test(agent): pin the reasoning-off continuation to exactly one request; document its prompt-cache cost
The one-shot reasoning-off retry changes a request parameter that is part
of the provider cache key on config-sensitive providers (Anthropic renders
thinking/effort into the prompt; OpenAI lists reasoning.effort as
prefix-affecting), so that request is a deliberate single cache miss.
Pin the bound: the request AFTER it must carry the configured reasoning
again and the system prompt must be byte-identical across the whole retry
sequence. Sabotage-verified (sticky flag -> test fails on request 3).
Docstring on _consume_ephemeral_reasoning_off states the cost honestly.
2026-09-02 00:55:42 -07:00
AlexGabbia fb76fb0526 fix(agent): thinking-only length truncations no longer wedge continuations
GLM-5.3-flash on ollama-cloud with reasoning_effort=high can spend the ENTIRE
output cap on reasoning delivered in a separate field and return
finish_reason=length with no visible content (verified live: max_tokens=4096,
completion_tokens=4096, content empty).

The length-continuation path handled that shape badly:
  1. the empty response was appended as an interim assistant fragment,
     poisoning the transcript until the pre-call sanitizer healed it
     (observed 3+ healings per turn on the reporting user's session);
  2. every continuation re-ran with thinking ON, re-deriving the whole
     thinking budget against a growing context, so 4 attempts still produced
     nothing and the turn died with 'Response remains truncated after 4
     continuation attempts'.

Now:
  - interim assistant fragments with no visible content are never appended
    (whichever way they got empty);
  - a thinking-only truncation sets a one-shot reasoning-off override that
    build_api_kwargs consumes for the next request, so the continuation
    writes the answer instead of re-thinking it;
  - the ceiling exit clears a pending override and, when every fragment was
    empty, returns an actionable final_response instead of an invisible None.
2026-09-02 00:55:42 -07:00
AgentLinker cac9db7caf fix(codex): retired stream requests must not synthesize a completed response
When a watchdog (TTFB / stream-idle / stale-call) force-closes a Codex
Responses request, the worker thread can still be draining SSE frames.
`_consume_codex_event_stream` returns `status=terminal_status`, which defaults
to `"completed"`, and its only truncation guard is
`if not saw_terminal and not output`. A mid-stream kill leaves
`saw_terminal=False` but `output`/text non-empty, so the partial text came back
as a `finish_reason=stop` response and got persisted as a finished assistant
turn — a long reply just stops mid-sentence with no error surfaced.

Observed as a long generation dying at `1. Create (6/6)` and never emitting its
end marker, with the truncated text already stored in state.db.

Fix: publish a per-request retirement token so the worker can tell it has been
retired.

- `agent/chat_completion_helpers.py`: `interruptible_api_call` installs
  `agent._active_codex_stream_request_token` before handing off to the worker
  (codex_responses only) and clears it at all four kill sites plus the worker's
  own `finally`. Retirement is cleared BEFORE `_close_request_client_once`,
  which can raise — every other call site wraps it in try/except, and a leaked
  token would let a later worker mistake itself for the owning attempt. The
  request-local `_codex_request_retired` mirror also swallows the transport
  error our own force-close causes, so the worker's local error cannot replace
  the watchdog's retryable TimeoutError (same split as `_request_cancelled`).
- `agent/codex_runtime.py`: `run_codex_stream` captures the token and raises
  `TimeoutError` from `interrupt_check` when it no longer owns the request —
  raising rather than breaking, because a break returns the partial `final`.
  The four stream callbacks also drop post-retirement frames so an abandoned
  attempt cannot stream tokens into the live turn's bubble (the gateway caches
  AIAgent instances per session).

`TimeoutError` is not an httpx / ConnectionError / RuntimeError subclass, so it
passes through the four `except` clauses around the consume call untouched.
No token installed (auxiliary callers such as `handle_max_iterations` drive
`_run_codex_stream` directly) means every check passes — behavior unchanged.

Tests: 5 new cases. Retirement raises instead of returning partial output;
post-retirement deltas stop reaching callbacks; the no-token path keeps its
existing terminal-frame tolerance; the watchdog installs and clears the token;
non-codex api_modes install nothing. A `_LazyCreateStream` helper is needed
because `_FakeCreateStream` materializes events in __init__, which would run
the retirement side effect before consumption starts.
2026-09-01 22:14:06 -07:00
Teknium c5b99a3ee5 fix(agent): delegated children and cron turns stream again — inline, no worker
should_use_direct_api_call() contexts (gateway cron turns #62151, delegate_task
children #60203) were short-circuited onto the NON-streaming wire because the
interrupt worker wedges inside their nested thread pools. That dropped every
liveness property streaming provides: edge proxies kill the silent POST
(z.ai HTTP 524 — three retries later the child dies as "max_iterations"), and
the non-stream stale watchdog cannot tell a reasoning model's thinking phase
from a hung provider, so children die at exactly stale_timeout (#100260).

Keep those contexts on interruptible_streaming_api_call. The request now runs
INLINE on the conversation thread (no worker → the deadlock class stays
closed) while the existing poll loop — 30s heartbeat, stale-stream detector,
cross-thread interrupt abort — moves onto a monitor thread that only ever
aborts sockets, never dispatches (same shape as direct_api_call's watchdog
timer). Interactive sessions are unchanged: worker + poll loop as before.

should_use_direct_api_call() itself is untouched; only what it routes to.

Live A/B (real SSE server, real AIAgent.run_conversation):
  before: subagent/cron wire stream=None, request on conversation thread
  after:  subagent/cron wire stream=True, request on conversation thread
          cli unchanged (stream=True, spawned worker)
  inline stale detector kills a one-chunk-then-silence stream at budget;
  AIAgent.interrupt() from another thread unwinds the inline stream in 0.6s.

Co-authored-by: Expri-commits <184641533+Expri-commits@users.noreply.github.com>
2026-09-01 21:42:19 -07:00
emozilla 43e67d872f feat: local models — managed llama.cpp runtime with one-click desktop setup
Run models locally as a first-class provider. The CLI grows a managed
llama.cpp runtime (engine install, model download, server supervision);
the desktop app grows the full setup and management story on top of it.
GUI surfaces ship behind the desktop --local launch flag (hermes desktop
--local, or the flag on the packaged app); backend routes and the CLI
are always live.

Runtime (hermes_cli/local_runtime/):
- curated GGUF catalog with per-machine variant selection: hardware
  probe (VRAM/RAM/UMA), fit planning with spill accounting, quant choice
  by context window
- derived recommendation: quality-ranked picks gated by a predicted
  decode-speed floor, bandwidth-aware on unified memory; the decision
  table is pinned as a test (pick AND reason per memory class), and the
  Recommended badge explains its pick in a tooltip fed by the resolver's
  actual branch
- engine install + model download with resumable split parts, cumulative
  plan-level progress, and staged-model integrity (a split GGUF counts
  only when every part is present)
- server supervision: spawn/adopt/stop, router mode with per-model load
  progress relayed over SSE, abandoned-request cleanup

Desktop:
- Settings -> Providers -> Local models: one-click quickstart (install
  engine, download the recommended model, boot) plus per-model download/
  activate/eject, fit-ranked catalog with context pills
- model pickers (composer dropdown + Cmd+K) show staged local models,
  in-flight downloads as live progress rows, and load-into-memory bars
- local-setup campaign tip for eligible hardware; System resources
  statusbar widget (GPU/VRAM/RAM); in-chat load progress during sends
- friendly dead-server errors, and failed agent builds retry on the next
  send instead of wedging the session

Co-developed with NVIDIA field feedback on RTX 5090 and DGX Spark.
2026-09-01 16:01:53 -04:00
rainbowgits 622883bad7 fix(agent): accept marker-only finish_reason after stream supersession
A superseded writer was fencing the payload-empty terminal chunk, so
completed streams were mislabeled as mid-stream drops.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-01 10:27:06 -07:00
Teknium 93591eccb5 fix(proxy): harden SSE DONE tracker — spec multi-line data joins, truthy lastOne, EOF-write guard
Follow-ups on the salvaged cluster:
- sse_done.py: dispatch SSE events at blank-line boundaries and join
  consecutive data: lines per the SSE spec (a split JSON event no longer
  reads as two malformed fragments that disable synthesis)
- accept integer/string-truthy lastOne sentinels (1 / "true") in both the
  proxy tracker and the agent stream reader
- server.py: guard the [DONE] append against client hangup at EOF and
  widen the interrupt tuple with OSError
- contributor email mappings for loulanyue and jon-nielsen
2026-09-01 10:12:21 -07:00
Jon Nielsen d304422b3d fix(streaming): extract finish_reason/usage before content-shape continues
vLLM >= 0.1.dev20051 merges finish_reason into the final content chunk.
When the SSE-echo guard is engaged at that moment (GLM-family tokenizers
emit standalone ':' / ' id' tokens mid-prose), the guard's content-shape
continue paths swallow the terminal chunk and finish_reason is never
captured, so a complete stream is misclassified as a mid-stream drop and
retried.

Extract finish_reason/usage at the top of the chunk loop body, before any
content-shape continue; the late tail-side extraction becomes redundant.

Addresses a second root cause of #94614 (the consume-gate fence is
covered by #94625; usage-side classification by #91376).
2026-09-01 10:12:21 -07:00