feat: 补齐三个方向的流式工具调用;Anthropic 上游走 x-api-key;禁用模型真正下架

- 流式转换补齐 chat→responses / responses→chat / messages→responses 的工具调用(此前只转文本,SDK 客户端拿不到 tool_calls);completed 事件携带完整 output 数组
- Anthropic 协议上游同时发 x-api-key(官方 API 不接受 API key 走 Bearer)并透传 anthropic-beta
- 请求侧校验模型存在且启用:禁用模型 404 model_not_found,不再只是从 /v1/models 消失、实际照发照扣费;空模型名 400
- 模型在请求中途被删时按 0 记账留痕,不再按硬编码示例价扣用户的钱
- finishUsage 新增一行式请求日志(trace/user/model/channel/tokens/cost/status/latency),记账是异步的,日志是唯一即时排障依据
This commit is contained in:
Sakurasan committed 2026-10-29 02:56:37 +08:00
1 parent 984a31cbc5
commit d6db0f8162
6 files changed
+485 -31

No files matched your search

+17
View File
@@ -128,6 +128,23 @@ func (g *Gateway) checkModelAllowed(c *gin.Context, u *store.User, model string)
return true
}
// checkModelExists 拦截不存在或已禁用的模型。/v1/models 只列启用的模型,请求侧口径必须一致:
// 禁用模型不能只是从列表消失——否则照样能调用、照样计费,管理员的下架操作形同虚设。
func (g *Gateway) checkModelExists(c *gin.Context, model string) bool {
if model == "" {
apiError(c, http.StatusBadRequest, "invalid_request_error", "model is required")
return false
}
var n int64
g.db.Model(&store.Model{}).Where("name = ? AND enabled = ?", model, true).Count(&n)
if n == 0 {
apiError(c, http.StatusNotFound, "model_not_found",
fmt.Sprintf("The model %q does not exist or is not enabled", model))
return false
}
return true
}
func contains(list []string, s string) bool {
for _, v := range list {
if v == s {