From 6b0bb64795beb99b5e7a63261132695462d69da4 Mon Sep 17 00:00:00 2001 From: SuYao Date: Wed, 7 Jan 2026 01:03:37 +0800 Subject: [PATCH] fix: convert 'developer' role to 'system' for unsupported providers (#12325) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .../src/aiCore/provider/providerConfig.ts | 50 ++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/src/renderer/src/aiCore/provider/providerConfig.ts b/src/renderer/src/aiCore/provider/providerConfig.ts index 0ad15ea895..54915795a8 100644 --- a/src/renderer/src/aiCore/provider/providerConfig.ts +++ b/src/renderer/src/aiCore/provider/providerConfig.ts @@ -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 }