csusage_test_support/
openhands.rs1use std::path::Path;
2
3pub fn create_fixture(conversations_root: impl AsRef<Path>) {
8 write_conversation(
9 conversations_root.as_ref().join("conv-1"),
10 &[
11 stats_event(
13 "2026-01-01T00:00:00.000000",
14 "default",
15 "openai/gpt-5",
16 100,
17 10,
18 0,
19 0,
20 Some(0.5),
21 ),
22 stats_event(
24 "2026-01-02T00:00:00.000000",
25 "condenser",
26 "gpt-5-mini",
27 0,
28 0,
29 0,
30 0,
31 None,
32 ),
33 stats_event(
35 "2026-01-02T00:00:01.000000",
36 "default",
37 "openai/gpt-5",
38 200,
39 20,
40 30,
41 10,
42 Some(1.5),
43 ),
44 non_stats_event(),
45 ],
46 );
47 write_conversation(
48 conversations_root.as_ref().join("conv-2"),
49 &[stats_event(
50 "2026-01-03T00:00:00.000000",
51 "default",
52 "anthropic/claude-sonnet-4-5",
53 50,
54 5,
55 0,
56 0,
57 None,
58 )],
59 );
60}
61
62fn write_conversation(conversation: std::path::PathBuf, events: &[serde_json::Value]) {
63 let events_dir = conversation.join("events");
64 std::fs::create_dir_all(&events_dir).unwrap();
65 for (index, event) in events.iter().enumerate() {
66 let path = events_dir.join(format!("event-{index:05}-id{index}.json"));
67 std::fs::write(path, serde_json::to_string(event).unwrap()).unwrap();
68 }
69}
70
71#[allow(clippy::too_many_arguments)]
72fn stats_event(
73 timestamp: &str,
74 usage_id: &str,
75 model: &str,
76 prompt_tokens: i64,
77 completion_tokens: i64,
78 cache_read: i64,
79 cache_write: i64,
80 cost: Option<f64>,
81) -> serde_json::Value {
82 let metrics = serde_json::json!({
83 "model_name": model,
84 "accumulated_cost": cost.unwrap_or(0.0),
85 "max_budget_per_task": None::<serde_json::Value>,
86 "accumulated_token_usage": {
87 "model": model,
88 "prompt_tokens": prompt_tokens,
89 "completion_tokens": completion_tokens,
90 "cache_read_tokens": cache_read,
91 "cache_write_tokens": cache_write,
92 "reasoning_tokens": 0,
93 "context_window": 0,
94 "per_turn_token": prompt_tokens + completion_tokens,
95 "response_id": ""
96 }
97 });
98 serde_json::json!({
99 "id": format!("event-{usage_id}-{timestamp}"),
100 "timestamp": timestamp,
101 "source": "environment",
102 "parent_id": null,
103 "kind": "ConversationStateUpdateEvent",
104 "key": "stats",
105 "value": {"usage_to_metrics": {usage_id: metrics}}
106 })
107}
108
109fn non_stats_event() -> serde_json::Value {
110 serde_json::json!({
111 "id": "event-other",
112 "timestamp": "2026-01-02T00:00:02.000000",
113 "source": "environment",
114 "parent_id": null,
115 "kind": "MessageEvent",
116 "key": "",
117 "value": {}
118 })
119}