mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
fix(agents): preserve native runtime controls on Codex routes (#107588)
* fix(agents): preserve Codex runtime controls * fix(agents): cover shipped fast cutoff aliases * fix(agents): validate native runtime control values * refactor(agents): simplify fast mode key matching --------- Co-authored-by: VACInc <3279061+VACInc@users.noreply.github.com> Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
@@ -1503,6 +1503,35 @@ describe("selectAgentHarness", () => {
|
||||
},
|
||||
);
|
||||
|
||||
it("keeps native model run controls compatible with Codex", () => {
|
||||
expect(
|
||||
buildAgentHarnessSupportContext({
|
||||
provider: "openai",
|
||||
modelId: "gpt-5.6-sol",
|
||||
modelProvider: {
|
||||
api: "openai-responses",
|
||||
baseUrl: "https://api.openai.com/v1",
|
||||
requestTransportOverrides: "none",
|
||||
},
|
||||
requestedRuntime: "codex",
|
||||
config: {
|
||||
agents: {
|
||||
defaults: {
|
||||
models: {
|
||||
"openai/gpt-5.6-sol": {
|
||||
params: { thinking: "xhigh", fastMode: true, fastAutoOnSeconds: 30 },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig,
|
||||
}).modelProvider,
|
||||
).toMatchObject({
|
||||
requestTransportOverrides: "none",
|
||||
runtimePolicy: { compatibleIds: ["openclaw", "codex"] },
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects explicit Codex when agent request params cannot be reproduced", () => {
|
||||
const supports = vi.fn((ctx: Parameters<AgentHarness["supports"]>[0]) =>
|
||||
ctx.modelProvider?.requestTransportOverrides === "present"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { normalizeFastMode } from "@openclaw/normalization-core/string-coerce";
|
||||
import { normalizeThinkLevel } from "../auto-reply/thinking.shared.js";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { modelKey } from "../shared/model-key.js";
|
||||
import { resolveAgentConfig } from "./agent-scope-config.js";
|
||||
@@ -8,6 +10,35 @@ type ModelExtraParamSources = {
|
||||
agentParams?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
const FAST_MODE_CUTOFF_MODEL_PARAM_KEYS = new Set([
|
||||
"fastAutoOnSeconds",
|
||||
"fastSeconds",
|
||||
"fast_auto_on_seconds",
|
||||
"fast_seconds",
|
||||
]);
|
||||
|
||||
// Native harnesses receive recognized values as typed run controls. Other value
|
||||
// shapes with the same keys remain authored provider request parameters.
|
||||
function isAgentRuntimeModelParam(key: string, value: unknown): boolean {
|
||||
if (key === "thinking") {
|
||||
return (
|
||||
value === false ||
|
||||
value === "disabled" ||
|
||||
value === "none" ||
|
||||
(typeof value === "string" && normalizeThinkLevel(value) !== undefined)
|
||||
);
|
||||
}
|
||||
if (key === "fastMode" || key === "fast_mode") {
|
||||
return normalizeFastMode(value) !== undefined;
|
||||
}
|
||||
return (
|
||||
FAST_MODE_CUTOFF_MODEL_PARAM_KEYS.has(key) &&
|
||||
typeof value === "number" &&
|
||||
Number.isInteger(value) &&
|
||||
value > 0
|
||||
);
|
||||
}
|
||||
|
||||
function legacyModelKey(provider: string, modelId: string): string | undefined {
|
||||
const rawKey = `${provider.trim()}/${modelId.trim()}`;
|
||||
const canonicalKey = modelKey(provider, modelId);
|
||||
@@ -36,12 +67,19 @@ export function resolveModelExtraParamSources(params: {
|
||||
return { defaultParams, modelParams, agentParams };
|
||||
}
|
||||
|
||||
/** Returns whether embedded OpenClaw would apply authored request parameters. */
|
||||
/** Returns whether embedded OpenClaw would apply authored provider request parameters. */
|
||||
export function hasModelExtraParams(
|
||||
params: Parameters<typeof resolveModelExtraParamSources>[0],
|
||||
): boolean {
|
||||
const sources = resolveModelExtraParamSources(params);
|
||||
return [sources.defaultParams, sources.modelParams, sources.agentParams].some(
|
||||
(source) => source !== undefined && Object.keys(source).length > 0,
|
||||
if (
|
||||
[sources.defaultParams, sources.agentParams].some(
|
||||
(source) => source !== undefined && Object.keys(source).length > 0,
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return Object.entries(sources.modelParams ?? {}).some(
|
||||
([key, value]) => !isAgentRuntimeModelParam(key, value),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,62 @@ describe("OpenAI runtime routing policy", () => {
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it.each([
|
||||
["thinking", { thinking: "xhigh" }],
|
||||
["fastMode", { fastMode: true }],
|
||||
["fast_mode", { fast_mode: true }],
|
||||
["fastAutoOnSeconds", { fastMode: "auto", fastAutoOnSeconds: 30 }],
|
||||
["fast_auto_on_seconds", { fastMode: "auto", fast_auto_on_seconds: 30 }],
|
||||
["fastSeconds", { fastMode: "auto", fastSeconds: 30 }],
|
||||
["fast_seconds", { fastMode: "auto", fast_seconds: 30 }],
|
||||
])("keeps Codex for model-scoped %s controls", (_label, params) => {
|
||||
const config = {
|
||||
agents: {
|
||||
defaults: {
|
||||
models: {
|
||||
"openai/gpt-5.6-sol": {
|
||||
params,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
|
||||
expect(
|
||||
resolveOpenAIImplicitAgentRuntime({
|
||||
provider: "openai",
|
||||
modelId: "gpt-5.6-sol",
|
||||
config,
|
||||
env: {},
|
||||
}),
|
||||
).toBe("codex");
|
||||
});
|
||||
|
||||
it.each([
|
||||
["provider-native thinking", { thinking: { type: "enabled", budget_tokens: 2_048 } }],
|
||||
["invalid fast mode", { fastMode: { enabled: true } }],
|
||||
["invalid fast cutoff", { fastAutoOnSeconds: "30" }],
|
||||
])("keeps %s values on the OpenClaw runtime", (_label, params) => {
|
||||
const config = {
|
||||
agents: {
|
||||
defaults: {
|
||||
models: {
|
||||
"openai/gpt-5.6-sol": { params },
|
||||
},
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
|
||||
expect(
|
||||
resolveOpenAIImplicitAgentRuntime({
|
||||
provider: "openai",
|
||||
modelId: "gpt-5.6-sol",
|
||||
config,
|
||||
env: {},
|
||||
}),
|
||||
).toBe("openclaw");
|
||||
});
|
||||
|
||||
it("maps provider route facts onto a closed implicit runtime", () => {
|
||||
expect(
|
||||
resolveOpenAIImplicitAgentRuntime({ provider: "openai", modelId: "gpt-5.6", env: {} }),
|
||||
|
||||
Reference in New Issue
Block a user