revert(proxy): drop the 1-hour cache TTL option and TTL-bucketed write accounting

The forced 1-hour cache_control TTL (schema v14) was a mistake. Injected
breakpoints return to Anthropic's standard 5-minute TTL, caller-owned
markers are preserved verbatim instead of having their TTLs rewritten,
and the 5m/1h cache-write buckets are removed from usage parsing and
pricing (back to the single aggregate cache-creation rate). The cache
TTL selector is removed from the rectifier settings panel along with its
i18n keys in all four locales.

SCHEMA_VERSION returns to 13: the unreleased cache_creation_1h_tokens
column and the v13->v14 migration are removed. The feature never shipped
in a release and the introducing commit was never pushed, so no
databases were stamped v14 outside this machine (local DB verified at
user_version 13).
This commit is contained in:
Jason
2026-07-13 17:55:33 +08:00
parent ac52c851bf
commit 6eb217b242
23 changed files with 56 additions and 416 deletions
-2
View File
@@ -448,8 +448,6 @@ fn insert_session_log_entry(
output_tokens: msg.output_tokens,
cache_read_tokens: msg.cache_read_tokens,
cache_creation_tokens: msg.cache_creation_tokens,
cache_creation_5m_tokens: 0,
cache_creation_1h_tokens: 0,
model: Some(msg.model.clone()),
message_id: None,
};
@@ -634,8 +634,6 @@ fn insert_codex_session_entry(
output_tokens: delta.output,
cache_read_tokens: delta.cached_input,
cache_creation_tokens: 0,
cache_creation_5m_tokens: 0,
cache_creation_1h_tokens: 0,
model: Some(model.to_string()),
message_id: None,
};
@@ -270,8 +270,6 @@ fn insert_gemini_session_entry(
output_tokens,
cache_read_tokens: tokens.cached,
cache_creation_tokens: 0,
cache_creation_5m_tokens: 0,
cache_creation_1h_tokens: 0,
model: Some(model.to_string()),
message_id: None,
};
@@ -362,8 +362,6 @@ fn insert_opencode_message(
output_tokens: output_with_reasoning,
cache_read_tokens: msg.cache_read_tokens,
cache_creation_tokens: msg.cache_write_tokens,
cache_creation_5m_tokens: 0,
cache_creation_1h_tokens: 0,
model: Some(msg.model_id.clone()),
message_id: None,
};
+20 -79
View File
@@ -137,9 +137,6 @@ pub struct RequestLogDetail {
pub output_tokens: u32,
pub cache_read_tokens: u32,
pub cache_creation_tokens: u32,
/// Internal TTL pricing bucket; aggregate UI metrics remain unchanged.
#[serde(skip)]
pub cache_creation_1h_tokens: u32,
/// Internal storage semantics; omitted from the UI/API payload.
#[serde(skip)]
pub input_token_semantics: i64,
@@ -162,12 +159,12 @@ pub struct RequestLogDetail {
pub pricing_model: Option<String>,
}
/// 把 27 列的查询结果映射为 `RequestLogDetail`。
/// 把 26 列的查询结果映射为 `RequestLogDetail`。
///
/// 调用方的 SELECT **必须**按以下顺序返回 27 列:
/// 调用方的 SELECT **必须**按以下顺序返回 26 列:
/// `request_id, provider_id, provider_name, app_type, model, request_model,
/// cost_multiplier, input_tokens, output_tokens, cache_read_tokens,
/// cache_creation_tokens, cache_creation_1h_tokens, input_cost_usd, output_cost_usd, cache_read_cost_usd,
/// cache_creation_tokens, input_cost_usd, output_cost_usd, cache_read_cost_usd,
/// cache_creation_cost_usd, total_cost_usd, is_streaming, latency_ms,
/// first_token_ms, duration_ms, status_code, error_message, created_at,
/// data_source, pricing_model, input_token_semantics`
@@ -188,22 +185,21 @@ fn row_to_request_log_detail(row: &rusqlite::Row<'_>) -> rusqlite::Result<Reques
output_tokens: row.get::<_, i64>(8)? as u32,
cache_read_tokens: row.get::<_, i64>(9)? as u32,
cache_creation_tokens: row.get::<_, i64>(10)? as u32,
cache_creation_1h_tokens: row.get::<_, i64>(11)? as u32,
input_cost_usd: row.get(12)?,
output_cost_usd: row.get(13)?,
cache_read_cost_usd: row.get(14)?,
cache_creation_cost_usd: row.get(15)?,
total_cost_usd: row.get(16)?,
is_streaming: row.get::<_, i64>(17)? != 0,
latency_ms: row.get::<_, i64>(18)? as u64,
first_token_ms: row.get::<_, Option<i64>>(19)?.map(|v| v as u64),
duration_ms: row.get::<_, Option<i64>>(20)?.map(|v| v as u64),
status_code: row.get::<_, i64>(21)? as u16,
error_message: row.get(22)?,
created_at: row.get(23)?,
data_source: row.get(24)?,
pricing_model: row.get(25)?,
input_token_semantics: row.get::<_, i64>(26)?,
input_cost_usd: row.get(11)?,
output_cost_usd: row.get(12)?,
cache_read_cost_usd: row.get(13)?,
cache_creation_cost_usd: row.get(14)?,
total_cost_usd: row.get(15)?,
is_streaming: row.get::<_, i64>(16)? != 0,
latency_ms: row.get::<_, i64>(17)? as u64,
first_token_ms: row.get::<_, Option<i64>>(18)?.map(|v| v as u64),
duration_ms: row.get::<_, Option<i64>>(19)?.map(|v| v as u64),
status_code: row.get::<_, i64>(20)? as u16,
error_message: row.get(21)?,
created_at: row.get(22)?,
data_source: row.get(23)?,
pricing_model: row.get(24)?,
input_token_semantics: row.get::<_, i64>(25)?,
})
}
@@ -1534,7 +1530,6 @@ impl Database {
"SELECT l.request_id, l.provider_id, {logs_pname} as provider_name, l.app_type, l.model,
l.request_model, l.cost_multiplier,
l.input_tokens, l.output_tokens, l.cache_read_tokens, l.cache_creation_tokens,
l.cache_creation_1h_tokens,
l.input_cost_usd, l.output_cost_usd, l.cache_read_cost_usd, l.cache_creation_cost_usd, l.total_cost_usd,
l.is_streaming, l.latency_ms, l.first_token_ms, l.duration_ms,
l.status_code, l.error_message, l.created_at, l.data_source, l.pricing_model,
@@ -1579,7 +1574,6 @@ impl Database {
"SELECT l.request_id, l.provider_id, {detail_pname} as provider_name, l.app_type, l.model,
l.request_model, l.cost_multiplier,
input_tokens, output_tokens, cache_read_tokens, cache_creation_tokens,
cache_creation_1h_tokens,
input_cost_usd, output_cost_usd, cache_read_cost_usd, cache_creation_cost_usd, total_cost_usd,
is_streaming, latency_ms, first_token_ms, duration_ms,
status_code, error_message, created_at, l.data_source, l.pricing_model,
@@ -1736,7 +1730,6 @@ impl Database {
"SELECT request_id, provider_id, NULL AS provider_name, app_type, model, request_model,
cost_multiplier,
input_tokens, output_tokens, cache_read_tokens, cache_creation_tokens,
cache_creation_1h_tokens,
input_cost_usd, output_cost_usd, cache_read_cost_usd,
cache_creation_cost_usd, total_cost_usd, is_streaming, latency_ms,
first_token_ms, duration_ms, status_code, error_message, created_at,
@@ -1843,17 +1836,9 @@ impl Database {
let cache_read_cost = rust_decimal::Decimal::from(log.cache_read_tokens as u64)
* pricing.cache_read
/ million;
let cache_creation_1h_tokens =
log.cache_creation_1h_tokens.min(log.cache_creation_tokens) as u64;
let cache_creation_standard_tokens =
(log.cache_creation_tokens as u64).saturating_sub(cache_creation_1h_tokens);
let cache_creation_1h_price = pricing.cache_creation * rust_decimal::Decimal::from(8u32)
/ rust_decimal::Decimal::from(5u32);
let cache_creation_cost = rust_decimal::Decimal::from(cache_creation_standard_tokens)
let cache_creation_cost = rust_decimal::Decimal::from(log.cache_creation_tokens as u64)
* pricing.cache_creation
/ million
+ rust_decimal::Decimal::from(cache_creation_1h_tokens) * cache_creation_1h_price
/ million;
/ million;
// 总成本 = 基础成本之和 × 倍率
let base_total = input_cost + output_cost + cache_read_cost + cache_creation_cost;
let total_cost = base_total * multiplier;
@@ -2883,50 +2868,6 @@ mod tests {
Ok(())
}
#[test]
fn test_backfill_preserves_one_hour_cache_write_pricing() -> Result<(), AppError> {
let db = Database::memory()?;
{
let conn = lock_conn!(db.conn);
insert_usage_log(
&conn,
"claude-cache-one-hour",
"claude",
"p1",
"claude-haiku-4-5",
"proxy",
1000,
0,
0,
0,
1_000_000,
200,
"0",
)?;
conn.execute(
"UPDATE proxy_request_logs
SET cache_creation_1h_tokens = 1000000
WHERE request_id = 'claude-cache-one-hour'",
[],
)?;
}
assert_eq!(db.backfill_missing_usage_costs()?, 1);
let conn = lock_conn!(db.conn);
let (cache_creation_cost, total_cost): (String, String) = conn.query_row(
"SELECT cache_creation_cost_usd, total_cost_usd
FROM proxy_request_logs WHERE request_id = 'claude-cache-one-hour'",
[],
|row| Ok((row.get(0)?, row.get(1)?)),
)?;
assert_eq!(cache_creation_cost, "2.000000");
assert_eq!(total_cost, "2.000000");
Ok(())
}
#[test]
fn test_get_usage_summary() -> Result<(), AppError> {
let db = Database::memory()?;