This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
- 语言:中文输出,技术术语保留 English
- 语气:自然、克制、礼貌;禁止命令式、情绪化、俚语、粗鲁
- 表达:先结论后必要补充;简洁,避免空泛抽象措辞
- 不确定:先澄清再执行,避免假设性操作
- 禁止:自我评价、填充词、无必要冗长背景
- 冲突:文档与用户即时指令冲突时优先后者并简短提示
- 拒绝:不合规请求礼貌拒绝,最少必要解释
- Build:
bun run build - Dev (watch):
bun run dev - Start (production env):
bun run start - Lint (with cache):
bun run lint - Lint with auto-fix:
bun run lint --fix - Lint fix on staged:
bunx lint-staged - Typecheck:
bun run typecheck - Test all:
bun test - Test single file:
bun test tests/path/to/filename.test.ts - Test coverage:
bun test --coverage - Package (prepack builds):
bun run prepack
Notes:
- 不要手动运行 Prettier;风格遵循 eslint 规则与现有配置,修复交给 lint-staged。
- 本仓库的命令使用 bash 运行。
- 质量保证流程:频繁运行
bun run lint && bun run typecheck && bun run build避免错误积累
High-level
- 这是一个 GitHub Copilot API 代理,统一暴露 OpenAI-compatible、Anthropic-compatible、Gemini-compatible API。基于 Hono + Bun。
Core components
- API Translation Layer
- OpenAI ↔ Anthropic:
src/routes/messages/* - OpenAI ↔ Gemini:
src/routes/generate-content/*
- OpenAI ↔ Anthropic:
- Token counting for Anthropic:
src/lib/tokenizer.ts - GitHub Copilot integration:
src/services/* - Controls:
src/lib/rate-limit.ts,src/lib/approval.ts,src/lib/state.ts - Server routes mounting:
src/server.ts
API endpoints
- OpenAI compatible
POST /v1/chat/completionsGET /v1/modelsPOST /v1/embeddings
- Anthropic compatible
POST /v1/messagesPOST /v1/messages/count_tokens
- Gemini compatible
POST /v1beta/models/{model}:generateContentPOST /v1beta/models/{model}:streamGenerateContentPOST /v1beta/models/{model}:countTokens
- Monitoring
GET /usageGET /token
Gemini integration
-
路由装载与匹配
src/server.ts:35将geminiRouter挂在根路径server.route("/", geminiRouter)- 路由定义使用通配符与顺序匹配,必须先注册 stream,再注册 countTokens,最后才是 generateContent
src/routes/generate-content/route.ts:15-25, 29-39, 43-56
- 型号提取通过 URL 正则,不能用 Hono 的
:param方式处理冒号语法
-
请求翻译(Gemini → OpenAI)
translateGeminiToOpenAINonStream/Stream生成ChatCompletionsPayload,必要时从contents合成最小工具声明以满足 Copilot 的 tool schema 要求- 工具声明优先使用
parametersJsonSchema,否则回退parameters,最终兜底为{type:"object",properties:{}} - 取消的 tool call 会在后处理阶段从任意位置清理,以避免 400
- 代码位置:
translateGeminiToOpenAINonStream/Stream:src/routes/generate-content/translation.ts:39-63, 65-89synthesizeToolsFromContents:translation.ts:304-326translateGeminiToolsToOpenAI:translation.ts:365-431- 取消工具调用清理与消息合并:
translation.ts:185-236, 297-302
-
响应翻译与流式(OpenAI → Gemini)
- 非流→流式回退:当上游返回非流响应时,拆分为小块 SSE 返回,保证 CLI 一致性
handleNonStreamingToStreaming/sendTextInChunks:src/routes/generate-content/handler.ts:71-104, 106-143
- 流式 JSON 累积解析:
StreamingJSONParser先尝试直接解析,失败后切换累积模式直到形成完整 JSON,避免半包导致崩流StreamingJSONParser:handler.ts:158-188- 逐块处理:
processAndWriteChunk/handleStreamingResponse:handler.ts:194-238, 241-282
- 工具调用的增量参数:流式工具调用参数积累器处理完整/分片参数
- 不完整 JSON
arguments跳过当次块,等待后续完整块 - 支持 Gemini 风格的完整参数传递和 Claude/GPT 风格的分片传递
processToolCalls:translation.ts:537-577- 测试覆盖:
tests/generate-content/stream-tool-call-accumulator.test.ts
- 不完整 JSON
- 工具响应处理:确保 tool call 与 response 1:1 映射
ensureToolCallResponseMatch:translation.ts对 tool responses 按tool_call_id去重- 问题:OpenAI 可能返回重复的 tool responses,导致 Gemini 1:1 映射要求失败
- 解决:简单去重逻辑,保留每个
tool_call_id的第一个响应
- 终止原因映射
- OpenAI → Gemini:
mapOpenAIFinishReasonToGeminiinsrc/routes/generate-content/utils.ts:3-23 - Gemini → OpenAI:
mapGeminiFinishReasonToOpenAIinutils.ts:26-50
- OpenAI → Gemini:
- 非流→流式回退:当上游返回非流响应时,拆分为小块 SSE 返回,保证 CLI 一致性
-
计数(Gemini countTokens)
getTokenCount返回{input, output};Gemini 期望 totaltotalTokens = input + output,translation.ts提供translateGeminiCountTokensToOpenAI与translateTokenCountToGemini- handler:
src/routes/generate-content/handler.ts:314-336 - translation:
src/routes/generate-content/translation.ts:732-756
- handler:
Model mapping
- 仅映射不被 Copilot 支持的 Gemini 型号到已知等价(例如
gemini-2.5-flash→gemini-2.0-flash-001),已支持的型号保持原样translation.ts:27-35
Error handling
- 路由层 try/catch 并使用
forwardError规范转发src/routes/generate-content/route.ts:15-22, 29-36, 49-53
- TypeScript 严格模式;避免
any - Imports 使用
~/*别名 - 无分号、双引号、按组排序 imports
- 不要直接运行 Prettier;依赖 eslint 与 lint-staged 做自动修复
- 不要打印或写入敏感信息
- 测试专用类型声明放在
tests/@types/而不是全局types/目录 - 遵循关注点分离:测试相关文件与测试代码放在一起
- 使用
declare module "~/server?*"支持测试中的查询参数导入隔离
-
常见 Gemini 问题
invalid_tool_call_format:工具声明缺失或参数为空;确保tools与tool_choice按需出现,并有非空parameters- Tool call/response 1:1 映射错误:"Please ensure that the number of function response parts is equal to the number of function call parts" - 通常由重复的
tool_call_id响应引起,需要去重而不是拆分 - 嵌套
functionResponse:Gemini CLI 会发送嵌套数组,需用processFunctionResponseArray处理 tool_call_id关联:用函数名暂存并在用户回应时取回,保持一致性- 取消的 tool call:清理掉未完成的
assistant+tool_calls信息 HTTPError:多半是 OpenAI 侧 payload 校验失败
-
Debug 日志分析方法
- 使用
DebugLogger自动生成 debug-logs/ 文件夹中的请求日志DebugLogger.getInstance()单例模式访问logRequest()记录 Gemini 请求翻译过程logCopilotResponse()记录 GitHub Copilot API 响应logDebugData()通用调试数据记录logResponseComparison()对比原始与翻译后的响应
- Debug logging: Set
DEBUG_GEMINI_REQUESTS=trueto enable request logging to debug-logs/ - 压缩大日志文件便于分析:用
compress-logs.js脚本删除重复内容 - 分析时用 PowerShell/脚本统计 function calls vs responses 数量:检查
functionCall与functionResponse计数,以及翻译后的tool_calls与 tool responses 计数 - 调试方法论:
- 数据先行:从实际 debug logs 出发,不要基于理论假设
- 验证而非猜测:每次修改后必须通过新 debug logs 验证效果
- 简单解决方案优先:去重 > 拆分,避免过度复杂化
- 承认错误:当证据显示修复制造了新问题时,快速重新思考
- 使用
-
快速自检
- 完整质量保证流程:
bun run lint && bun run typecheck && bun run build && bun test --coverage - 单独运行:
bun run lint && bun run typecheck && bun run build curl http://localhost:4142/v1/models查看真实支持的模型集合- 不要在助手侧运行服务;由用户本地确认行为
- 完整质量保证流程:
- 测试结构:遵循行为驱动测试(BDD),专注于外部行为而非内部实现细节
- 测试组织:针对覆盖率不足的代码创建专门的
-coverage.test.ts文件 - Mock 模式:使用严格的 TypeScript 接口(如
CapturedPayload)替代any类型 - 主要测试场景:
- API 翻译正确性:Gemini ↔ OpenAI 格式转换
- 流式响应处理:完整响应与分片响应的正确处理
- 工具调用:完整参数 vs 分片参数的兼容性处理
- 错误处理:各种边缘情况的错误转发与处理
- 测试命令:
- 运行所有测试:
bun test - 运行特定文件:
bun test tests/path/to/file.test.ts - 查看覆盖率:
bun test --coverage
- 运行所有测试:
- 目标覆盖率:保持 90%+ 的代码覆盖率,重点关注核心翻译逻辑
基于实际项目经验的最佳实践:
- 渐进式改进:小步快跑,频繁运行 lint + typecheck + build,避免错误积累
- 立即验证:每次重要变更后立即运行测试确保功能正常
- 类型安全:严格使用 TypeScript 类型,测试中避免
any类型
- 关注点分离:测试专用类型放
tests/@types/,避免污染全局类型定义 - 测试隔离:使用
~/server?query模式实现测试场景隔离
- 数据先行:基于实际 debug logs 而非理论假设进行问题分析
- 简单解决方案优先:例如工具调用去重 > 复杂拆分逻辑
- 验证而非猜测:每次修改必须通过新的测试/logs 验证效果