mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-17 05:45:51 +00:00
* 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>
168 lines
4.8 KiB
TypeScript
168 lines
4.8 KiB
TypeScript
import { describe, expect, test } from 'bun:test'
|
|
import { anthropicToolsToOpenAI, anthropicToolChoiceToOpenAI } from '../openaiConvertTools.js'
|
|
|
|
describe('anthropicToolsToOpenAI', () => {
|
|
test('converts basic tool', () => {
|
|
const tools = [
|
|
{
|
|
type: 'custom',
|
|
name: 'bash',
|
|
description: 'Run a bash command',
|
|
input_schema: {
|
|
type: 'object',
|
|
properties: { command: { type: 'string' } },
|
|
required: ['command'],
|
|
},
|
|
},
|
|
]
|
|
|
|
const result = anthropicToolsToOpenAI(tools as any)
|
|
|
|
expect(result).toEqual([{
|
|
type: 'function',
|
|
function: {
|
|
name: 'bash',
|
|
description: 'Run a bash command',
|
|
parameters: {
|
|
type: 'object',
|
|
properties: { command: { type: 'string' } },
|
|
required: ['command'],
|
|
},
|
|
},
|
|
}])
|
|
})
|
|
|
|
test('uses empty schema when input_schema missing', () => {
|
|
const tools = [{ type: 'custom', name: 'noop', description: 'no-op' }]
|
|
const result = anthropicToolsToOpenAI(tools as any)
|
|
|
|
expect((result[0] as { function: { parameters: unknown } }).function.parameters).toEqual({ type: 'object', properties: {} })
|
|
})
|
|
|
|
test('strips Anthropic-specific fields', () => {
|
|
const tools = [
|
|
{
|
|
type: 'custom',
|
|
name: 'bash',
|
|
description: 'Run bash',
|
|
input_schema: { type: 'object', properties: {} },
|
|
cache_control: { type: 'ephemeral' },
|
|
defer_loading: true,
|
|
},
|
|
]
|
|
const result = anthropicToolsToOpenAI(tools as any)
|
|
|
|
expect((result[0] as any).cache_control).toBeUndefined()
|
|
expect((result[0] as any).defer_loading).toBeUndefined()
|
|
})
|
|
|
|
test('handles empty tools array', () => {
|
|
expect(anthropicToolsToOpenAI([])).toEqual([])
|
|
})
|
|
|
|
test('sanitizes const to enum in tool schema', () => {
|
|
const tools = [
|
|
{
|
|
type: 'custom',
|
|
name: 'test',
|
|
description: 'test tool',
|
|
input_schema: {
|
|
type: 'object',
|
|
properties: {
|
|
mode: { const: 'read' },
|
|
name: { type: 'string' },
|
|
},
|
|
},
|
|
},
|
|
]
|
|
const result = anthropicToolsToOpenAI(tools as any)
|
|
const props = (result[0] as { function: { parameters: any } }).function.parameters as any
|
|
expect(props.properties.mode).toEqual({ enum: ['read'] })
|
|
expect(props.properties.mode.const).toBeUndefined()
|
|
expect(props.properties.name).toEqual({ type: 'string' })
|
|
})
|
|
|
|
test('sanitizes const in deeply nested schemas', () => {
|
|
const tools = [
|
|
{
|
|
type: 'custom',
|
|
name: 'deep',
|
|
description: 'nested const',
|
|
input_schema: {
|
|
type: 'object',
|
|
properties: {
|
|
outer: {
|
|
type: 'object',
|
|
properties: {
|
|
inner: { const: 'fixed' },
|
|
},
|
|
},
|
|
},
|
|
definitions: {
|
|
MyType: {
|
|
type: 'object',
|
|
properties: {
|
|
field: { const: 42 },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
]
|
|
const result = anthropicToolsToOpenAI(tools as any)
|
|
const params = (result[0] as { function: { parameters: any } }).function.parameters as any
|
|
expect(params.properties.outer.properties.inner).toEqual({ enum: ['fixed'] })
|
|
expect(params.definitions.MyType.properties.field).toEqual({ enum: [42] })
|
|
})
|
|
|
|
test('sanitizes const in anyOf/oneOf/allOf', () => {
|
|
const tools = [
|
|
{
|
|
type: 'custom',
|
|
name: 'union',
|
|
description: 'union test',
|
|
input_schema: {
|
|
type: 'object',
|
|
properties: {
|
|
val: {
|
|
anyOf: [
|
|
{ const: 'a' },
|
|
{ const: 'b' },
|
|
{ type: 'string' },
|
|
],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
]
|
|
const result = anthropicToolsToOpenAI(tools as any)
|
|
const anyOf = ((result[0] as { function: { parameters: any } }).function.parameters as any).properties.val.anyOf
|
|
expect(anyOf[0]).toEqual({ enum: ['a'] })
|
|
expect(anyOf[1]).toEqual({ enum: ['b'] })
|
|
expect(anyOf[2]).toEqual({ type: 'string' })
|
|
})
|
|
})
|
|
|
|
describe('anthropicToolChoiceToOpenAI', () => {
|
|
test('maps auto', () => {
|
|
expect(anthropicToolChoiceToOpenAI({ type: 'auto' })).toBe('auto')
|
|
})
|
|
|
|
test('maps any to required', () => {
|
|
expect(anthropicToolChoiceToOpenAI({ type: 'any' })).toBe('required')
|
|
})
|
|
|
|
test('maps tool to function', () => {
|
|
const result = anthropicToolChoiceToOpenAI({ type: 'tool', name: 'bash' })
|
|
expect(result).toEqual({ type: 'function', function: { name: 'bash' } })
|
|
})
|
|
|
|
test('returns undefined for undefined input', () => {
|
|
expect(anthropicToolChoiceToOpenAI(undefined)).toBeUndefined()
|
|
})
|
|
|
|
test('returns undefined for unknown type', () => {
|
|
expect(anthropicToolChoiceToOpenAI({ type: 'unknown' })).toBeUndefined()
|
|
})
|
|
})
|