feat(model): llm_usage.session_id 真正落库(补完 U1 缺口)

通道:新增 model/session_ctx.go 的 WithSessionID / SessionIDFrom(与 tool.WithTurnPersona
同族的 ctx 范式,避开 model 反向 import loop 的环),loop 在 Run 顶部播种;适配器
record(ctx,...) 接会话;usageSessionArg 空串映射为 NULL。
兼容:sink 未注入或会话未播种时行为逐字不变;既有 model 测试零断言改动即全绿。
文件:model/{session_ctx.go(新), usage.go, usage_store.go, openai.go, anthropic.go,
usage_session_test.go(新)} + loop/{run.go, usage_session_test.go(新)}。
变异三处:去 loop 打标 / 写侧不绑会话 / 适配器不读 ctx ⇒ 各自红行,还原复绿。
契约与日志字段集未动;读侧不按 session 过滤,面板汇总口径不变。
This commit is contained in:
AskaEth committed 2026-10-05 08:04:49 +08:00
1 parent cb564fe981
commit 59a1ac4056
9 files changed
+359 -18

No files matched your search

+5 -4
View File
@@ -152,7 +152,7 @@ func (a *OpenAIChatAdapter) Chat(ctx context.Context, req Request, opts Options)
return Response{}, fmt.Errorf("model: %w: decode response: %v", ErrUpstream, err)
}
out := responseFromWire(&wire)
a.record(req, start, out)
a.record(ctx, req, start, out)
return out, nil
}
@@ -189,7 +189,7 @@ func (a *OpenAIChatAdapter) ChatStream(ctx context.Context, req Request, onEvent
return Response{}, err
}
out.Model = model
a.record(req, start, out)
a.record(ctx, req, start, out)
return out, nil
}
@@ -241,8 +241,9 @@ func (a *OpenAIChatAdapter) upstreamError(resp *http.Response) error {
// record 上报一次成功调用的用量:先记一条结构化用量日志(含缓存命中率,
// 所有调用点都覆盖 —— 见 usage.go),再喂给可选的 CallRecord 计账出口。
func (a *OpenAIChatAdapter) record(req Request, start time.Time, resp Response) {
logUsage(a.log, a.usageSink, a.name, req, resp, time.Since(start))
// ctx 用来取本次调用所属的会话(loop 播种,见 session_ctx.go)。
func (a *OpenAIChatAdapter) record(ctx context.Context, req Request, start time.Time, resp Response) {
logUsage(ctx, a.log, a.usageSink, a.name, req, resp, time.Since(start))
if a.recorder == nil {
return
}