fix(console): transform system role messages for Console API compatibility

Claude Console API (e.g., GLM accounts) does not accept messages with
role='system' in the messages array, returning 422 error:
"Input should be 'user' or 'assistant'"

This fix automatically transforms system messages by merging them into
the first user message's content, maintaining compatibility with clients
like opencode that send system role messages.

Changes:
- Add _transformSystemMessages() method to merge system content into user messages
- Apply transformation in both relayRequest() and relayStreamRequestWithUsageCapture()
- Only affects claude-console account type, no impact on official API

Fixes issues where opencode users get 422 errors when using Console API accounts.
This commit is contained in:
Lukin
2026-02-12 23:32:26 +08:00
parent 02e82cd245
commit fe52a62cda

View File

@@ -19,6 +19,68 @@ class ClaudeConsoleRelayService {
this.defaultUserAgent = 'claude-cli/2.0.52 (external, cli)'
}
/**
* 🔄 转换 messages 数组中的 system role 消息
* Console API 不支持 role="system",需要将 system 内容合并到第一条 user 消息
* @param {Object} requestBody - 原始请求体
* @returns {Object} 转换后的请求体
*/
_transformSystemMessages(requestBody) {
if (!requestBody || !Array.isArray(requestBody.messages)) {
return requestBody
}
// 收集所有 system messages 的内容
const systemContents = []
const nonSystemMessages = []
for (const msg of requestBody.messages) {
if (msg && msg.role === 'system') {
systemContents.push(msg.content)
} else {
nonSystemMessages.push(msg)
}
}
// 如果没有 system messages直接返回原请求
if (systemContents.length === 0) {
return requestBody
}
// 合并 system 内容到第一条 user 消息
const systemText = systemContents.join('\n\n')
let transformedMessages = nonSystemMessages
// 查找第一条 user 消息
const firstUserIndex = nonSystemMessages.findIndex((m) => m && m.role === 'user')
if (firstUserIndex !== -1) {
// 将 system 内容前置到第一条 user 消息
const firstUserMsg = nonSystemMessages[firstUserIndex]
const mergedContent = `${systemText}\n\n${firstUserMsg.content}`
transformedMessages = [...nonSystemMessages]
transformedMessages[firstUserIndex] = {
...firstUserMsg,
content: mergedContent
}
} else {
// 如果没有 user 消息,创建一个包含 system 内容的 user 消息
logger.warn(
`⚠️ Console API: No user message found to merge system prompt, creating new user message with system content`
)
transformedMessages = [{ role: 'user', content: systemText }, ...nonSystemMessages]
}
logger.debug(
`🔄 Console API: Transformed ${systemContents.length} system message(s) into user message context`
)
return {
...requestBody,
messages: transformedMessages
}
}
// 🚀 转发请求到Claude Console API
async relayRequest(
requestBody,
@@ -163,6 +225,9 @@ class ClaudeConsoleRelayService {
model: mappedModel
}
// 🔄 转换 system messages (Console API 不支持 role="system")
const transformedRequestBody = this._transformSystemMessages(modifiedRequestBody)
// 模型兼容性检查已经在调度器中完成,这里不需要再检查
// 创建代理agent
@@ -219,7 +284,7 @@ class ClaudeConsoleRelayService {
const requestConfig = {
method: 'POST',
url: apiEndpoint,
data: modifiedRequestBody,
data: transformedRequestBody,
headers: {
'Content-Type': 'application/json',
'anthropic-version': '2023-06-01',
@@ -649,6 +714,9 @@ class ClaudeConsoleRelayService {
model: mappedModel
}
// 🔄 转换 system messages (Console API 不支持 role="system")
const transformedRequestBody = this._transformSystemMessages(modifiedRequestBody)
// 模型兼容性检查已经在调度器中完成,这里不需要再检查
// 创建代理agent
@@ -656,7 +724,7 @@ class ClaudeConsoleRelayService {
// 发送流式请求
await this._makeClaudeConsoleStreamRequest(
modifiedRequestBody,
transformedRequestBody,
account,
proxyAgent,
clientHeaders,