fix: 修复 OpenAI provider (gpt-5.4/gpt-5.3-codex等模型)下 内建mcp__plugin_weixin_weixin__reply 微信工具不可见的问题 (#359)

* fix: 修复 OpenAI provider 下 MCP 工具不可见

* docs: 补充 OpenAI MCP 工具列表注释

* fix: 修正 OpenAI Langfuse 输入记录

* refactor: 使用类型守卫收窄 Langfuse role

* fix: 保留 Langfuse OpenAI 数组消息角色

* fix: 合并 Langfuse OpenAI tool_calls

* fix: 修复 OpenAI Langfuse 类型检查
This commit is contained in:
WANG HONGXIANG
2026-04-26 09:17:09 +08:00
committed by GitHub
parent cf33c06021
commit 901628b4d9
5 changed files with 355 additions and 16 deletions

View File

@@ -5,6 +5,7 @@ import type {
StreamEvent,
SystemAPIErrorMessage,
AssistantMessage,
UserMessage,
} from '../../../types/message.js'
import type { AgentId } from '../../../types/ids.js'
import type { Tools } from '../../../Tool.js'
@@ -32,18 +33,58 @@ import type { Options } from '../claude.js'
import { randomUUID } from 'crypto'
import {
createAssistantAPIErrorMessage,
createUserMessage,
normalizeContentFromAPI,
} from '../../../utils/messages.js'
import type { SDKAssistantMessageError } from '../../../entrypoints/agentSdkTypes.js'
import {
isToolSearchEnabled,
extractDiscoveredToolNames,
isDeferredToolsDeltaEnabled,
} from '../../../utils/toolSearch.js'
import {
formatDeferredToolLine,
isDeferredTool,
TOOL_SEARCH_TOOL_NAME,
} from '@claude-code-best/builtin-tools/tools/ToolSearchTool/prompt.js'
/**
* Mirrors the Anthropic request path's deferred-tool announcement for OpenAI.
*
* OpenAI-compatible endpoints cannot consume Anthropic's `defer_loading` or
* `tool_reference` beta payloads directly, so the model needs the same textual
* list of deferred MCP tool names that Anthropic receives before it can ask
* ToolSearchTool to load their full schemas.
*/
function prependDeferredToolListIfNeeded(
messages: (AssistantMessage | UserMessage)[],
tools: Tools,
deferredToolNames: Set<string>,
useToolSearch: boolean,
): (AssistantMessage | UserMessage)[] {
if (!useToolSearch || isDeferredToolsDeltaEnabled()) return messages
const deferredToolList = tools
.filter(tool => deferredToolNames.has(tool.name))
.map(formatDeferredToolLine)
.sort()
.join('\n')
if (!deferredToolList) return messages
return [
createUserMessage({
content: `<available-deferred-tools>\n${deferredToolList}\n</available-deferred-tools>`,
isMeta: true,
}),
...messages,
]
}
function isOpenAIConvertibleMessage(msg: Message): msg is AssistantMessage | UserMessage {
return msg.type === 'assistant' || msg.type === 'user'
}
/**
* Assemble the final AssistantMessage (and optional max_tokens error) from
* accumulated stream state. Extracted to avoid duplication between the
@@ -176,9 +217,18 @@ export async function* queryModelOpenAI(
// 8. Convert messages and tools to OpenAI format
const enableThinking = isOpenAIThinkingEnabled(openaiModel)
const openaiMessages = anthropicMessagesToOpenAI(messagesForAPI, systemPrompt, {
enableThinking,
})
const openAIConvertibleMessages = messagesForAPI.filter(isOpenAIConvertibleMessage)
const messagesWithDeferredToolList = prependDeferredToolListIfNeeded(
openAIConvertibleMessages,
tools,
deferredToolNames,
useToolSearch,
)
const openaiMessages = anthropicMessagesToOpenAI(
messagesWithDeferredToolList,
systemPrompt,
{ enableThinking },
)
const openaiTools = anthropicToolsToOpenAI(standardTools)
const openaiToolChoice = anthropicToolChoiceToOpenAI(options.toolChoice)
@@ -356,7 +406,7 @@ export async function* queryModelOpenAI(
recordLLMObservation(options.langfuseTrace ?? null, {
model: openaiModel,
provider: 'openai',
input: convertMessagesToLangfuse(messagesForAPI, systemPrompt),
input: convertMessagesToLangfuse(openaiMessages),
output: convertOutputToLangfuse(collectedMessages),
usage: {
input_tokens: usage.input_tokens,