fix(usage): use unique database row IDs for event list keys

Fixes #298
This commit is contained in:
lzt404 committed 2026-09-23 21:35:59 +08:00
1 parent 395ac1c4fc
commit 345e228a91
2 files changed
+23 -2

No files matched your search

+21 -1
View File
@@ -185,6 +185,8 @@ struct UsageTokenStats {
#[derive(Clone, Serialize, Deserialize)]
pub(crate) struct UsageRecord {
id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
row_id: Option<String>,
timestamp: String,
#[serde(default)]
latency_ms: u64,
@@ -2714,6 +2716,7 @@ fn normalize_usage_record(value: Value, config: &GuiConfigFile) -> Result<UsageR
};
Ok(UsageRecord {
id,
row_id: None,
timestamp,
latency_ms: u64_field(object, "latency_ms"),
ttft_ms: optional_u64_field(object, "ttft_ms"),
@@ -3928,7 +3931,7 @@ fn load_usage_events(
cached_tokens, collector_source,
input_tokens, output_tokens, reasoning_tokens, cache_read_tokens,
cache_creation_tokens, total_tokens, canceled, failure_status,
failure_body
failure_body, id
FROM usage_events{}
ORDER BY timestamp_ms DESC, id DESC
LIMIT ? OFFSET ?
@@ -3961,6 +3964,7 @@ fn load_usage_events(
fn usage_record_from_row(row: &Row<'_>) -> rusqlite::Result<UsageRecord> {
Ok(UsageRecord {
id: row.get(0)?,
row_id: Some(row.get::<_, i64>(36)?.to_string()),
timestamp: row.get(1)?,
latency_ms: from_sql_i64(row.get(2)?),
ttft_ms: row.get::<_, Option<i64>>(3)?.map(from_sql_i64),
@@ -4676,6 +4680,7 @@ mod tests {
fn sample_record(id: &str, timestamp: &str, model: &str) -> UsageRecord {
UsageRecord {
id: id.to_string(),
row_id: None,
timestamp: timestamp.to_string(),
latency_ms: 100,
ttft_ms: Some(20),
@@ -5819,6 +5824,21 @@ mod tests {
.unwrap();
assert_eq!(count, 2);
let events = load_usage_events(
&connection,
&UsageQuery::default(),
&GuiConfigFile::default(),
)
.unwrap();
assert_eq!(events.items.len(), 2);
assert_eq!(events.items[0].id, events.items[1].id);
assert_ne!(events.items[0].row_id, events.items[1].row_id);
let serialized = serde_json::to_value(&events).unwrap();
assert!(serialized["items"][0]["row_id"].as_str().is_some());
assert_ne!(
serialized["items"][0]["row_id"],
serialized["items"][1]["row_id"]
);
drop(connection);
fs::remove_dir_all(root).unwrap();
}
+2 -1
View File
@@ -95,6 +95,7 @@ type UsageAnalysis = {
type UsageRecord = {
id: string;
row_id: string;
timestamp: string;
latency_ms: number;
ttft_ms: number | null;
@@ -2157,7 +2158,7 @@ function EventsView({
</thead>
<tbody>
{events.items.map((record) => (
<tr key={record.id}>
<tr key={record.row_id}>
{visibleColumns.map((column) => (
<UsageEventCell
key={column.key}