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:
1 parent
cb564fe981
commit
59a1ac4056
9 files changed
+359
-18
No files matched your search
@@ -44,13 +44,22 @@ const (
|
||||
usageFinalFlushTimeout = 5 * time.Second
|
||||
)
|
||||
|
||||
// insertUsageSQL 写入一条用量样本。**session_id 当前恒为 NULL**(模型适配器跑在
|
||||
// loop 之下、没有会话概念;见迁移 0017 的说明),列留着是为了接线时只改这一处。
|
||||
// insertUsageSQL 写入一条用量样本。session_id 取 ctx 播种的会话标识
|
||||
// (session_ctx.go),**没有会话归属时写 NULL**:管理面板试连、未接循环的
|
||||
// 后台流程本就不属于任何会话,写空串会让它们与真实会话在列里混成一个值。
|
||||
const insertUsageSQL = `INSERT INTO llm_usage
|
||||
(session_id, purpose, adapter, model, prompt_tokens, completion_tokens,
|
||||
cache_hit_tokens, cache_miss_tokens, reasoning_tokens, total_tokens, took_ms)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
||||
|
||||
// usageSessionArg 把会话标识转成可绑定的值:空串 ⇒ NULL。
|
||||
func usageSessionArg(sessionID string) any {
|
||||
if sessionID == "" {
|
||||
return nil
|
||||
}
|
||||
return sessionID
|
||||
}
|
||||
|
||||
// UsageWriter 把用量样本异步落进 `llm_usage`。它实现 UsageSink,因此在模型适配器
|
||||
// 里就是一个「可选出口」:注入即有、不注入则只记日志。
|
||||
//
|
||||
@@ -234,9 +243,8 @@ func insertUsageBatch(ctx context.Context, db *sql.DB, batch []UsageSample) erro
|
||||
defer stmt.Close()
|
||||
|
||||
for i, sample := range batch {
|
||||
// session_id 传 NULL:见 insertUsageSQL 的说明。
|
||||
if _, err := stmt.ExecContext(ctx,
|
||||
nil,
|
||||
usageSessionArg(sample.SessionID),
|
||||
sample.Purpose,
|
||||
sample.Adapter,
|
||||
sample.Model,
|
||||
|
||||
Reference in new issue
Block a user