Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions src/harness/pi-harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1034,6 +1034,22 @@ const FAST_MODE_BETA = "fast-mode-2026-02-01";

export { modelSupportsFastMode } from "../model/pi-models.ts";

/**
* Whether a turn should run in fast mode.
*
* Fast mode is OPT-IN: only an explicit `true` selects it. An unset `fastMode` means the
* caller expressed no preference, and treating that as "yes" bills the turn against a tier
* it never asked for — or fails it outright on an organization with no fast-mode quota,
* where the provider answers `rate_limit_error: … 0 fast mode input tokens per minute`.
*
* Only the web UI ever sets the field today, so every other entry point (CLI, API clients,
* integrations) leaves it undefined. `claude-harness` already reads it as opt-in
* (`turn.fastMode && …`); this keeps both harnesses agreeing on the same default.
*/
export function wantsFastMode(fastMode: boolean | undefined, modelId: string | undefined): boolean {
return fastMode === true && modelSupportsFastMode(modelId);
}

export const TURN_PROVIDER_EFFORT_ALIASES: Record<string, string | null> = {
max: "max",
ultracode: "max",
Expand Down Expand Up @@ -1447,7 +1463,7 @@ export function createPiHarness(opts?: PiHarnessOptions): Harness {
entry.ref.toolApprovalGate = turn.toolApprovalGate;

const desiredModelId = turn.model ?? resolveModelId(turn.scopeLabel);
const wantFast = turn.fastMode !== false && modelSupportsFastMode(desiredModelId);
const wantFast = wantsFastMode(turn.fastMode, desiredModelId);
const current = entry.agentSession.model as { id?: string; headers?: Record<string, string> } | undefined;
const currentFast = Boolean(current?.headers?.["anthropic-beta"]?.includes(FAST_MODE_BETA));
if (current?.id !== desiredModelId || currentFast !== wantFast) {
Expand Down Expand Up @@ -1707,7 +1723,7 @@ export function createPiHarness(opts?: PiHarnessOptions): Harness {
console.error(
`[pi] provider refusal — retrying on fallback model ${fromId} -> ${fallbackId} session=${turn.session.id}: ${refusal}`,
);
const wantFast = turn.fastMode !== false && modelSupportsFastMode(fallbackId);
const wantFast = wantsFastMode(turn.fastMode, fallbackId);
await entry.agentSession.setModel(wantFast ? withFastModeHeaders(fallback) : fallback);
const active = entry.agentSession.model as { headers?: Record<string, string> } | undefined;
entry.ref.fast = Boolean(active?.headers?.["anthropic-beta"]?.includes(FAST_MODE_BETA));
Expand Down
22 changes: 21 additions & 1 deletion test/pi-harness-fast-mode.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { test } from "node:test";
import assert from "node:assert/strict";
import { applyFastSpeed, modelSupportsFastMode, TURN_PROVIDER_EFFORT_ALIASES } from "../src/harness/pi-harness.ts";
import {
applyFastSpeed,
modelSupportsFastMode,
wantsFastMode,
TURN_PROVIDER_EFFORT_ALIASES,
} from "../src/harness/pi-harness.ts";
import { defaultInteractiveThinkingLevel } from "../src/model/pi-models.ts";

test("modelSupportsFastMode allows only the documented direct Opus ids", () => {
Expand Down Expand Up @@ -50,3 +55,18 @@ test("defaultInteractiveThinkingLevel keeps human turns light by provider", () =
assert.equal(defaultInteractiveThinkingLevel({ provider: "anthropic", api: "anthropic-messages" }), "low");
assert.equal(defaultInteractiveThinkingLevel({ provider: "openai", api: "openai-responses" }), "auto");
});

test("fast mode is opt-in: only an explicit true selects it", () => {
// A turn that never mentions fastMode has expressed no preference. Reading that as "yes"
// bills it against a tier nobody asked for, and on an organization with no fast-mode
// quota the provider rejects every such turn outright.
assert.equal(wantsFastMode(undefined, "claude-opus-5"), false, "unset must not select fast mode");
assert.equal(wantsFastMode(false, "claude-opus-5"), false);
assert.equal(wantsFastMode(true, "claude-opus-5"), true, "an explicit opt-in is honoured");
});

test("an explicit opt-in still cannot select fast mode on a model that lacks it", () => {
assert.equal(wantsFastMode(true, "claude-sonnet-5"), false);
assert.equal(wantsFastMode(true, undefined), false);
assert.equal(wantsFastMode(true, ""), false);
});