Skip to content

Tags: EchoPing07/Qoder-2API-Go

Tags

v0.4.2

Toggle v0.4.2's commit message
fix: 发布构建注入登录页版本号(release 二进制与 Docker 镜像)

目的:
登录页版本号由构建期 ldflags -X main.Version 注入(main.go Version → web.New →
VersionDisplay),但 release.yml 与 Dockerfile 的 go build 均未注入,导致
release 产物与 ghcr 镜像的登录页恒显示 dev,无法区分实际发布版本。

主要变更:
- release.yml (build-binaries): 新增 Extract version 步骤——tag 触发取 tag 名
  去 v 前缀(按 GITHUB_REF_TYPE=tag 判断,避免误用分支名),workflow_dispatch
  等分支触发回退 git describe;构建命令追加 -X main.Version
- release.yml (build-docker): 同样的版本提取,经 build-args VERSION 传入 Dockerfile
- Dockerfile: 新增 ARG VERSION=dev,构建追加 -ldflags "-X main.Version=${VERSION}"
- README.md: Docker 手动构建示例补充 --build-arg VERSION 用法说明

影响范围:
仅构建与发布链路(CI 工作流、Dockerfile、文档),不改任何 Go 代码与运行时行为;
本地 go build 不受影响,版本仍回退 dev。下次打 v* tag 发布即生效,
已验证 tag 触发注入 1.2.3、分支触发注入 git describe 结果。

v0.4.1

Toggle v0.4.1's commit message
feat: 新增请求日志(元信息审计 + 面板日志页),统计补充实际扣费与均值口径

新增 logs 包:请求路径上只做内存追加,由 30s 定时器与优雅关闭统一落盘 data.json
(logs 字段)。仅记录元信息——时间、模型、密钥 ID/备注、是否流式、token 用量、
credits、耗时、状态码与截断至 500 字符的错误摘要,不含任何对话内容;按保留天数
与条数上限自动裁剪最旧条目,避免 data.json 无限增长。

主要变更:
- logs: 新 Recorder(Record/Query/Flush/SetLimits),过滤支持模型模糊、密钥精确、
  状态码与「仅失败」,分页返回;持久化经 Persister 接口复用 store.Store。
- store: Config 增加 logs / log_retention_days / log_max_entries 的读写与校验
  (缺失或越界自动归一化/夹取);新增常数时间的 LookupKey,返回密钥 ID 与备注。
- bridge: BridgeResolver 改为返回 ResolvedBridge(bridge + KeyID/KeyNote);chat
  handler 全错误路径埋点、defer 记录耗时,鉴权失败不落日志;上游状态码映射抽为
  clientStatusForUpstream,客户端中断记为 499。
- stats: usage 帧按 model 归属到 by_model / hourly;新增 billed_requests、
  credits、avg_credits(分母只算计费帧),趋势 tooltip 显示每小时消耗。
- admin: 新增 GET /admin/api/logs;/admin/api/config 读写日志限额、对 env 固定项
  拒改,保存后即时热应用到 recorder;新增 QODER_LOG_* 环境变量(与超时同优先级)。
- web: 新增日志页(筛选 + 分页),模型页内容并入统计页并从导航移除;设置页新增
  日志配置;统计页新增「实际消耗 Credits」卡与按模型消耗/均价列。

影响范围:
- 数据兼容:data.json 新增字段缺失时取默认值,旧文件无需迁移,历史 credits 与
  周期数据不受影响(billed_requests 自本版起累计)。
- 接口:/v1/chat/completions 响应行为不变,仅增加一次内存写(日志标签在 defer
  记录,错误状态码口径与原回传完全一致);新增 /admin/api/logs 与 config 的两个
  可选字段(0/留空 = 保持当前值)。
- 文档与测试:README 同步参数、接口与计费口径说明;补 logs/store/stats/bridge/
  admin/web 测试。

v0.3.6

Toggle v0.3.6's commit message
v0.3.6

修复 CI 计费周期测试硬编码日期导致的到期式失败(v0.3.5 的 Release 因该
测试失败被跳过,二进制未发布)。

v0.3.5

Toggle v0.3.5's commit message
fix: 修复计费周期清零、思考能力误关与额度显示取错数据源

本次修复覆盖代码审查中发现的全部 23 项缺陷。原则是「最小改动、不过度修复」:
能在桥接层/统计层根治的不在前端打补丁,能在上游语义层确认的不猜测网关行为。

- stats: 周期边界接受规则改为「先本地推进 → 拒绝非前进的陈旧报告 → 前进
  幅度在 7 天内视为同周期 → 超过才清零」。原先按派生起点精确比较,边界
  抖动 1 小时就抹掉本周期累计;且 /user/status 有 1 小时缓存,陈旧报告会
  把已经滚进新周期的累计一起清零。首次观测只采纳边界、不清零,否则新装
  会丢弃同步前累计的 credits。
- stats: addMonthsMs 把日夹取到目标月长度(3/31 减一月得 2/28,闰年得
  2/29)。原先 AddDate 规范化月溢出(3/31 → 3/3),31 号重置的账号每月
  都被误判为新周期并清零。
- stats: Report() 改为纯投影,不再推进边界、不再置 dirty。原先读路径带写
  副作用,面板 15 秒轮询在跨周期瞬间就会清零并持久化本周期累计。
- stats: 滚动循环加「不推进即丢弃边界」与 2400 次循环上限。原先
  addMonthsMs 对少数历史时区(如 America/Asuncion 1972-09)非严格递增,
  循环在持锁下死循环会挂死整个进程。
- stats: 新增 ClearAccount()。原先 SetAccount(nil) 是 no-op,没有任何入口
  能清除已持久化的账号快照。
- models: 能力判定改为「thinking_config.enabled 存在且非 null → 顶层
  is_reasoning → 默认 true」,两个兜底方向统一为 true。原先只信顶层
  flag,导致 dfmodel / kmodel_latest 的思考能力被静默关闭;且字段缺失时
  为 false、整个目录不可达时为 true,同一部署内方向矛盾。
- models: 档位元数据改读真实的嵌套路径 thinking_config.disabled /
  thinking_config.enabled.efforts,保留扁平旧格式兼容。原先读的扁平
  efforts / supports_disabled 在真实目录里不存在,档位校验形同虚设,任意
  字符串(含畸形值)都会被原样转发给上游。
- models: 新增 MaxRequestedOutputTokens = 1_000_000,调用方传入的
  max_tokens 超出即钳制。原先 1e18 之类异常值会原样写入网关请求。
- models: normalizeEfforts 的 map 分支先排序,输出确定化。原先迭代顺序
  随机,结果不可复现。
- bridge: 计费周期改以 /user/status 的 nextResetAt 为准,OpenAPI 的
  expiresAt 仅在缺失时兜底且必须小于 2100-01-01。无期限套餐会把
  expiresAt 填成 9999-12-31 哨兵值,原先它会覆盖真实边界,导致本地周期
  滚动永久失效、面板显示「剩 2912178 天」。统计层加同样的守卫,使已落盘
  的哨兵值不会永久拒绝后续真实上报。
- bridge: 元数据不可用时不再放行 reasoning_effort=none。未声明
  thinking_config.disabled 的模型(gmodel / gfmodel / kmodel /
  kmodel_latest)会以 HTTP 400 拒绝整个请求。
- bridge: resolveAccountStatus 用 5 秒派生超时包住 /user/status 查询。该
  调用位于 refreshMu 临界区内,原先失败会串行阻塞所有等待续期的请求。
- bridge: 新增 errNoUserID 哨兵,首次 jobToken 交换不带 user id 属预期
  降级路径,原先同样打 WARN。
- bridge: fetchAccountStatus 缓存命中分支改为返回克隆。原先直接交出内部
  指针,调用方可绕过 accountMu 改写缓存。
- bridge: resolveMaxTokens 的钳制在 float64 域完成。原先依赖 int,32 位
  构建下 int(1e18) 会回绕并被判为非法,静默回退目录缺省值。
- bridge: parallel_tool_calls 保持原有顶层落点,注释改为陈述事实(网关
  是否读取尚未验证),并补测试锁定,搬移或删除都必须是显式变更。
- admin: 「合计剩余」只累加 available 的池子。原先不可用池子仍全额计入,
  同一张卡上逐行标「不可用」、汇总却把它算进可用合计。
- admin: 额度池 total=0 视为不存在,首行不再固定取 userQuota。免费账号
  的套餐池 total=0、真实额度在 addOnQuota,原先首行渲染成「套餐 8 / 0 ·
  剩 0」加红色超额徽标。
- admin: 移除恒真的 total >= 0 死分支。
- auth: 上游响应日志降为 status + duration,去掉 headers 与 body;非 200
  路径的错误体先按 key 脱敏、再按 rune 边界截断,AuthError.Error() 内部
  同样脱敏(Detail 保持原始供分类使用)。原先每分钟一次打印完整响应体
  (含 userId、trace id、配额明细),约 2 MB/天。
- transform: Usage.Billable 加 json:"-",仅内部传给
  stats.Usage.NonBillable。原先它会随 usage chunk 序列化给 OpenAI 客户端,
  billable=false 会被误读为「这次没算钱」。
- main: 额度刷新 goroutine 纳入优雅关闭(独立的 accountStop /
  accountDone,先 join 再触发最终 flush)。原先它的写回会落在 flush 之后
  并静默丢失。
- main: shutdown 的 drain 与 join 并发启动,但等待 drain 完成后才取消
  context。原先 join 一完成即 cancel,SIGTERM 时会中断所有在途流式请求。
- main: 停止流程用 sync.Once 保证幂等,首个信号后注销 handler,重复触发
  不再 panic。
- main: PAT 变更(含清空)后清除账号快照;写入与清除共享 patWriteMu 并
  在锁内复核 PAT,关闭「在途刷新写回已被清除的旧账号」的时序窗口;启动
  时清一次持久化快照以覆盖重启场景。
- README: 说明 parameters.max_tokens 总是发送属本次的行为变更、档位实效
  (除 none 外差异落在采样噪声内)、周期边界接受规则,以及「合计剩余」
  只统计可用池子的口径。

验证:go build ./... / go vet ./... / go test ./... -count=2 全绿;
GOARCH=386 与 5 个时区(UTC、Asia/Shanghai、America/New_York、
Pacific/Chatham、Australia/Lord_Howe)全绿;改动文件去 CRLF 后 gofmt
无差异。关键回归测试均经变异验证:回退修复即失败。

v0.3.4

Toggle v0.3.4's commit message
修复网关 10605 并发拒绝被误判为登录过期导致的频繁报错

目的:
网关的排队/并发准入拒绝(业务码 10605,携带 isQueued/queueCount/
retryAfterSeconds 等队列元数据)复用 HTTP 401/403 信封与流内
statusCodeValue 帧下发,此前被一律判定为登录过期,触发无意义的
jobToken 强制刷新 + 立即重发(再次撞上同一并发窗口),客户端并发调用时
几乎必现 "HTTP 401 403 {\"code\":\"10605\",...}" 报错。

主要变更:
- auth: 新增 BusyError 类型与 10605 解析(HTTP 信封 + 流内帧两条路径),
  code 兼容字符串/数值两种形态;10605 不再归类为 AuthError
- auth: 错误体读取上限 300B → 4KiB,避免长 body 截断致 JSON 解析失败
  而回落误判;上游回显消息截断至 512 runes
- auth: 流内 busy 检测增加 statusCodeValue 廉价预判,避免对每条 SSE
  内容帧做完整 JSON 反序列化
- bridge: openStreamAsync 对 BusyError 改为按 retryAfterSeconds 退避后
  重试一次(不再刷新令牌);回退等待提取为可注入变量便于测试
- bridge: HandleChat 新增每 PAT 上游并发闸门(信号量,默认 1,超出本地
  排队),从源头避免触发网关限流;可用 QODER_MAX_CONCURRENCY(1-32)
  调整
- bridge: 非流式路径忙错误映射为 HTTP 429 + Retry-After(原为 500),
  便于 OpenAI 兼容客户端自动退避重试

影响范围:
- /v1/chat/completions 同步与流式路径的错误分类、重试行为与状态码映射;
  /v1/models 及管理面板不受影响
- 新环境变量 QODER_MAX_CONCURRENCY;README 已补充说明
- 行为变化:并发请求默认在本地排队串行执行(原为直冲网关被拒)

测试:auth 新增 5 个用例(数值 code、长 body、消息截断、预判不误伤等),
bridge 新增 3 个用例(busy 退避重试且不刷新令牌、429/SSE 错误块映射、
并发闸门实测上游并发=1)。go build/vet/test 全部通过。

v0.3.3

Toggle v0.3.3's commit message
新增 Chat 响应超时与流空闲超时自定义(env/面板热生效)及 /health 端点;修复空闲看门狗定时器竞态与超时错误回报不一致

v0.3.2

Toggle v0.3.2's commit message
修复移动端导航项宽度异常;忽略 work 目录

- admin/webui.go: 移动端导航项显式设置 width: auto,防止 flex 布局下导航项被拉伸导致宽度错乱
- .gitignore: 新增 work/ 忽略规则,避免本地工作目录被纳入版本控制

影响范围:
- 仅影响管理面板移动端导航栏渲染样式
- .gitignore 不影响已跟踪文件

v0.3.1

Toggle v0.3.1's commit message
修复多模型流式 usage 丢失与流终止误判;修正 UI 显示问题

改动目的:
- 真实网关不同模型族的 SSE 流终止与 usage 帧形状存在差异,上次提交
  的 usage 解析仅覆盖 Qwen 形状,GLM/DeepSeek 系 usage 被静默丢弃、
  MiniMax 整流被误判截断报错;另修复 [DONE] 检测不识别网关信封
  包裹格式导致的 "stream ended without [DONE] marker" 报错

主要变更:
- usage 解析(transform/bridge):usage 不再与 choices 互斥——GLM 系
  (gmodel/gm51model)与 DeepSeek 系(dmodel/dfmodel)的 usage 附在
  末帧 choices(finish_reason=stop)上而非独立终帧,改为同帧双取:
  ExtractDelta 先提取 usage 再处理内容,流式/同步回调捕获 usage 后
  不再提前 return
- 流终止(auth):新增 isDoneLine 识别两种 [DONE] 形式(裸
  "data: [DONE]" 与信封包裹 {"body":"[DONE]"});新增 isFinishEvent
  将 "event:finish" 认作合法终止——MiniMax(mmodel)流以此事件结束、
  无 [DONE] 帧,此前被误判为连接截断
- 管理面板:修正密钥列表「创建时间」列显示错位问题
- 测试:新增 GLM 风格末帧、MiniMax 风格 event:finish 终止、
  信封 [DONE] 格式三组真实 wire 格式回归测试;修正旧测试中
  固化"choices 与 usage 互斥"错误假设的断言

影响范围:
- 修复后全部模型族(Qwen/GLM/DeepSeek/Kimi/MiniMax)usage 与统计
  均正常:真实 PAT e2e 7 请求全 PASS,stats 逐 token 精确核对
  (prompt 322 / completion 444);单测全绿
- 客户端可见变化:GLM/DeepSeek 系模型流式响应现在会回传 usage 帧、
  非流式响应携带真实 usage(此前为 0);MiniMax 请求不再报
  stream ended without [DONE] 错误;其余行为不变

v0.3.0

Toggle v0.3.0's commit message
传递真实 token 用量统计并重做统计面板;修复代码审查发现的缺陷

改动目的:
- 网关在流结束前会发送 usage 终帧(空 choices + usage),此前被整体丢弃,
  导致无法统计真实 token 消耗与实际扣费额度
- 修复代码审查遗留及新增发现的缺陷:登录退避溢出、PAT 明文回显、
  改密后会话未失效、SSE 无空闲超时、流提前截断被当作成功、
  持久化无 fsync、并发竞态、工具调用累积上限可被绕过等

主要变更:
- 用量传递:解析网关 usage 终帧(prompt/completion/total tokens、
  cached_tokens、reasoning_tokens、credits),流式响应按
  stream_options.include_usage 约定在 [DONE] 前回传 usage 帧,
  非流式响应透传 usage;两路径均经 UsageSink 接入统计
- 统计与 UI:stats 新增 token 总量与 Credits 累计(RecordUsage),
  移除流式/同步拆分计数;管理面板统计卡改为总请求/成功率、
  Token 总数/Credits、总输入/缓存命中、总输出/平均输出,
  并修复窄屏下统计网格的响应式布局
- 安全修复:登录退避 1<<63 溢出导致锁死失效(钳制到 30s 上限);
  修改密码后全量失效会话;QODER_ADMIN_PASSWORD 模式下禁止面板改密;
  PAT 接口不再回显明文;clientIP 改用 net.SplitHostPort 支持 IPv6
- 健壮性修复:SSE 空闲超时 5 分钟;EOF 前未见 [DONE] 视为截断错误;
  store 写入增加 fsync 防崩溃截断;catalog 拉取单飞防并发堆叠;
  expireTime 缺失时按 30 分钟 TTL 兜底,避免每次请求都续期;
  identity 快照加锁消除数据竞争;工具调用累积硬上限不可绕过且保留
  上游 index;finish_reason 透传;UTF-8 安全截断(truncateRunes);
  动态目录缺省模型优先廉价档位(flash/lite/plus)
- 构建/清理:baseprompt.json 删除,请求模板内联进 bridge 包;
  CI 新增 go vet + go test -race 测试作业并在构建前执行;
  发布产物与 Docker 镜像不再打包模板文件

影响范围:
- 涉及 transform、bridge、stats、admin、auth、store、models 全部核心
  模块及对应测试,新增大量端到端与回归测试
- API 行为变化:流式响应末尾新增 usage 帧、非流式响应新增 usage 字段,
  对 OpenAI 兼容客户端无破坏性影响
- 管理面板统计口径变化:移除流式/同步拆分,新增 token/Credits 指标;
  /admin/api/pat 不再返回明文 PAT

v0.2.1

Toggle v0.2.1's commit message
安全加固与健壮性修复:代码审查全量整改

安全:
- HTTP Server 增加 ReadHeaderTimeout/IdleTimeout,抵御 slowloris
- /v1/chat/completions 与管理 API 请求体加 MaxBytesReader 上限
- API Key 校验与管理密码比对改用 crypto/subtle 恒定时间比较
- 登录按客户端 IP 指数退避限流,429 + Retry-After
- 会话 Cookie 在 HTTPS 下自动附加 Secure
- WebUI 移除内联 onclick 拼接改 addEventListener,消除 XSS

并发/健壮性:
- bootstrapped 改 atomic.Bool 消除 data race;删除死字段 templateLoaded
- ToolCallAccumulator 索引边界保护,防负/极大索引 panic 与 OOM
- LoadStats 返回深拷贝;导出 Data.Clone()
- 客户端主动断开不计入失败统计

生命周期/资源:
- SIGINT/SIGTERM 优雅关闭 + 统计最终落盘
- 上游错误响应体排空以利连接复用
- 删除死代码 UserStatus/Heartbeat 及 runtime 导入

数据校验:
- API Key 唯一性校验(重复 409);端口范围 1-65535;rand 错误传播
- models.enable 非 bool 值容错

测试:
- 新增 5 个回归测试覆盖上述改动

文档:
- README 新增安全章节、错误响应约定、优雅关闭说明