Skip to content

Commit afd75ca

Browse files
committed
fix(provider): wire reasoningEffort through OpenCode Go and Zen responses
1 parent afa244c commit afd75ca

5 files changed

Lines changed: 166 additions & 1 deletion

File tree

‎src/config.test.ts‎

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { join, resolve } from "node:path";
1414
import {
1515
buildBifrostSource,
1616
buildGoSource,
17+
buildZenSource,
1718
buildOpenAISource,
1819
buildXaiSource,
1920
buildProviderCatalog,
@@ -1697,6 +1698,77 @@ describe("buildGoSource", () => {
16971698
});
16981699
}
16991700
});
1701+
1702+
test("forwards reasoningEffort into defaults.providerOptions.reasoning_effort on responses models and omits when undefined", () => {
1703+
for (const model of [
1704+
"muse-spark-1.3-contributor",
1705+
"grok-4.6",
1706+
"gpt-5.6-luna",
1707+
]) {
1708+
const withEffort = buildGoSource({
1709+
id: "opencode-go",
1710+
apiKey: "sk-go",
1711+
model,
1712+
sessionId: "sess-1",
1713+
reasoningEffort: "low",
1714+
});
1715+
expect(withEffort.provider).toBe("openai-responses");
1716+
expect(withEffort.defaults?.providerOptions).toEqual({
1717+
openaiSessionId: "sess-1",
1718+
opencodeSessionId: "sess-1",
1719+
reasoning_effort: "low",
1720+
});
1721+
1722+
const unset = buildGoSource({
1723+
id: "opencode-go",
1724+
apiKey: "sk-go",
1725+
model,
1726+
sessionId: "sess-1",
1727+
});
1728+
expect(unset.provider).toBe("openai-responses");
1729+
expect(unset.defaults?.providerOptions).toEqual({
1730+
openaiSessionId: "sess-1",
1731+
opencodeSessionId: "sess-1",
1732+
});
1733+
}
1734+
});
1735+
});
1736+
1737+
describe("buildZenSource", () => {
1738+
test("forwards reasoningEffort into defaults.providerOptions.reasoning_effort on responses models and omits when undefined", () => {
1739+
for (const model of [
1740+
"muse-spark-1.3-contributor-free",
1741+
"muse-spark-1.3",
1742+
"grok-4.6",
1743+
"gpt-5.6-luna",
1744+
]) {
1745+
const withEffort = buildZenSource({
1746+
id: "zen",
1747+
apiKey: "sk-zen",
1748+
model,
1749+
sessionId: "sess-1",
1750+
reasoningEffort: "low",
1751+
});
1752+
expect(withEffort.provider).toBe("openai-responses");
1753+
expect(withEffort.defaults?.providerOptions).toEqual({
1754+
openaiSessionId: "sess-1",
1755+
opencodeSessionId: "sess-1",
1756+
reasoning_effort: "low",
1757+
});
1758+
1759+
const unset = buildZenSource({
1760+
id: "zen",
1761+
apiKey: "sk-zen",
1762+
model,
1763+
sessionId: "sess-1",
1764+
});
1765+
expect(unset.provider).toBe("openai-responses");
1766+
expect(unset.defaults?.providerOptions).toEqual({
1767+
openaiSessionId: "sess-1",
1768+
opencodeSessionId: "sess-1",
1769+
});
1770+
}
1771+
});
17001772
});
17011773

17021774
describe("buildOpenAISource", () => {

‎src/config/index.ts‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,9 @@ export function buildGoSource(fields: {
575575
providerOptions: {
576576
[OPENAI_SESSION_ID_OPTION]: fields.sessionId,
577577
[OPENCODE_SESSION_ID_OPTION]: fields.sessionId,
578+
...(fields.reasoningEffort !== undefined
579+
? { reasoning_effort: fields.reasoningEffort }
580+
: {}),
578581
},
579582
},
580583
};
@@ -642,6 +645,9 @@ export function buildZenSource(fields: {
642645
providerOptions: {
643646
[OPENAI_SESSION_ID_OPTION]: fields.sessionId,
644647
[OPENCODE_SESSION_ID_OPTION]: fields.sessionId,
648+
...(fields.reasoningEffort !== undefined
649+
? { reasoning_effort: fields.reasoningEffort }
650+
: {}),
645651
},
646652
},
647653
};

‎src/config/inference-sources.test.ts‎

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,77 @@ describe("reasoning effort on the wire", () => {
820820
);
821821
});
822822

823+
test("forwards reasoning effort on OpenCode Go and Zen openai-responses sources", () => {
824+
const goCatalog: ProviderCatalogEntry[] = [
825+
{
826+
name: "opencode-go",
827+
baseURL: "https://opencode.ai/zen/go/v1",
828+
apiKey: "sk-go",
829+
models: ["muse-spark-1.3-contributor", "grok-4.6", "gpt-5.6-luna"],
830+
opencodeGo: true,
831+
},
832+
];
833+
for (const model of [
834+
"muse-spark-1.3-contributor",
835+
"grok-4.6",
836+
"gpt-5.6-luna",
837+
]) {
838+
const withEffort = buildInferenceSourceForRef(
839+
{ provider: "opencode-go", model, reasoningEffort: "low" },
840+
{ sessionId: "s1", catalog: goCatalog },
841+
undefined,
842+
);
843+
expect(withEffort?.provider).toBe(OPENAI_RESPONSES_PROVIDER);
844+
expect(withEffort?.defaults?.providerOptions).toMatchObject({
845+
reasoning_effort: "low",
846+
});
847+
848+
const unset = buildInferenceSourceForRef(
849+
{ provider: "opencode-go", model },
850+
{ sessionId: "s1", catalog: goCatalog },
851+
undefined,
852+
);
853+
expect(unset?.provider).toBe(OPENAI_RESPONSES_PROVIDER);
854+
expect(unset?.defaults?.providerOptions).not.toHaveProperty(
855+
"reasoning_effort",
856+
);
857+
}
858+
859+
const zenResponsesCatalog: ProviderCatalogEntry[] = [
860+
{
861+
name: ZEN_PROVIDER_ID,
862+
baseURL: ZEN_DEFAULT_BASE_URL,
863+
apiKey: "zen-key",
864+
models: ["muse-spark-1.3-contributor-free", "grok-4.6", "gpt-5.6-luna"],
865+
},
866+
];
867+
for (const model of [
868+
"muse-spark-1.3-contributor-free",
869+
"grok-4.6",
870+
"gpt-5.6-luna",
871+
]) {
872+
const withEffort = buildInferenceSourceForRef(
873+
{ provider: ZEN_PROVIDER_ID, model, reasoningEffort: "low" },
874+
{ sessionId: "s1", catalog: zenResponsesCatalog },
875+
undefined,
876+
);
877+
expect(withEffort?.provider).toBe(OPENAI_RESPONSES_PROVIDER);
878+
expect(withEffort?.defaults?.providerOptions).toMatchObject({
879+
reasoning_effort: "low",
880+
});
881+
882+
const unset = buildInferenceSourceForRef(
883+
{ provider: ZEN_PROVIDER_ID, model },
884+
{ sessionId: "s1", catalog: zenResponsesCatalog },
885+
undefined,
886+
);
887+
expect(unset?.provider).toBe(OPENAI_RESPONSES_PROVIDER);
888+
expect(unset?.defaults?.providerOptions).not.toHaveProperty(
889+
"reasoning_effort",
890+
);
891+
}
892+
});
893+
823894
test("a hand-edited first-class row does not leak the operator ladder", () => {
824895
// The ladder override is a custom-OpenAI-compatible-provider concept. A
825896
// first-class row (OpenCode Go here) that somehow carries reasoningEfforts

‎src/provider/openai-responses.test.ts‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,22 @@ describe("openai-responses buildRequest", () => {
7070
) as Record<string, unknown>;
7171
expect(body).not.toHaveProperty("prompt_cache_key");
7272
});
73+
74+
test("serializes reasoning.effort from providerOptions.reasoning_effort", () => {
75+
const body = JSON.parse(
76+
adapter().buildRequest([userTurn("hi")], "gpt-5.6-luna", {
77+
providerOptions: { reasoning_effort: "low" },
78+
}).body,
79+
) as Record<string, unknown>;
80+
expect(body["reasoning"]).toEqual({ summary: "auto", effort: "low" });
81+
});
82+
83+
test("omits reasoning.effort when providerOptions.reasoning_effort is absent", () => {
84+
const body = JSON.parse(
85+
adapter().buildRequest([userTurn("hi")], "gpt-5.6-luna", {}).body,
86+
) as Record<string, unknown>;
87+
expect(body["reasoning"]).toEqual({ summary: "auto" });
88+
});
7389
});
7490

7591
describe("openai-responses promotion cache safety", () => {

‎src/provider/openai-responses.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export const hostQuirks: ResponsesQuirks = {
2121
},
2222
systemPrompt: { role: "system", shape: "string" },
2323
contentShape: "flat",
24-
reasoning: { summary: "auto" },
24+
reasoning: { summary: "auto", effortOption: "reasoning_effort" },
2525
};
2626

2727
export const createOpenAIResponsesAdapter: AdapterFactory =

0 commit comments

Comments
 (0)