Skip to content
Open
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
99 changes: 99 additions & 0 deletions packages/omo-opencode/src/plugin/chat-params.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,4 +248,103 @@ describe("createChatParamsHandler", () => {
//#then
expect(output.maxOutputTokens).toBe(4096)
})

test("strips reasoningEffort for openai-compatible providers (issue #5529)", async () => {
//#given — gpt-5.5 agent config injects reasoningEffort: "medium", but the provider
// is openai-compatible (e.g. local llama.cpp, vLLM, salad-cloud) and OpenAI
// rejects requests that combine tools + reasoning_effort on /v1/chat/completions
setSessionPromptParams("ses_chat_params_compat", {
options: {
reasoningEffort: "medium",
},
})

const handler = createChatParamsHandler()

const input = {
sessionID: "ses_chat_params_compat",
agent: { name: "oracle" },
model: {
providerID: "salad-cloud",
modelID: "gpt-5.5",
api: { npm: "@ai-sdk/openai-compatible" },
},
provider: { id: "salad-cloud" },
message: {},
}

const output: ChatParamsOutput = {
options: {},
}

//#when
await handler(input, output)

//#then — reasoningEffort must NOT be injected for openai-compatible providers
expect(output.options).not.toHaveProperty("reasoningEffort")
})

test("preserves reasoningEffort for native openai provider (issue #5529)", async () => {
//#given — same agent config, but native openai provider DOES support it
setSessionPromptParams("ses_chat_params_native", {
options: {
reasoningEffort: "medium",
},
})

const handler = createChatParamsHandler()

const input = {
sessionID: "ses_chat_params_native",
agent: { name: "oracle" },
model: {
providerID: "openai",
modelID: "gpt-5.5",
api: { npm: "@ai-sdk/openai" },
},
provider: { id: "openai" },
message: {},
}

const output: ChatParamsOutput = {
options: {},
}

//#when
await handler(input, output)

//#then — native openai provider must keep reasoningEffort
expect(output.options.reasoningEffort).toBe("medium")
})

test("preserves reasoningEffort when api.npm is missing (backward compat, issue #5529)", async () => {
//#given — when the chat-params hook does not receive api.npm, fall back to
// existing behavior and let reasoningEffort through. This preserves backward
// compatibility for any plugin/hook that does not forward the api field.
setSessionPromptParams("ses_chat_params_legacy", {
options: {
reasoningEffort: "medium",
},
})

const handler = createChatParamsHandler()

const input = {
sessionID: "ses_chat_params_legacy",
agent: { name: "oracle" },
model: { providerID: "openai", modelID: "gpt-5.5" },
provider: { id: "openai" },
message: {},
}

const output: ChatParamsOutput = {
options: {},
}

//#when
await handler(input, output)

//#then — without api.npm, preserve legacy behavior
expect(output.options.reasoningEffort).toBe("medium")
})
})
21 changes: 20 additions & 1 deletion packages/omo-opencode/src/plugin/chat-params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export type ChatParamsInput = {

type ChatParamsHookInput = ChatParamsInput & {
rawMessage?: Record<string, unknown>
apiNpm?: string
}

export type ChatParamsOutput = {
Expand All @@ -24,6 +25,12 @@ export type ChatParamsOutput = {
options: Record<string, unknown>
}

const NATIVE_OPENAI_NPM = "@ai-sdk/openai"

function isNativeOpenAIProvider(apiNpm: string | undefined): boolean {
return apiNpm === NATIVE_OPENAI_NPM
}



function buildChatParamsInput(raw: unknown): ChatParamsHookInput | null {
Expand Down Expand Up @@ -62,13 +69,17 @@ function buildChatParamsInput(raw: unknown): ChatParamsHookInput | null {
if (typeof modelID !== "string") return null
if (typeof providerId !== "string") return null

const api = isRecord(model.api) ? model.api : undefined
const apiNpm = api && typeof api.npm === "string" ? api.npm : undefined

return {
sessionID,
agent: { name: agentName },
model: { providerID, modelID },
provider: { id: providerId },
message,
rawMessage: message,
apiNpm,
}
}

Expand Down Expand Up @@ -143,7 +154,15 @@ export function createChatParamsHandler(_args: {
normalizedInput.message = normalizedInput.rawMessage as { variant?: string }

if (compatibility.reasoningEffort !== undefined) {
output.options.reasoningEffort = compatibility.reasoningEffort
if (normalizedInput.apiNpm !== undefined && !isNativeOpenAIProvider(normalizedInput.apiNpm)) {
// OpenAI rejects requests that combine tools + reasoning_effort on
// /v1/chat/completions (issue #5529). openai-compatible providers and
// non-native OpenAI variants may use that endpoint, so drop the
// reasoning_effort injection entirely.
delete output.options.reasoningEffort
} else {
output.options.reasoningEffort = compatibility.reasoningEffort
}
} else if ("reasoningEffort" in output.options) {
delete output.options.reasoningEffort
}
Expand Down
2 changes: 1 addition & 1 deletion packages/shared-skills/scripts/frontend-refs-manifest.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const sharedSkillsRoot = join(here, "..");
export const frontendSkillRoot = join(sharedSkillsRoot, "skills", "frontend");
export const upstreamsRoot = join(sharedSkillsRoot, "upstreams");

export const designOriginals = ["README.md", "_INDEX.md", "design-system-architecture.md", "react-dev-tooling-skill.md"];
export const designOriginals = ["README.md", "_INDEX.md", "design-system-architecture.md", "react-dev-tooling-skill.md", "aside.md", "motherduck.md"];

export const brandStems = [
"airbnb", "airtable", "apple", "binance", "bmw", "bugatti", "cal", "claude", "clay", "clickhouse",
Expand Down
Loading