mirror of
https://github.com/Nezumi-2711/9router.git
synced 2026-09-22 13:38:31 +00:00
fix(kiro): map GPT reasoning effort fields
GPT-5.6 via Kiro needs reasoning.effort while Claude uses output_config.effort. Resolve the effort path per-model schema (like Kiro CLI/KAS) so GPT-5.6 receives the correct structured thinking level. Claude path unchanged. - Add resolveKiroEffortPath returning "reasoning" | "output_config" | null - buildKiroAdditionalModelRequestFields emits schema-specific shape - Keep prompt tags for backward compatibility - Add OpenAI/Claude translator coverage for GPT-5.6 effort mapping
This commit is contained in:
@@ -144,9 +144,13 @@ export function extractKiroEffortLevel(body) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildKiroAdditionalModelRequestFields(body) {
|
export function buildKiroAdditionalModelRequestFields(body, effortPath = "output_config") {
|
||||||
const effort = extractKiroEffortLevel(body);
|
const effort = extractKiroEffortLevel(body);
|
||||||
if (!effort) return undefined;
|
if (!effort) return undefined;
|
||||||
|
if (effortPath === "reasoning") {
|
||||||
|
// Mirrors Kiro CLI/KAS buildEffortRequestFields("reasoning") for GPT.
|
||||||
|
return { reasoning: { effort } };
|
||||||
|
}
|
||||||
// Mirrors Kiro CLI/KAS buildEffortRequestFields("output_config").
|
// Mirrors Kiro CLI/KAS buildEffortRequestFields("output_config").
|
||||||
return {
|
return {
|
||||||
thinking: { type: "adaptive", display: "summarized" },
|
thinking: { type: "adaptive", display: "summarized" },
|
||||||
@@ -154,12 +158,15 @@ export function buildKiroAdditionalModelRequestFields(body) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function supportsKiroAdditionalModelRequestFields(model) {
|
export function resolveKiroEffortPath(model) {
|
||||||
if (typeof model !== "string") return false;
|
if (typeof model !== "string") return null;
|
||||||
const normalized = model.toLowerCase().replace(/-/g, ".");
|
const normalized = model.toLowerCase().replace(/-/g, ".");
|
||||||
if (!normalized.includes("claude")) return false;
|
if (/(?:^|[/.])gpt[/.]5[/.]6(?:[/.]|$)/.test(normalized)) {
|
||||||
|
return "reasoning";
|
||||||
|
}
|
||||||
|
if (!normalized.includes("claude")) return null;
|
||||||
const match = normalized.match(/(?:^|[/.])claude(?:[/.][a-z]+)*[/.](\d+)(?:[/.](\d+))?(?:[/.]|$)/);
|
const match = normalized.match(/(?:^|[/.])claude(?:[/.][a-z]+)*[/.](\d+)(?:[/.](\d+))?(?:[/.]|$)/);
|
||||||
if (!match) return false;
|
if (!match) return null;
|
||||||
const [, majorText, minorText] = match;
|
const [, majorText, minorText] = match;
|
||||||
const major = Number(majorText);
|
const major = Number(majorText);
|
||||||
const minor = minorText === undefined ? null : Number(minorText);
|
const minor = minorText === undefined ? null : Number(minorText);
|
||||||
@@ -167,12 +174,19 @@ export function supportsKiroAdditionalModelRequestFields(model) {
|
|||||||
// Kiro rejected additionalModelRequestFields on legacy 4.5 models in live smoke.
|
// Kiro rejected additionalModelRequestFields on legacy 4.5 models in live smoke.
|
||||||
// Default future Claude/Kiro models to supported so new model releases do not
|
// Default future Claude/Kiro models to supported so new model releases do not
|
||||||
// need a code allowlist update.
|
// need a code allowlist update.
|
||||||
return !(major < 4 || (major === 4 && (minor === null || minor <= 5 || dateSuffixMinor)));
|
return major < 4 || (major === 4 && (minor === null || minor <= 5 || dateSuffixMinor))
|
||||||
|
? null
|
||||||
|
: "output_config";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function supportsKiroAdditionalModelRequestFields(model) {
|
||||||
|
return resolveKiroEffortPath(model) !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildKiroAdditionalModelRequestFieldsForModel(body, model) {
|
export function buildKiroAdditionalModelRequestFieldsForModel(body, model) {
|
||||||
if (!supportsKiroAdditionalModelRequestFields(model)) return undefined;
|
const effortPath = resolveKiroEffortPath(model);
|
||||||
return buildKiroAdditionalModelRequestFields(body);
|
if (!effortPath) return undefined;
|
||||||
|
return buildKiroAdditionalModelRequestFields(body, effortPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -511,12 +511,10 @@ function convertMessages(messages, tools, model) {
|
|||||||
* Kiro's 2-3 minute server timeout. The suffix is stripped before being
|
* Kiro's 2-3 minute server timeout. The suffix is stripped before being
|
||||||
* sent upstream.
|
* sent upstream.
|
||||||
*
|
*
|
||||||
* 2. Thinking / reasoning. Kiro does not accept `thinking.type` or
|
* 2. Thinking / reasoning. Detection covers Anthropic-Beta header, Claude API
|
||||||
* `reasoning_effort` natively. The only way to enable reasoning is to
|
|
||||||
* inject `<thinking_mode>enabled</thinking_mode>` into the user content
|
|
||||||
* sent upstream. Detection covers Anthropic-Beta header, Claude API
|
|
||||||
* `thinking`, OpenAI `reasoning_effort`, AMP/Cursor magic tags, and model
|
* `thinking`, OpenAI `reasoning_effort`, AMP/Cursor magic tags, and model
|
||||||
* name hints.
|
* name hints. Kiro's prompt tags remain for compatibility, while supported
|
||||||
|
* models also receive the same schema-specific effort fields as Kiro CLI.
|
||||||
*/
|
*/
|
||||||
export function openaiToKiroRequest(model, body, stream, credentials) {
|
export function openaiToKiroRequest(model, body, stream, credentials) {
|
||||||
const messages = body.messages || [];
|
const messages = body.messages || [];
|
||||||
|
|||||||
@@ -112,6 +112,17 @@ describe("Claude → Kiro (direct route)", () => {
|
|||||||
expect(out.systemPrompt).toContain("<max_thinking_length>24576</max_thinking_length>");
|
expect(out.systemPrompt).toContain("<max_thinking_length>24576</max_thinking_length>");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("maps Claude-format effort to GPT-5.6 Kiro CLI reasoning fields", () => {
|
||||||
|
const out = C2K({
|
||||||
|
output_config: { effort: "low" },
|
||||||
|
messages: [{ role: "user", content: "think lightly" }],
|
||||||
|
}, null, "gpt-5.6-sol");
|
||||||
|
|
||||||
|
expect(out.additionalModelRequestFields).toEqual({
|
||||||
|
reasoning: { effort: "low" },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("sends Claude system as top-level systemPrompt and keeps a user-content fallback", () => {
|
it("sends Claude system as top-level systemPrompt and keeps a user-content fallback", () => {
|
||||||
const out = C2K({
|
const out = C2K({
|
||||||
system: "system-only instruction",
|
system: "system-only instruction",
|
||||||
|
|||||||
@@ -316,6 +316,44 @@ describe("openaiToKiroRequest", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("maps GPT-5.6 reasoning.effort high to Kiro CLI reasoning fields", () => {
|
||||||
|
const body = {
|
||||||
|
reasoning: { effort: "high" },
|
||||||
|
messages: [{ role: "user", content: "Think deeply" }]
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = openaiToKiroRequest("gpt-5.6-sol", body, true, {});
|
||||||
|
|
||||||
|
expect(systemPromptOf(result)).toContain("<max_thinking_length>24576</max_thinking_length>");
|
||||||
|
expect(result.additionalModelRequestFields).toEqual({
|
||||||
|
reasoning: { effort: "high" },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("maps prefixed GPT-5.6 reasoning_effort medium to Kiro CLI reasoning fields", () => {
|
||||||
|
const body = {
|
||||||
|
reasoning_effort: "medium",
|
||||||
|
messages: [{ role: "user", content: "Think normally" }]
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = openaiToKiroRequest("kiro/gpt-5.6-terra", body, true, {});
|
||||||
|
|
||||||
|
expect(result.additionalModelRequestFields).toEqual({
|
||||||
|
reasoning: { effort: "medium" },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not forward unsupported GPT-5.6 effort values", () => {
|
||||||
|
const body = {
|
||||||
|
reasoning: { effort: "ultra" },
|
||||||
|
messages: [{ role: "user", content: "Unknown effort" }]
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = openaiToKiroRequest("gpt-5.6-luna", body, true, {});
|
||||||
|
|
||||||
|
expect(result.additionalModelRequestFields).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
it("does not send additionalModelRequestFields for legacy Kiro model ids", () => {
|
it("does not send additionalModelRequestFields for legacy Kiro model ids", () => {
|
||||||
const body = {
|
const body = {
|
||||||
reasoning_effort: "high",
|
reasoning_effort: "high",
|
||||||
|
|||||||
Reference in New Issue
Block a user