Skip to content
zeelerPublic

About

cc-proxy is a minimal API proxy designed for the Claude Code CLI, enabling it to use models like Kimi that support only the Chat Completions API.

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

18 Commits

Folders and files

Repository files navigation

cc-proxy

将 Anthropic Messages API 格式的请求转换为 OpenAI Chat Completions 格式,转发给 Kimi API,使 Claude Code CLI 能够使用 Kimi 大模型。

架构

Claude Code CLI                     Kimi API
     │                                  │
     │  POST /v1/messages               │
     │  (Anthropic 格式)                 │
     ▼                                  │
┌──────────┐   ┌───────────┐   ┌─────────────┐
│  server  │──▶│ converter  │──▶│   client    │──▶ POST /chat/completions
│          │◀──│           │◀──│             │◀── (OpenAI 格式)
└──────────┘   └───────────┘   └─────────────┘
  • server — HTTP 服务端,接收 Anthropic 格式请求,路由 /v1/messages 和 /v1/models
  • converter — 双向格式转换引擎,支持请求转换、响应转换、SSE 流式转换状态机
  • client — Kimi API 的 HTTP 客户端,支持流式和非流式调用
  • config — 纯环境变量配置,零配置文件

零外部依赖,仅使用 Go 标准库。

快速开始

编译

go build -o cc-proxy ./cmd/proxy/

运行

export MOONSHOT_API_KEY=sk-xxxxx
./cc-proxy

代理默认监听 http://localhost:3699,启动日志:

cc-proxy listening on :3699
Kimi API: https://api.moonshot.cn/v1
Model map: map[kimi-k2.7-code:kimi-k2.7-code kimi-k3:kimi-k3]

配置 Claude Code CLI

在 ~/.claude/settings.json 中修改 env 部分:

{
  "env": {
    "ANTHROPIC_BASE_URL": "http://localhost:3699",
    "ANTHROPIC_AUTH_TOKEN": "any-value-not-empty",
    "ANTHROPIC_MODEL": "kimi-k3",
    "ANTHROPIC_DEFAULT_OPUS_MODEL": "kimi-k3",
    "ANTHROPIC_DEFAULT_SONNET_MODEL": "kimi-k3",
    "ANTHROPIC_DEFAULT_HAIKU_MODEL": "kimi-k2.7-code",
    "CLAUDE_CODE_SUBAGENT_MODEL": "kimi-k2.7-code"
  }
}

说明:ANTHROPIC_AUTH_TOKEN 填任意非空值即可(代理不校验),ANTHROPIC_MODEL 等模型名会被 MODEL_MAP 映射到对应的 Kimi 模型。

格式转换

请求转换(Anthropic → OpenAI)

Anthropic 字段 OpenAI 字段 转换方式
model model 查 MODEL_MAP 映射,未匹配则原样透传
system (string/array) messages[0] (role: system) 提取文本,插入消息数组最前面
messages[].content (string) messages[].content (string) 直接映射
messages[].content (blocks) 多个 OpenAI 消息 按 block type 拆分(见下方)
max_tokens max_tokens 直接映射
temperature temperature 直接映射
top_p top_p 直接映射
top_k — OpenAI 不支持,丢弃
stop_sequences stop 直接映射
stream stream 直接映射
tools[] tools[] 包装 {type:"function", function:{...}}

Content Block 转换规则:

Anthropic Block OpenAI 表示
{type:"text", text:"..."} 合并到 content 字符串
{type:"tool_use", id, name, input} role:"assistant" + tool_calls[{id, function:{name, arguments}}]
{type:"tool_result", tool_use_id, content} role:"tool" + tool_call_id + content
{type:"thinking", thinking:"..."} 包裹为 [思考]...[\/思考] 合并到 content

响应转换(OpenAI → Anthropic)

OpenAI 字段 Anthropic 字段
choices[0].message.content content: [{type:"text", text:"..."}]
choices[0].message.tool_calls[] content: [{type:"tool_use", id, name, input}]
choices[0].message.reasoning_content content: [{type:"thinking", thinking:"..."}]
choices[0].finish_reason stop_reason(映射规则见下方)
usage usage(字段名直接对应)

finish_reason → stop_reason 映射:

OpenAI Anthropic
stop end_turn
tool_calls tool_use
length max_tokens
content_filter end_turn
未知值 原样透传

流式转换(SSE 状态机)

OpenAI 的 SSE 流式事件被 StreamConverter 状态机逐 chunk 转换为 Anthropic 的 SSE 事件序列:

message_start → content_block_start(text) → content_block_delta(text_delta)*
→ content_block_stop → ... (轮换 thinking/tool_use block)
→ message_delta(含 stop_reason)→ message_stop

支持的 delta 类型:

  • content → text_delta
  • reasoning_content → thinking_delta
  • tool_calls[] → input_json_delta(累积 strings.Builder 拼接 JSON 片段)

环境变量

变量 必填 默认值 说明
MOONSHOT_API_KEY 是 — Kimi API 密钥,启动时校验
PROXY_PORT 否 3699 代理监听端口(1-65535,无效值回退默认)
KIMI_BASE_URL 否 https://api.moonshot.cn/v1 Kimi API 地址(末尾 / 自动去除)
MODEL_MAP 否 kimi-k3:kimi-k3,kimi-k2.7-code:kimi-k2.7-code 模型映射,格式 src:dst,...(逗号分隔多个映射对,冒号分隔源与目标)

MODEL_MAP 示例:MODEL_MAP=kimi-k3:kimi-k3,kimi-k2.7-code:kimi-k2.7-code,claude-sonnet-4-20250514:kimi-k3

未匹配的模型名会原样透传给 Kimi API,不做修改。

API 端点

POST /v1/messages

Anthropic Messages API 兼容端点,支持 stream: true/false。

非流式请求示例:

curl -s http://localhost:3699/v1/messages \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "kimi-k3",
    "max_tokens": 100,
    "messages": [{"role": "user", "content": "你好"}]
  }' | python3 -m json.tool

流式请求示例:

curl -sN http://localhost:3699/v1/messages \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "kimi-k3",
    "max_tokens": 100,
    "stream": true,
    "messages": [{"role": "user", "content": "你好"}]
  }'

GET /v1/models

返回 MODEL_MAP 中所有目标模型的去重列表:

{
  "data": [
    {"id": "kimi-k3", "type": "model"},
    {"id": "kimi-k2.7-code", "type": "model"}
  ],
  "has_more": false
}

错误处理

场景 HTTP 状态码 响应格式
MOONSHOT_API_KEY 未设置 启动时 fatal Config error: MOONSHOT_API_KEY environment variable is required
请求体 JSON 解析失败 400 Anthropic 格式 {"type":"error","error":{"type":"api_error","message":"..."}}
非 POST 方法访问 /v1/messages 405 同上
Kimi API 返回错误 透传 Kimi 状态码 转换为 Anthropic 格式 error 响应
流式传输中断 — 记录日志,结束 SSE 流
未匹配路由 404 Anthropic 格式 error 响应

项目结构

cc-proxy/
├── cmd/proxy/main.go              # 入口点:config.Load() → server.New() → srv.Start()
├── internal/
│   ├── config/config.go           # 环境变量解析与校验
│   ├── converter/
│   │   ├── types.go               # Anthropic + OpenAI 双向数据类型(26 个 struct)
│   │   ├── request.go             # Anthropic → OpenAI 请求转换
│   │   ├── response.go            # OpenAI → Anthropic 响应转换 + /v1/models
│   │   ├── stream.go              # SSE 流式状态机(StreamConverter)
│   │   └── converter_test.go      # 12 个单元测试
│   ├── client/client.go           # Kimi API HTTP 客户端(流式 + 非流式)
│   └── server/server.go           # HTTP 服务端(路由、处理器、错误响应)
├── scripts/smoke.sh               # 端到端冒烟测试
├── go.mod
├── go.sum
└── README.md

测试

# 运行所有单元测试(12 个用例,覆盖请求/响应/内容解析)
go test ./... -v

# 静态检查
go vet ./...

# 端到端冒烟测试(需要有效 MOONSHOT_API_KEY)
export MOONSHOT_API_KEY=sk-xxxxx
bash scripts/smoke.sh

冒烟测试会:

  1. 编译二进制
  2. 启动代理
  3. 验证 /v1/models 返回模型列表
  4. 发送非流式 /v1/messages 请求并验证 Anthropic 格式响应
  5. 发送流式请求并验证收到 SSE 事件
  6. 自动清理

技术要点

  • 零外部依赖 — 仅使用 Go 标准库(net/http, encoding/json, bufio 等)
  • Go 1.26+ — 使用模块模式 cc-proxy
  • 多态 JSON 处理 — AnthropicMessage.Content 和 AnthropicRequest.System 字段可能是 string 或 array,使用 json.RawMessage + 两级尝试反序列化
  • 流式安全 — StreamConverter 内建状态机追踪 content block 生命周期,client.ChatCompletionStream 通过 buffered channel(容量 100)和 context 取消防止 goroutine 泄漏
  • 模型名映射 — kimi-k3 支持 128K 上下文,kimi-k2.7-code 为代码场景优化

About

cc-proxy is a minimal API proxy designed for the Claude Code CLI, enabling it to use models like Kimi that support only the Chat Completions API.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages