story-kit: merge 301_story_dedicated_token_usage_page_in_web_ui
This commit is contained in:
@@ -130,6 +130,26 @@ struct TokenCostResponse {
|
||||
agents: Vec<AgentCostEntry>,
|
||||
}
|
||||
|
||||
/// A single token usage record in the all-usage response.
|
||||
#[derive(Object, Serialize)]
|
||||
struct TokenUsageRecordResponse {
|
||||
story_id: String,
|
||||
agent_name: String,
|
||||
model: Option<String>,
|
||||
timestamp: String,
|
||||
input_tokens: u64,
|
||||
output_tokens: u64,
|
||||
cache_creation_input_tokens: u64,
|
||||
cache_read_input_tokens: u64,
|
||||
total_cost_usd: f64,
|
||||
}
|
||||
|
||||
/// Response for the all token usage endpoint.
|
||||
#[derive(Object, Serialize)]
|
||||
struct AllTokenUsageResponse {
|
||||
records: Vec<TokenUsageRecordResponse>,
|
||||
}
|
||||
|
||||
/// Returns true if the story file exists in `work/5_done/` or `work/6_archived/`.
|
||||
///
|
||||
/// Used to exclude agents for already-archived stories from the `list_agents`
|
||||
@@ -532,6 +552,42 @@ impl AgentsApi {
|
||||
agents,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Get all token usage records across all stories.
|
||||
///
|
||||
/// Returns the full history from the persistent token_usage.jsonl log.
|
||||
#[oai(path = "/token-usage", method = "get")]
|
||||
async fn get_all_token_usage(
|
||||
&self,
|
||||
) -> OpenApiResult<Json<AllTokenUsageResponse>> {
|
||||
let project_root = self
|
||||
.ctx
|
||||
.agents
|
||||
.get_project_root(&self.ctx.state)
|
||||
.map_err(bad_request)?;
|
||||
|
||||
let records = crate::agents::token_usage::read_all(&project_root)
|
||||
.map_err(|e| bad_request(format!("Failed to read token usage: {e}")))?;
|
||||
|
||||
let response_records: Vec<TokenUsageRecordResponse> = records
|
||||
.into_iter()
|
||||
.map(|r| TokenUsageRecordResponse {
|
||||
story_id: r.story_id,
|
||||
agent_name: r.agent_name,
|
||||
model: r.model,
|
||||
timestamp: r.timestamp,
|
||||
input_tokens: r.usage.input_tokens,
|
||||
output_tokens: r.usage.output_tokens,
|
||||
cache_creation_input_tokens: r.usage.cache_creation_input_tokens,
|
||||
cache_read_input_tokens: r.usage.cache_read_input_tokens,
|
||||
total_cost_usd: r.usage.total_cost_usd,
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(Json(AllTokenUsageResponse {
|
||||
records: response_records,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -3862,7 +3862,7 @@ mod tests {
|
||||
total_cost_usd: 1.57,
|
||||
};
|
||||
let record =
|
||||
crate::agents::token_usage::build_record("42_story_foo", "coder-1", usage);
|
||||
crate::agents::token_usage::build_record("42_story_foo", "coder-1", None, usage);
|
||||
crate::agents::token_usage::append_record(root, &record).unwrap();
|
||||
|
||||
let result = tool_get_token_usage(&json!({}), &ctx).unwrap();
|
||||
@@ -3888,8 +3888,8 @@ mod tests {
|
||||
cache_read_input_tokens: 0,
|
||||
total_cost_usd: 0.5,
|
||||
};
|
||||
let r1 = crate::agents::token_usage::build_record("10_story_a", "coder-1", usage.clone());
|
||||
let r2 = crate::agents::token_usage::build_record("20_story_b", "coder-2", usage);
|
||||
let r1 = crate::agents::token_usage::build_record("10_story_a", "coder-1", None, usage.clone());
|
||||
let r2 = crate::agents::token_usage::build_record("20_story_b", "coder-2", None, usage);
|
||||
crate::agents::token_usage::append_record(root, &r1).unwrap();
|
||||
crate::agents::token_usage::append_record(root, &r2).unwrap();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user