fix(llms): model compatibility problem

This commit is contained in:
Simon
2026-07-03 02:26:01 +08:00
parent 2163a55cc7
commit 04b84b6e8a
2 changed files with 67 additions and 25 deletions
+2 -1
View File
@@ -56,7 +56,8 @@ export class OpenAIClient implements LLMClient {
requestBody.temperature = this.config.temperature
}
modelPatch(requestBody)
modelPatch(requestBody, this.config.baseURL)
let transformedBody: Record<string, unknown> | undefined
try {
transformedBody = this.config.transformRequestBody(requestBody)
+65 -24
View File
@@ -36,10 +36,12 @@ export function zodToOpenAITool(name: string, tool: Tool) {
* @todo Need vendor-specific patches.
* Local and 3rd-party hosted models may have different schema.
*/
export function modelPatch(body: Record<string, any>) {
export function modelPatch(body: Record<string, any>, baseURL?: string) {
const model: string = body.model || ''
if (!model) return body
const provider = getProvider(baseURL)
const modelName = normalizeModelName(model)
if (modelName.startsWith('qwen')) {
@@ -58,12 +60,14 @@ export function modelPatch(body: Record<string, any>) {
}
if (modelName.startsWith('gpt')) {
body.verbosity = 'low'
if (modelName.startsWith('gpt-5')) {
body.verbosity = 'low'
// gpt-5 gpt-5-mini gpt-5-nano only supports "minimal";
// 5.1+ supports "none".
body.reasoning_effort = /^gpt-5(-|$)/.test(modelName) ? 'minimal' : 'none'
debug(`Patch GPT-5: verbosity=low, reasoning_effort=${body.reasoning_effort}`)
// gpt-5 gpt-5-mini gpt-5-nano only supports "minimal";
// 5.1+ supports "none".
body.reasoning_effort = /^gpt-5(-|$)/.test(modelName) ? 'minimal' : 'none'
debug(`Patch GPT-5: verbosity=low, reasoning_effort=${body.reasoning_effort}`)
}
if (modelName.includes('chat-latest')) {
debug('Omitting reasoning_effort and temperature for chat-latest')
@@ -76,20 +80,27 @@ export function modelPatch(body: Record<string, any>) {
if (/opus|sonnet|haiku/.test(modelName)) {
debug('Patch Claude: disable thinking')
body.thinking = { type: 'disabled' }
if (provider !== 'openrouter') {
// Convert tool_choice to Claude format
if (body.tool_choice === 'required') {
// 'required' -> { type: 'any' } (must call some tool)
debug('Applying Claude patch: convert tool_choice "required" to { type: "any" }')
body.tool_choice = { type: 'any' }
} else if (body.tool_choice?.function?.name) {
// { type: 'function', function: { name: '...' } } -> { type: 'tool', name: '...' }
debug('Applying Claude patch: convert tool_choice format')
body.tool_choice = { type: 'tool', name: body.tool_choice.function.name }
}
}
} else {
debug('Patch Claude: reasoning_effort=low')
body.reasoning_effort = 'low'
}
// Convert tool_choice to Claude format
if (body.tool_choice === 'required') {
// 'required' -> { type: 'any' } (must call some tool)
debug('Applying Claude patch: convert tool_choice "required" to { type: "any" }')
body.tool_choice = { type: 'any' }
} else if (body.tool_choice?.function?.name) {
// { type: 'function', function: { name: '...' } } -> { type: 'tool', name: '...' }
debug('Applying Claude patch: convert tool_choice format')
body.tool_choice = { type: 'tool', name: body.tool_choice.function.name }
// Fable and mythos can not disable adaptive thinking.
// Claude does not support tool_choice with extended thinking.
// These 2 concepts are blurred. Basically no tool_choice with thinking.
delete body.tool_choice
}
}
@@ -124,18 +135,40 @@ export function modelPatch(body: Record<string, any>) {
}
}
if (modelName.startsWith('kimi') && !modelName.includes('code')) {
// kimi-k2.7-code cannot disable thinking (errors), hence the code exclusion.
debug('Patch Kimi: disable thinking')
body.thinking = { type: 'disabled' }
if (modelName.startsWith('kimi')) {
if (!modelName.includes('code')) {
// kimi-k2.7-code cannot disable thinking
debug('Patch Kimi: disable thinking')
body.thinking = { type: 'disabled' }
}
}
if (modelName.startsWith('minimax')) {
// Only M3 can disable thinking; M2.x accepts the field as a silent no-op.
// parallel_tool_calls is unsupported.
debug('Patch MiniMax: disable thinking, remove parallel_tool_calls')
body.thinking = { type: 'disabled' }
debug('Patch MiniMax: remove parallel_tool_calls')
delete body.parallel_tool_calls
if (modelName.includes('m3')) {
// Only M3 can disable thinking
debug('Patch MiniMax: disable thinking')
body.thinking = { type: 'disabled' }
}
}
// provider patches
if (provider === 'openrouter') {
// openrouter use reasoning object instead of reasoning_effort
const reasoningEffort = body.reasoning_effort
let reasoningEnabled = true
if (body.thinking?.type === 'disabled') reasoningEnabled = false
if (body.enable_thinking === false) reasoningEnabled = false
body.reasoning = { enabled: reasoningEnabled }
if (reasoningEnabled && reasoningEffort) {
body.reasoning.effort = reasoningEffort
}
}
return body
@@ -172,3 +205,11 @@ export function normalizeModelName(modelName: string): string {
return normalizedName
}
export function getProvider(baseURL?: string): 'openrouter' | undefined {
if (!baseURL) return undefined
const url = new URL(baseURL)
const hostname = url.hostname
if (hostname === 'openrouter.ai') return 'openrouter'
return undefined
}