fix: convert 'developer' role to 'system' for unsupported providers (#12325)

AI SDK v5 uses 'developer' role for reasoning models, but some providers
like Azure DeepSeek R1 only support 'system', 'user', 'assistant', 'tool'
roles, causing HTTP 422 errors.

This fix adds a custom fetch wrapper that converts 'developer' role back
to 'system' for providers that don't support it.

Fixes #12321

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
SuYao 2026-01-07 01:03:37 +08:00 committed by GitHub
parent 116ee6f94b
commit 6b0bb64795
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -1,5 +1,5 @@
import { formatPrivateKey, hasProviderConfig, ProviderConfigFactory } from '@cherrystudio/ai-core/provider'
import { isOpenAIChatCompletionOnlyModel } from '@renderer/config/models'
import { isOpenAIChatCompletionOnlyModel, isOpenAIReasoningModel } from '@renderer/config/models'
import {
getAwsBedrockAccessKeyId,
getAwsBedrockApiKey,
@ -29,6 +29,7 @@ import {
isNewApiProvider,
isOllamaProvider,
isPerplexityProvider,
isSupportDeveloperRoleProvider,
isSupportStreamOptionsProvider,
isVertexProvider
} from '@renderer/utils/provider'
@ -264,6 +265,14 @@ export function providerToAiSdkConfig(actualProvider: Provider, model: Model): A
}
}
// Apply developer-to-system role conversion for providers that don't support developer role
// bug: https://github.com/vercel/ai/issues/10982
// fixPR: https://github.com/vercel/ai/pull/11127
// TODO: but the PR don't backport to v5, the code will be removed when upgrading to v6
if (!isSupportDeveloperRoleProvider(actualProvider) || !isOpenAIReasoningModel(model)) {
extraOptions.fetch = createDeveloperToSystemFetch(extraOptions.fetch)
}
if (hasProviderConfig(aiSdkProviderId) && aiSdkProviderId !== 'openai-compatible') {
const options = ProviderConfigFactory.fromProvider(aiSdkProviderId, baseConfig, extraOptions)
return {
@ -302,6 +311,44 @@ export function isModernSdkSupported(provider: Provider): boolean {
return hasProviderConfig(aiSdkProviderId)
}
/**
* Creates a custom fetch wrapper that converts 'developer' role to 'system' role in request body.
* This is needed for providers that don't support the 'developer' role (e.g., Azure DeepSeek R1).
*
* @param originalFetch - Optional original fetch function to wrap
* @returns A fetch function that transforms the request body
*/
function createDeveloperToSystemFetch(originalFetch?: typeof fetch): typeof fetch {
const baseFetch = originalFetch ?? fetch
return async (input: RequestInfo | URL, init?: RequestInit) => {
let options = init
if (options?.body && typeof options.body === 'string') {
try {
const body = JSON.parse(options.body)
if (body.messages && Array.isArray(body.messages)) {
let hasChanges = false
body.messages = body.messages.map((msg: { role: string }) => {
if (msg.role === 'developer') {
hasChanges = true
return { ...msg, role: 'system' }
}
return msg
})
if (hasChanges) {
options = {
...options,
body: JSON.stringify(body)
}
}
}
} catch {
// If parsing fails, just use original body
}
}
return baseFetch(input, options)
}
}
/**
* provider的配置,
*/
@ -360,5 +407,6 @@ export async function prepareSpecialProviderConfig(
}
}
}
return config
}