fix(catalog): omitted forced tool choice for go responses
Applied OpenCode Go DeepSeek tool-choice compat to both OpenAI APIs, so the Responses route drops named selectors while preserving tools. Added generated-policy and request-payload regression coverage. Fixes #8243
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { describe, expect, it } from "bun:test";
|
||||
import { type } from "@oh-my-pi/omptype";
|
||||
import { streamOpenAICompletions } from "@oh-my-pi/pi-ai/providers/openai-completions";
|
||||
import { type OpenAICompletionsOptions, streamOpenAICompletions } from "@oh-my-pi/pi-ai/providers/openai-completions";
|
||||
import { type OpenAIResponsesOptions, streamOpenAIResponses } from "@oh-my-pi/pi-ai/providers/openai-responses";
|
||||
import type { Context, Model, Tool } from "@oh-my-pi/pi-ai/types";
|
||||
import { getBundledModel } from "@oh-my-pi/pi-catalog/models";
|
||||
|
||||
@@ -23,7 +24,7 @@ function abortedSignal(): AbortSignal {
|
||||
|
||||
async function capturePayload(
|
||||
model: Model<"openai-completions">,
|
||||
opts: Parameters<typeof streamOpenAICompletions>[2],
|
||||
opts: OpenAICompletionsOptions,
|
||||
): Promise<Record<string, unknown>> {
|
||||
const { promise, resolve } = Promise.withResolvers<unknown>();
|
||||
streamOpenAICompletions(model, context, {
|
||||
@@ -35,12 +36,36 @@ async function capturePayload(
|
||||
return (await promise) as Record<string, unknown>;
|
||||
}
|
||||
|
||||
async function captureResponsesPayload(
|
||||
model: Model<"openai-responses">,
|
||||
opts: OpenAIResponsesOptions,
|
||||
): Promise<Record<string, unknown>> {
|
||||
const { promise, resolve } = Promise.withResolvers<unknown>();
|
||||
streamOpenAIResponses(model, context, {
|
||||
...opts,
|
||||
apiKey: "test-key",
|
||||
signal: abortedSignal(),
|
||||
onPayload: payload => resolve(payload),
|
||||
});
|
||||
return (await promise) as Record<string, unknown>;
|
||||
}
|
||||
|
||||
describe("OpenCode Go tool_choice compatibility", () => {
|
||||
it("marks deepseek-v4-pro as not supporting tool_choice via compat override", () => {
|
||||
const model = getBundledModel("opencode-go", "deepseek-v4-pro") as Model<"openai-completions">;
|
||||
expect(model.compat?.supportsToolChoice).toBe(false);
|
||||
});
|
||||
|
||||
it("omits forced tool_choice from DeepSeek Flash Responses payloads while preserving tools", async () => {
|
||||
const model = getBundledModel("opencode-go", "deepseek-v4-flash") as Model<"openai-responses">;
|
||||
expect(model.compat.supportsToolChoice).toBe(false);
|
||||
const body = await captureResponsesPayload(model, {
|
||||
toolChoice: { type: "tool", name: "echo" },
|
||||
});
|
||||
expect(body.tools).toEqual([expect.objectContaining({ type: "function", name: "echo" })]);
|
||||
expect(body.tool_choice).toBeUndefined();
|
||||
});
|
||||
|
||||
it("marks mimo-v2.5-pro as not supporting tool_choice via compat override", () => {
|
||||
const model = getBundledModel("opencode-go", "mimo-v2.5-pro") as Model<"openai-completions">;
|
||||
expect(model.compat?.supportsToolChoice).toBe(false);
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed `opencode-go/deepseek-v4-flash` Responses requests sending forced named `tool_choice` selectors that Console Go rejects while thinking mode is active.
|
||||
|
||||
## [17.2.12] - 2026-08-08
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -391,7 +391,7 @@ function applyGeneratedModelPolicy(model: ModelSpec<Api>): void {
|
||||
};
|
||||
}
|
||||
if (
|
||||
model.api === "openai-completions" &&
|
||||
(model.api === "openai-completions" || model.api === "openai-responses") &&
|
||||
model.provider === "opencode-go" &&
|
||||
(model.id === "deepseek-v4-flash" || model.id === "deepseek-v4-pro")
|
||||
) {
|
||||
|
||||
+720
-207
File diff suppressed because it is too large
Load Diff
@@ -319,11 +319,11 @@ describe("generated model policies", () => {
|
||||
expect(models[0]?.compat?.supportsToolChoice).toBe(false);
|
||||
});
|
||||
|
||||
it("sets OpenCode Go DeepSeek V4 tool-call request compat", () => {
|
||||
const models: ModelSpec<"openai-completions">[] = [
|
||||
it("sets OpenCode Go DeepSeek V4 tool-call request compat for both OpenAI APIs", () => {
|
||||
const models: ModelSpec<Api>[] = [
|
||||
createSpec({
|
||||
id: "deepseek-v4-flash",
|
||||
api: "openai-completions",
|
||||
api: "openai-responses",
|
||||
provider: "opencode-go",
|
||||
}),
|
||||
createSpec({
|
||||
|
||||
Reference in New Issue
Block a user