mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-18 22:35:51 +00:00
feat: 重构供应商层次 (#286)
* refactor: 创建 @anthropic-ai/model-provider 包骨架与类型定义
- 新建 workspace 包 packages/@anthropic-ai/model-provider
- 定义 ModelProviderHooks 接口(依赖注入:分析、成本、日志等)
- 定义 ClientFactories 接口(Anthropic/OpenAI/Gemini/Grok 客户端工厂)
- 搬入核心类型:Message 体系、NonNullableUsage、EMPTY_USAGE、SystemPrompt、错误常量
- 主项目 src/types/message.ts 等改为 re-export,保持向后兼容
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: 提升 OpenAI 转换器和模型映射到 model-provider 包
- 搬入 OpenAI 消息转换(convertMessages)、工具转换(convertTools)、流适配(streamAdapter)
- 搬入 OpenAI 和 Grok 模型映射(resolveOpenAIModel、resolveGrokModel)
- 主项目文件改为 thin re-export proxy
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: 搬入 Gemini 兼容层到 model-provider 包
- 搬入 Gemini 类型定义、消息转换、工具转换、流适配、模型映射
- 主项目 gemini/ 目录下文件改为 thin re-export proxy
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: 搬入 errorUtils 并迁移消费者导入到 model-provider
- 搬入 formatAPIError、extractConnectionErrorDetails 等 errorUtils
- 迁移 10 个消费者文件直接从 @anthropic-ai/model-provider 导入
- 更新 emptyUsage、sdkUtilityTypes、systemPromptType 为 re-export proxy
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: compact 模型降级为 -1 模式(Opus→Sonnet, Sonnet→Haiku)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: 添加 agent-loop 绘图
* Revert "feat: compact 模型降级为 -1 模式(Opus→Sonnet, Sonnet→Haiku)"
This reverts commit e458d6391d.
* docs: 添加简化版 agent loop
* fix: 修复 n 快捷键导致关闭的问题
* fix: 修复 node 下 ws 没打包问题
* docs: 修复链接
* test: 添加测试支持
* fix: 修复类型问题(#267) (#271)
* fix: 修复 Bun 的 polyfill 问题
* fix: 类型修复完成
* feat: 统一所有包的类型文件
* fix: 修复构建问题
* test: 修复类型校验 (#279)
* fix: 修复 Bun 的 polyfill 问题
* fix: 类型修复完成
* feat: 统一所有包的类型文件
* fix: 修复构建问题
* fix(remote-control): harden self-hosted session flows (#278)
Co-authored-by: chengzifeng <chengzifeng@meituan.com>
* docs: update contributors
* build: 新增 vite 构建流程
* feat: 添加环境变量支持以覆盖 max_tokens 设置
* feat(langfuse): LLM generation 记录工具定义
将 Anthropic 格式的工具定义转换为 Langfuse 兼容的 OpenAI 格式,
并在 generation 的 input 中以 { messages, tools } 结构传入,
以便在 Langfuse UI 中查看完整的工具定义信息。
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: 添加对 ACP 协议的支持 (#284)
* feat: 适配 zed acp 协议
* docs: 完善 acp 文档
* chore: 1.4.0
* conflict: 解决冲突
* feat: 添加测试覆盖率上报
* style: 改名加移动文件夹位置
* refactor: 移动测试用例及实现
* test: 修复测试用例完成
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Cheng Zi Feng <1154238323@qq.com>
Co-authored-by: chengzifeng <chengzifeng@meituan.com>
Co-authored-by: claude-code-best <272536312+claude-code-best@users.noreply.github.com>
This commit is contained in:
103
src/services/api/openai/requestBody.ts
Normal file
103
src/services/api/openai/requestBody.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* Pure utility functions for building OpenAI request bodies and detecting
|
||||
* thinking mode. Extracted from index.ts so tests can import them without
|
||||
* triggering heavy module side-effects (OpenAI client, stream adapter, etc.).
|
||||
*/
|
||||
import type {
|
||||
ChatCompletionCreateParamsStreaming,
|
||||
} from 'openai/resources/chat/completions/completions.mjs'
|
||||
import { isEnvTruthy, isEnvDefinedFalsy } from '../../../utils/envUtils.js'
|
||||
|
||||
/**
|
||||
* Detect whether DeepSeek-style thinking mode should be enabled.
|
||||
*
|
||||
* Enabled when:
|
||||
* 1. OPENAI_ENABLE_THINKING=1 is set (explicit enable), OR
|
||||
* 2. Model name contains "deepseek-reasoner" OR "DeepSeek-V3.2" (auto-detect, case-insensitive)
|
||||
*
|
||||
* Disabled when:
|
||||
* - OPENAI_ENABLE_THINKING=0/false/no/off is explicitly set (overrides model detection)
|
||||
*
|
||||
* @param model - The resolved OpenAI model name
|
||||
*/
|
||||
export function isOpenAIThinkingEnabled(model: string): boolean {
|
||||
// Explicit disable takes priority (overrides model auto-detect)
|
||||
if (isEnvDefinedFalsy(process.env.OPENAI_ENABLE_THINKING)) return false
|
||||
// Explicit enable
|
||||
if (isEnvTruthy(process.env.OPENAI_ENABLE_THINKING)) return true
|
||||
// Auto-detect from model name (deepseek-reasoner and DeepSeek-V3.2 support thinking mode)
|
||||
const modelLower = model.toLowerCase()
|
||||
return modelLower.includes('deepseek-reasoner') || modelLower.includes('deepseek-v3.2')
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve max output tokens for the OpenAI-compatible path.
|
||||
*
|
||||
* Override priority:
|
||||
* 1. maxOutputTokensOverride (programmatic, from query pipeline)
|
||||
* 2. OPENAI_MAX_TOKENS env var (OpenAI-specific, useful for local models
|
||||
* with small context windows, e.g. RTX 3060 12GB running 65536-token models)
|
||||
* 3. CLAUDE_CODE_MAX_OUTPUT_TOKENS env var (generic override)
|
||||
* 4. upperLimit default (64000)
|
||||
*/
|
||||
export function resolveOpenAIMaxTokens(
|
||||
upperLimit: number,
|
||||
maxOutputTokensOverride?: number,
|
||||
): number {
|
||||
return maxOutputTokensOverride
|
||||
?? (process.env.OPENAI_MAX_TOKENS ? parseInt(process.env.OPENAI_MAX_TOKENS, 10) || undefined : undefined)
|
||||
?? (process.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS ? parseInt(process.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS, 10) || undefined : undefined)
|
||||
?? upperLimit
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the request body for OpenAI chat.completions.create().
|
||||
* Extracted for testability — the thinking mode params are injected here.
|
||||
*
|
||||
* DeepSeek thinking mode: inject thinking params via request body.
|
||||
* Two formats are added simultaneously to support different deployments:
|
||||
* - Official DeepSeek API: `thinking: { type: 'enabled' }`
|
||||
* - Self-hosted DeepSeek-V3.2: `enable_thinking: true` + `chat_template_kwargs: { thinking: true }`
|
||||
* OpenAI SDK passes unknown keys through to the HTTP body.
|
||||
* Each endpoint will use the format it recognizes and ignore the others.
|
||||
*/
|
||||
export function buildOpenAIRequestBody(params: {
|
||||
model: string
|
||||
messages: any[]
|
||||
tools: any[]
|
||||
toolChoice: any
|
||||
enableThinking: boolean
|
||||
maxTokens: number
|
||||
temperatureOverride?: number
|
||||
}): ChatCompletionCreateParamsStreaming & {
|
||||
thinking?: { type: string }
|
||||
enable_thinking?: boolean
|
||||
chat_template_kwargs?: { thinking: boolean }
|
||||
} {
|
||||
const { model, messages, tools, toolChoice, enableThinking, maxTokens, temperatureOverride } = params
|
||||
return {
|
||||
model,
|
||||
messages,
|
||||
max_tokens: maxTokens,
|
||||
...(tools.length > 0 && {
|
||||
tools,
|
||||
...(toolChoice && { tool_choice: toolChoice }),
|
||||
}),
|
||||
stream: true,
|
||||
stream_options: { include_usage: true },
|
||||
// DeepSeek thinking mode: enable chain-of-thought output.
|
||||
// When active, temperature/top_p/presence_penalty/frequency_penalty are ignored by DeepSeek.
|
||||
...(enableThinking && {
|
||||
// Official DeepSeek API format
|
||||
thinking: { type: 'enabled' },
|
||||
// Self-hosted DeepSeek-V3.2 format
|
||||
enable_thinking: true,
|
||||
chat_template_kwargs: { thinking: true },
|
||||
}),
|
||||
// Only send temperature when thinking mode is off (DeepSeek ignores it anyway,
|
||||
// but other providers may respect it)
|
||||
...(!enableThinking && temperatureOverride !== undefined && {
|
||||
temperature: temperatureOverride,
|
||||
}),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user