优化: token分项显示(in/cache/out/reasoning)+ 详情面板 + base前置
token分项(各计费不同,不显 total):df-ai 解析 provider cache/reasoning(openai_compat prompt_cache_hit/miss/reasoning_tokens + anthropic cache_read/creation)+ TokenUsage 加字段(全构造点)+ AiMessage/AiCompleted/DB V39(ai_messages 加 cache_hit/miss/reasoning 列)+ message_repo 映射(持久化)+ 前端 MessageList 显 in·cache·out·reason(in=cache_miss 全价,reasoning 有才显)+ 点击 token 弹详情面板(完整 usage+缓存命中率+model)+ df-miniapp 同步 base前置(提升 prompt cache 命中率):chat.rs aug 拼 base 后(4处)+ knowledge_inject 知识拼 base 后(固定 base 前缀,cache 命中) 附修:replace_conversation 原 13 列 INSERT 丢消息级 token → 改 18 列
This commit is contained in:
@@ -43,6 +43,9 @@ fn ai_message_from_row(row: &Row<'_>) -> std::result::Result<AiMessageRecord, ru
|
||||
created_at: row.get("created_at")?,
|
||||
prompt_tokens: row.get("prompt_tokens")?,
|
||||
completion_tokens: row.get("completion_tokens")?,
|
||||
prompt_cache_hit_tokens: row.get("prompt_cache_hit_tokens")?,
|
||||
prompt_cache_miss_tokens: row.get("prompt_cache_miss_tokens")?,
|
||||
reasoning_tokens: row.get("reasoning_tokens")?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -82,8 +85,9 @@ impl AiMessageRepo {
|
||||
"INSERT OR IGNORE INTO ai_messages
|
||||
(id, conversation_id, seq, role, content, parts, tool_call_id,
|
||||
tool_calls, model, status, reasoning_content, timestamp, created_at,
|
||||
prompt_tokens, completion_tokens)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15)",
|
||||
prompt_tokens, completion_tokens,
|
||||
prompt_cache_hit_tokens, prompt_cache_miss_tokens, reasoning_tokens)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18)",
|
||||
)
|
||||
.map_err(storage_err)?;
|
||||
for rec in &records {
|
||||
@@ -91,7 +95,8 @@ impl AiMessageRepo {
|
||||
rec.id, rec.conversation_id, rec.seq, rec.role, rec.content,
|
||||
rec.parts, rec.tool_call_id, rec.tool_calls, rec.model, rec.status,
|
||||
rec.reasoning_content, rec.timestamp, rec.created_at,
|
||||
rec.prompt_tokens, rec.completion_tokens
|
||||
rec.prompt_tokens, rec.completion_tokens,
|
||||
rec.prompt_cache_hit_tokens, rec.prompt_cache_miss_tokens, rec.reasoning_tokens
|
||||
])
|
||||
.map_err(storage_err)?;
|
||||
}
|
||||
@@ -116,7 +121,8 @@ impl AiMessageRepo {
|
||||
.prepare(
|
||||
"SELECT id, conversation_id, seq, role, content, parts, tool_call_id,
|
||||
tool_calls, model, status, reasoning_content, timestamp, created_at,
|
||||
prompt_tokens, completion_tokens
|
||||
prompt_tokens, completion_tokens,
|
||||
prompt_cache_hit_tokens, prompt_cache_miss_tokens, reasoning_tokens
|
||||
FROM ai_messages WHERE conversation_id = ?1 ORDER BY seq ASC",
|
||||
)
|
||||
.map_err(storage_err)?;
|
||||
@@ -157,12 +163,14 @@ impl AiMessageRepo {
|
||||
let sql = if before_seq.is_some() {
|
||||
"SELECT id, conversation_id, seq, role, content, parts, tool_call_id,
|
||||
tool_calls, model, status, reasoning_content, timestamp, created_at,
|
||||
prompt_tokens, completion_tokens
|
||||
prompt_tokens, completion_tokens,
|
||||
prompt_cache_hit_tokens, prompt_cache_miss_tokens, reasoning_tokens
|
||||
FROM ai_messages WHERE conversation_id = ?1 AND seq < ?2 ORDER BY seq DESC LIMIT ?3"
|
||||
} else {
|
||||
"SELECT id, conversation_id, seq, role, content, parts, tool_call_id,
|
||||
tool_calls, model, status, reasoning_content, timestamp, created_at,
|
||||
prompt_tokens, completion_tokens
|
||||
prompt_tokens, completion_tokens,
|
||||
prompt_cache_hit_tokens, prompt_cache_miss_tokens, reasoning_tokens
|
||||
FROM ai_messages WHERE conversation_id = ?1 ORDER BY seq DESC LIMIT ?2"
|
||||
};
|
||||
let mut stmt = guard.prepare(sql).map_err(storage_err)?;
|
||||
@@ -273,19 +281,25 @@ impl AiMessageRepo {
|
||||
)
|
||||
.map_err(storage_err)?;
|
||||
// 再批量插新行(INSERT OR IGNORE 幂等,id 冲突跳过)
|
||||
// 含 token 全列(prompt/completion/cache_hit/cache_miss/reasoning,2026-08-02 对齐 insert_batch),
|
||||
// 全量重写不丢消息级 token 数据。
|
||||
if !records.is_empty() {
|
||||
let mut stmt = tx.prepare(
|
||||
"INSERT OR IGNORE INTO ai_messages
|
||||
(id, conversation_id, seq, role, content, parts, tool_call_id,
|
||||
tool_calls, model, status, reasoning_content, timestamp, created_at)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13)",
|
||||
tool_calls, model, status, reasoning_content, timestamp, created_at,
|
||||
prompt_tokens, completion_tokens,
|
||||
prompt_cache_hit_tokens, prompt_cache_miss_tokens, reasoning_tokens)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18)",
|
||||
)
|
||||
.map_err(storage_err)?;
|
||||
for rec in &records {
|
||||
stmt.execute(params![
|
||||
rec.id, rec.conversation_id, rec.seq, rec.role, rec.content,
|
||||
rec.parts, rec.tool_call_id, rec.tool_calls, rec.model, rec.status,
|
||||
rec.reasoning_content, rec.timestamp, rec.created_at
|
||||
rec.reasoning_content, rec.timestamp, rec.created_at,
|
||||
rec.prompt_tokens, rec.completion_tokens,
|
||||
rec.prompt_cache_hit_tokens, rec.prompt_cache_miss_tokens, rec.reasoning_tokens
|
||||
])
|
||||
.map_err(storage_err)?;
|
||||
}
|
||||
@@ -356,6 +370,9 @@ mod tests {
|
||||
created_at: now_millis_str(),
|
||||
prompt_tokens: None,
|
||||
completion_tokens: None,
|
||||
prompt_cache_hit_tokens: None,
|
||||
prompt_cache_miss_tokens: None,
|
||||
reasoning_tokens: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -515,6 +532,9 @@ mod tests {
|
||||
created_at: now.clone(),
|
||||
prompt_tokens: None,
|
||||
completion_tokens: None,
|
||||
prompt_cache_hit_tokens: None,
|
||||
prompt_cache_miss_tokens: None,
|
||||
reasoning_tokens: None,
|
||||
},
|
||||
AiMessageRecord {
|
||||
id: "new_1".into(),
|
||||
@@ -532,6 +552,9 @@ mod tests {
|
||||
created_at: now,
|
||||
prompt_tokens: None,
|
||||
completion_tokens: None,
|
||||
prompt_cache_hit_tokens: None,
|
||||
prompt_cache_miss_tokens: None,
|
||||
reasoning_tokens: None,
|
||||
},
|
||||
];
|
||||
repo.replace_conversation("conv", records).await.expect("replace");
|
||||
@@ -601,6 +624,9 @@ mod tests {
|
||||
created_at: now,
|
||||
prompt_tokens: None,
|
||||
completion_tokens: None,
|
||||
prompt_cache_hit_tokens: None,
|
||||
prompt_cache_miss_tokens: None,
|
||||
reasoning_tokens: None,
|
||||
}],
|
||||
)
|
||||
.await
|
||||
@@ -639,6 +665,9 @@ mod tests {
|
||||
created_at: now.clone(),
|
||||
prompt_tokens: None,
|
||||
completion_tokens: None,
|
||||
prompt_cache_hit_tokens: None,
|
||||
prompt_cache_miss_tokens: None,
|
||||
reasoning_tokens: None,
|
||||
};
|
||||
repo.replace_conversation("c", vec![rec()]).await.expect("1st");
|
||||
repo.replace_conversation("c", vec![rec()]).await.expect("2nd");
|
||||
|
||||
Reference in New Issue
Block a user