25 Commits
Author SHA1 Message Date
Sakurasan 2e3bb22f81 fix: use weighted random pick for load balancing instead of sequential iteration 2026-09-02 17:32:12 +08:00
Sakurasan 923ac79039 删除请求超时 2026-09-02 13:08:32 +08:00
Sakurasan d8257df100 fix: messages 流式缓存场景 token 记账错乱
qwen/dashscope 等上游 messages 流式的 usage 语义:
- message_start.usage.input_tokens = 总输入
- message_delta.usage.input_tokens = 非缓存输入(缓存部分单列
  cache_read/cache_creation 字段),是最终计费口径

原 usageSink 字段级合并中 delta 的 input 覆盖 start 的 input,
总输入丢失(31790 → 8);缓存写也未参与计费。

- push:带 cache_* 字段的 usage 视为最终口径,整体替换 sink
- finishUsage:缓存写按 1.25× 输入价计费(Anthropic 5m 口径);
  落库 input_tokens 存总量(含缓存读/写)便于对账
- 估算兜底条件排除已有缓存计数的请求
- 回归测试:缓存写/缓存命中/chat 末块合并不回归
2026-08-28 18:11:24 +08:00
Sakurasan 0b31cc209e fix: chat→responses 转换 input 始终输出数组
单条消息时原逻辑把 input 塌缩成单个对象,火山方舟等上游只接受
[]*InputItem 数组,直接 400(Mismatch type)。chat/messages 经
vole 渠道转换因此全挂。改为恒为数组,三种协议全矩阵实测恢复。
2026-08-27 09:57:48 +08:00
Sakurasan 79545aa48e fix: 兼容上游 SSE data: 后无空格格式,流式记账不再 0 消耗
火山方舟等上游流式返回 data:{...}(data: 后无空格),原解析只认
'data: '(带空格),导致 content 文本与 usage 全部漏解析:
- sseContentText 抽成 sseDataPayload,统一处理 data: 前缀
  (支持带/不带空格 + event:+data: 多行块)
- scanUsage 复用 sseDataPayload
- convert/stream.go parseLine 也支持 data:{ 无空格
- 新增回归测试覆盖无空格/带空格/多行块三种形态
2026-08-27 01:24:30 +08:00
Sakurasan a45a6e16bd fix: API Key 前缀调整为 sk-ot- 2026-08-26 15:57:04 +08:00
Sakurasan c78a473e59 feat: API Key 前缀改为 sk-ot
- apikey 包 prefix 常量 sk- → sk-ot(生成/校验/展示前缀统一驱动)
- 鉴权错误提示同步更新为 sk-ot 格式
- 测试断言改用常量,避免硬编码长度
- 前端 curl 示例与 README/PLANNING 文档同步
2026-08-25 00:34:50 +08:00
Sakurasan f7a5741b33 fix: 模型列表仅显示已启用渠道中的模型 2026-08-22 14:19:45 +08:00
Sakurasan 10f51cbdae 请求明细支持查看原始请求/响应:OT_PROXY_LOG_RAW 开关控制,仅管理员记录与可见,流式全量捕获
- 配置: ProxyConfig.LogRaw (OT_PROXY_LOG_RAW, 默认 false)
- 存储: usage_logs 新增 raw_request/raw_response 文本列 (AutoMigrate)
- 网关: NewGateway 接收 logRaw 参数
- handlers: 三个协议入口按 开关+管理员 条件记录原始请求体
- passthrough: 非流式 copyAndCapture 捕获响应, 流式 streamCopy 累积全部原始 SSE 行, finishUsage 统一写入
- admin API: AdminUsage 返回 raw_request/raw_response (仅管理员)
- 前端: 用量页新增查看入口, 弹窗 tab 切换请求/响应
- gitignore: 修正 server/web/ 忽略规则(尾随空格导致未生效)
2026-08-20 00:37:48 +08:00
Sakurasan 0939f98fb5 修复流式请求记账为 0 消耗:支持 SSE 多行块 usage 提取 + 无上游 usage 时 tiktoken 估算
- sseContentText/scanUsage 兼容 event:+data: 多行块(转换器 eventLine 产出)
- usageFromMap 新增 delta.usage 支持(Anthropic message_delta 标准格式)
- finishUsage: 流式成功但上游不返回 usage(如火山方舟)时按内容 tiktoken 估算,in/out 独立估算
- .gitignore 忽略 server/web 本地静态托管软链
2026-08-19 20:59:04 +08:00
Sakurasan 30ab9e842c claude code tools error 2026-08-19 03:46:39 +08:00
SakurasanandClaude 967c54ae32 代理: 鉴权支持 Anthropic x-api-key 头
Claude Code / Anthropic SDK 用 x-api-key 头而不是 Authorization:
Bearer,缺失 Authorization 时回退读取 x-api-key,错误文案同步提示两种方式

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-18 00:51:53 +08:00
SakurasanandClaude 0ee92d78e7 代理: 流式中断按已生成部分计费
- 新增 tokenizer 包(tiktoken-go)按模型估算 token, 未知模型回退 cl100k_base
- passthrough 提取请求输入文本 + SSE 已生成内容, 客户端断开时记 canceled
- usage 计费范围扩展: canceled(流式中断)按已生成部分收费

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-16 09:04:18 +08:00
SakurasanandClaude 4b507d8064 转换器: 图片内容块跨协议互转保留(base64/url)
- 支持 OpenAI image_url ↔ Anthropic image.source(base64/url) 双向转换
- messages→chat / responses→chat 不再丢弃图片块, 保留图文原始顺序
- chat→messages 的 data URL 拆成 base64 source(media_type+data)
- chat→responses 的 image_url 转 input_image
- responses↔messages 经 chat 中转, 六方向全覆盖
- 新增多图/顺序回归测试

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-16 09:04:09 +08:00
SakurasanandClaude e96f194a59 转换器: 修复 Responses 流式 response.completed 缺 usage
- 上游 chat 流 finish_reason 块先于 usage 块, 原实现在 finish 时即发
  response.completed 导致 usage 为空; 改为等 usage 块到达(或 [DONE] 兜底)再发,
  completed 携带 input/output tokens

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-16 05:05:46 +08:00
SakurasanandClaude db83972b6f 渠道: 分协议 Base URL + 模型抽屉/远程拉取/操作图标
- 渠道支持分协议 base_url(chat/responses/messages 各一), 网关按协议选 base 直通
  (如智谱三种格式不同 base, 一个渠道即可), UpstreamURL 按 proto 拼接
- 渠道模型改为下方抽屉: 当前绑定列表(内联改上游/解除)、从接口拉取(remote 预览+勾选添加)、手动添加
- 新增 /channels/:id/models/remote 预览接口; 操作按钮加 Phosphor 图标
- 修复 formats jsonb 更新未序列化问题; 手机端渠道卡片化

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-16 04:31:28 +08:00
SakurasanandClaude b365779188 模型限制: 系统配置全局开放/禁止 + 用户级限制 + 网关拦截
- User 新增 allowed_models/denied_models(用户级模型限制)
- 系统配置 model_allowlist/model_denylist 全局策略, 保存即时失效网关缓存
- 网关 checkModelAllowed: 用户级 > 全局(禁止命中→403, 白名单非空→仅白名单)
- 三个协议处理器均校验, 错误按客户端协议格式返回
- 配置页"模型限制"卡片(全局允许/禁止多选); 用户编辑支持允许/禁止模型

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-16 01:34:12 +08:00
SakurasanandClaude 4d4d09ba58 渠道: 模型名称映射(网关生效 + 渠道侧管理)
- 候选渠道携带 upstream_model, prepareUpstream 改写请求体 model 字段为上游名
- 新增渠道视角绑定 CRUD: GET/POST/PATCH/DELETE /admin/channels/:id/models
- 前端渠道页新增"模型映射": 列出绑定、内联改上游名、解除、添加
- rewriteModel 三种协议通用(model 均在顶层)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-16 00:09:01 +08:00
SakurasanandClaude 8d9940ad5a 渠道: Base URL 智能识别前缀/完整端点 + API 格式垂直堆叠
- upstreamURL 按内容智能拼接: 完整端点直接用 / 含 /v1 只拼资源路径 / 纯域名拼全路径
- resolveBaseURL 不再去 /v1, 交网关识别
- 渠道表 API 格式改垂直堆叠, 显示短标识 chat/completions / responses / messages
- 前端 Base URL 提示支持前缀或完整端点

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-16 00:08:52 +08:00
SakurasanandClaude d0bc28a4fe 渠道: 移除主页/favicon 特性, Base URL 可选+完整地址
- 回退渠道 homepage/favicon 字段与代理接口(未采用)
- Base URL 改为可选: 留空按供应商默认(openai/anthropic),
  兼容兼容型渠道必须填; 填完整地址(含 /v1)时归一化去尾
- 网关 upstreamURL 兜底去 /v1, 避免路径重复

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-15 20:55:17 +08:00
SakurasanandClaude 5eff32afd8 渠道支持多 API 格式 + 协议全名展示
- Channel 新增 formats(jsonb: chat|responses|messages), 一个渠道可同时声明
  支持 OpenAI Chat Completions / OpenAI Responses API / Anthropic Messages
- 路由改为按渠道声明的 formats 决定直通/转换: 客户端协议在 formats 内直通,
  否则转换为其首选支持格式(chat > messages > responses)
- 兼容旧数据: formats 为空时按 provider 推断(openai→chat+responses, anthropic→messages, compatible→chat)
- 前端: 渠道表/表单展示 API 格式(支持多选), usage 明细显示协议全名
  (OpenAI Chat Completions / OpenAI Responses API / Anthropic Messages)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-15 16:47:03 +08:00
SakurasanandClaude 4846db9293 M3 收尾: 限流/配额 + 前端骨架屏 + 单端口托管前端
- ratelimit(内存计数): 密钥级每日请求数/Token 配额、用户级每秒速率
  (OT_RATELIMIT_USER_RPS), 超限返回 429
- 网关 Auth 前置配额/限流检查, finishUsage 累计密钥 token 用量
- 前端 Skeleton 组件 + Dashboard/管理总览加载态
- Go 服务托管 web/dist 静态资源(SPA 回退), 单端口即可访问前后端

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-15 16:13:09 +08:00
SakurasanandClaude 0637ce0a51 M5: 渠道体系(负载均衡+并发控制+健康检查+故障转移)
- channel.Candidates 按模型绑定取候选 + Pick 加权随机负载均衡
- TryAcquire 每渠道并发信号量, 满载溢出到其他候选
- HealthMonitor 后台定时探测, 连续失败进 cooldown, 恢复放回(可配 interval/threshold)
- doProxy 遍历候选故障转移: 网络错误/429/5xx/超时且未写出响应头时安全重试;
  400 等业务错误透传, 流式写出首字节后放弃重试
- 单测覆盖候选过滤/加权/并发/健康状态机
- E2E: 杀上游自动切换、cooldown、恢复、并发溢出 10/10

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-15 15:57:48 +08:00
SakurasanandClaude ec4de8d913 M0-M4: 推倒重来基线(基建+用户/密钥/核心代理+前端+管理后台+三协议互转)
- 后端 Go+Gin+GORM: 配置(OT_ env)/SQLite/Postgres 双驱动、用户体系(argon2id+JWT access/refresh)、
  API Key(sk- 48位, 仅存 SHA-256 哈希)
- 代理网关: /v1/chat/completions、/v1/responses、/v1/messages、/v1/models;错误按客户端协议返回
- 三协议互转(convert 包): Chat↔Messages↔Responses 请求/响应 + 流式 SSE 逐事件转换(直通优先)
- 用量计费: 异步批量记账、余额扣减、balance_logs、usage_daily 日聚合
- 管理 API: 用户/渠道 CRUD+测试+模型导入/模型定价+绑定/统计/系统配置
- 前端 Vue3+TS+Tailwind(taste-skill 设计 tokens): Landing/登录注册/控制台/管理后台,
  自建组件+Phosphor 图标+自建 SVG 趋势图, 已过 web-design-guidelines 复查
- mock 上游: OpenAI+Anthropic 双协议模拟(含流式)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-15 15:34:06 +08:00
Sakurasan 360c6b33a6 M0+M1: 基建 + 用户/密钥/核心代理
后端 (Go/Gin/GORM):
- 配置(viper+env)、SQLite/Postgres 迁移、argon2id、AES-GCM 渠道密钥、JWT+refresh cookie
- 用户注册/登录/刷新/登出、API Key CRUD(仅存哈希、明文一次展示)
- 代理网关: /v1/chat/completions、/v1/responses、/v1/models 直通 OpenAI 渠道
  非流式+流式(SSE 零缓冲转发), 用量捕获(chat 末块/responses completed 嵌套),
  OpenAI 错误格式(401/402/404/502), 余额检查
- 异步批量记账 + 余额流水 + 日聚合, admin 用户/余额/配置 API
- 单测: crypto/jwt/apikey/流式 usage 提取

前端 (Vue3+TS+Vite+Tailwind v4):
- taste-skill 设计 tokens: 深色仪表盘, 石墨+信号铜色, Outfit+JetBrains Mono
- Landing/登录/注册, 控制台(仪表盘图表/密钥管理/用量明细)
- 基础组件 Button/Input/Badge/Modal, ECharts 用量图

部署: docker-compose(nginx+api+postgres), 双 Dockerfile, nginx SSE 反代
联调: scripts/mockupstream 本地 mock 上游, 端到端验证通过
2026-08-15 13:10:47 +08:00