th-c127d1: send temperature 1.0 — 0 is rejected by many frontier models - #333
th-c127d1: send temperature 1.0 — 0 is rejected by many frontier models#333brentrager wants to merge 7 commits into
Conversation
…one step The conversation-workflow judge advances only on "yes". When a step's criteria demand evidence the judge never accepts, the step re-asks indefinitely and a multi-step guided flow (the public Transformation Posture agent) never reaches its posture-summary / lead-capture step — it just re-asks one dimension until the conversation runs out. The step pointer itself persists and advances correctly; the missing piece is an escape hatch. apply_step_cap force-advances to the next step after WORKFLOW_STEP_ATTEMPT_CAP (3) consecutive non-advancing turns, resetting on any real advance. The counter persists in session metadata (stepAttempts) alongside the existing currentStepId pointer, read/written in the send_message handler exactly like the pointer. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…per turn LocalServer/AppState now carry host-supplied `Vec<Arc<dyn ToolHook>>`, plumbed into every per-turn ToolRegistry BEFORE the per-agent auth gate and confirmation hooks — so a host permission/surveillance hook gets first say on every call. This is the prerequisite seam for Big Smooth's narc-judge + auto-mode: the host hands the server its own ToolHooks (auto-mode allow/deny/ask gate + LLM judge) without forking the runner. - LocalServerBuilder::tool_hooks(Vec<Arc<dyn ToolHook>>) → AppState::with_tool_hooks → TurnRequest::tool_hooks, registered in run_streaming_turn. - SharedToolHook newtype forwards all 5 hook methods so an Arc<dyn ToolHook> can be registered via ToolRegistry::add_hook (which takes impl ToolHook by value; the orphan rule forbids a blanket impl here). - Tests: spy-hook fires pre_call/post_call through the real runner turn; builder seam installs hooks onto AppState. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01USBgWKcgpkWAugmHPE7Bc9
…mission system + DenyPolicy) Bumps the workspace core pin 0.16.2 -> 1.7.0 so the server the Big Smooth daemon pins is built against the engine that ships the permission system. This lets the daemon install `PermissionHook::with_deny_policy(...)` via the existing tool_hooks seam (th-1f694a) without forking the runner. - rust/Cargo.toml: smooai-smooth-operator-core = "1.7.0" (+ updated note). - Merged the tool_hooks seam branch (th-1f694a, rev 5661772) which was not on main; resolved handler.rs/runner.rs merge with main's newer persist_workflow_step (th-c12df5) path, dropped the seam's stale duplicate cap_* binding, and unioned the runner import (LlmClient + ToolCall/ToolResult). - injection_seams test: add the multimodal `images` field main introduced after the seam branch was cut. Core 0.16.2 -> 1.7.0 required no server source changes beyond the merge — ToolHook::post_call already took `&mut ToolResult` at 0.16.2. Engine 1.7.0 exports PermissionHook / AutoMode / DenyPolicy (+ with_deny_policy) from the crate root for the daemon to use. cargo build + cargo test green for smooth-operator-server and smooai-smooth-operator (210+ tests, 0 failed). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…y turn (#330) The engine core already supported auto-recall (AgentConfig::with_memory → build_context_messages → memory.recall(msg, 5)), but the server never wired it: StorageAdapter had checkpoints()/knowledge() but no memory accessor, and runner.rs built each AgentConfig without .with_memory(). Add StorageAdapter::memory_for_access(&access) -> Option<Arc<dyn Memory>> (default None → every backend byte-for-byte unchanged; hosted auto-recall stays a deliberate opt-in). runner.rs now calls .with_memory() when the adapter returns Some. The in-memory conformance adapter gains a with_memory() builder + override so the seam is exercised end-to-end (2 new injection_seams tests, 8/8 pass). Lights up Big Smooth: its SQLite adapter overrides memory_for_access to return its store, injecting remembered preferences into every turn. Claude-Session: https://claude.ai/code/session_017QkUKdJ4Xbx7WXV2FAg555 Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Turns were spawned detached (tokio::spawn, no handle) and the canonical WS protocol had no cancel message, so a turn that went off the rails could not be stopped: the user's only recourse was another message, which spawned a SECOND concurrent turn racing the first. That is the "Big Smooth is being weird and I can't stop it" report. AppState now tracks a per-conversation CancellationToken, registered BEFORE the turn is spawned so there is no window where a turn runs uninterruptibly. The turn task races the token against its turn future in a tokio::select!, so cancelling drops the turn future at its next await point and tears down the session's confirmation/interaction parks. Registrations carry a monotonic sequence number so a straggler turn's cleanup cannot disarm the Stop button for the turn that replaced it. The stopped turn closes out with a normal eventual_response on its OWN requestId, so clients that know nothing about `interrupt` still resolve their streaming state — no new event type, no client codegen needed.
A growing set of frontier models accept only their default temperature
and 400 the entire request:
Unsupported value: 'temperature' does not support 0 with this model.
Only the default (1) value is supported.
The symptom does not look like a config error. The server boots, accepts
the turn, every LLM call 400s, and the user sees an assistant that
silently says nothing. Downstream (Big Smooth) this made the whole model
picker a no-op: every model except the default produced an empty reply.
A per-model allowlist was the obvious fix and is provably wrong — the
behaviour does not follow the names. Measured against llm.smoo.ai:
rejects temperature 0 : gpt-5.1, gpt-5.4-pro, gpt-5.5,
claude-opus-4-7, claude-opus-4-8,
claude-sonnet-5, claude-fable-5
accepts it : gpt-5, gpt-5.2, gpt-5.4, claude-haiku-4-5,
claude-sonnet-4-5/4-6, claude-opus-4-6,
gemini-3.5-flash, deepseek-v4-flash/pro,
glm-5.1, minimax-m2.7, groq-gpt-oss-20b
gpt-5.1 rejects while gpt-5.2 accepts; gpt-5.4 accepts while
gpt-5.4-pro rejects. There is no prefix rule to write. 1.0 was accepted
by all 12 models tested across 6 families, so it is the single value
that works everywhere — at the cost of temperature-0 determinism on the
models that would allow it, which is a fair trade against "only the
default model works".
`llm_config_with_key` (handler.rs:1112) is the turn's config, so this is
the load-bearing site. Verified end-to-end against a real gateway: with
this change gpt-5.5 goes from producing nothing at all to PASSing a
Big Smooth conversation scenario 5/5/5/5 with zero LLM errors, and
claude-sonnet-5 likewise runs.
Branched from e9ce68c (the rev smooth pins) rather than main, so smooth
can consume the fix without a 1.23 -> 1.36 version jump.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P83HrAKaqp2ehxQcW7m6QU
🦋 Changeset detectedLatest commit: b8acb3b The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Not for merge into |
…ls (#334) Forward-port of the fix already shipped on the pinned lineage (b8acb3b, PR #333, branched from e9ce68c so smooth could consume it without a 1.23 -> 1.36 jump). This is the same change on main so future releases carry it. A growing set of frontier models accept only their default temperature and 400 the entire request: Unsupported value: 'temperature' does not support 0 with this model. Only the default (1) value is supported. The symptom does not look like a config error. The server boots, accepts the turn, every LLM call 400s, and the user sees an assistant that silently says nothing. Downstream this made Big Smooth's entire model picker a no-op — every model except the default returned an empty reply. A per-model allowlist was the obvious fix and is provably wrong; the behaviour does not follow the names. Measured against llm.smoo.ai: rejects temperature 0 : gpt-5.1, gpt-5.4-pro, gpt-5.5, claude-opus-4-7, claude-opus-4-8, claude-sonnet-5, claude-fable-5 accepts it : gpt-5, gpt-5.2, gpt-5.4, claude-haiku-4-5, claude-sonnet-4-5/4-6, claude-opus-4-6, gemini-3.5-flash, deepseek-v4-flash/pro, glm-5.1, minimax-m2.7, groq-gpt-oss-20b gpt-5.1 rejects while gpt-5.2 accepts; gpt-5.4 accepts while gpt-5.4-pro rejects. 1.0 was accepted by all 12 models tested across 6 families. Verified end-to-end on the pinned lineage against a real gateway: gpt-5.5 went from producing nothing at all to passing Big Smooth conversation scenarios 5/5/5/5 with zero LLM errors. 112 server tests pass. Claude-Session: https://claude.ai/code/session_01P83HrAKaqp2ehxQcW7m6QU Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Superseded — all four things on this branch are resolved, so there is nothing left to extract. This is a mixed branch: the title names the temperature fix, but the diff carries 26 files and three unrelated changesets. Judged against current main, piece by piece:
On that last row: If anyone still wants Closing. Triage pearl th-b093c7. |
|
Closing this PR is only about the PR — per the pin-anchor note above, The close is in fact consistent with that note — "not for merge into |
The bug
A growing set of frontier models accept only their default temperature and 400 the entire request:
The symptom doesn't look like a config error. The server boots, accepts the turn, every LLM call 400s, and the user sees an assistant that silently says nothing. Downstream in Big Smooth this made the whole model picker a no-op — every model except the default produced an empty reply.
Why not a per-model allowlist
Provably wrong. Measured against llm.smoo.ai on 2026-08-07:
temperature: 0gpt-5.1,gpt-5.4-pro,gpt-5.5gpt-5,gpt-5.2,gpt-5.4claude-opus-4-7,claude-opus-4-8,claude-sonnet-5,claude-fable-5claude-haiku-4-5,claude-sonnet-4-5/4-6,claude-opus-4-6gemini-3.5-flash,deepseek-v4-flash/pro,glm-5.1,minimax-m2.7,groq-gpt-oss-20bgpt-5.1rejects whilegpt-5.2accepts.gpt-5.4accepts whilegpt-5.4-prorejects. There is no prefix rule to write.1.0was accepted by all 12 models tested across 6 families — the single value that works everywhere. Cost: temperature-0 determinism on models that would allow it. Fair trade against "only the default model works at all".Verification
llm_config_with_key(handler.rs:1112) is the turn's config, so this is the load-bearing site. Verified end-to-end against a real gateway from this exact commit:gpt-5.5: produced nothing at all → now PASSes two Big Smooth conversation scenarios at 5/5/5/5, zero LLM errors.claude-sonnet-5: now runs and passes.claude-opus-4-8: now runs and fails a scenario on merit (fabrication) rather than on infrastructure — real signal instead of an empty reply.112 server tests pass. The long-term shape is
Option<f32>onLlmConfigso we send nothing and take each provider's default.🤖 Generated with Claude Code
https://claude.ai/code/session_01P83HrAKaqp2ehxQcW7m6QU