tapes_client/core/models/trace.rs
1//! Trace shapes: one user-visible turn, its header, and its spend.
2
3use serde::{Deserialize, Serialize};
4
5use super::ContractModel;
6use super::span::{SpanItem, SpanLinkItem};
7
8/// One user-visible turn's header. session_id / harness ids are not
9/// duplicated here — they belong to the session.
10///
11/// Models the contract's `TraceItem` schema.
12#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
13#[serde(default)]
14#[non_exhaustive]
15pub struct TraceItem {
16 /// The contract's `duration_ns`.
17 pub duration_ns: i64,
18
19 /// The contract's `ended_at`, an RFC 3339 timestamp.
20 pub ended_at: String,
21
22 /// The contract's `main_usage`.
23 #[serde(deserialize_with = "super::null_default")]
24 pub main_usage: MainUsage,
25
26 /// The derive-time fold of the closing conversation- spine llm call's
27 /// text output — the answer line for collapsed turn cards, so summary
28 /// consumers never need spans.
29 pub response_preview: String,
30
31 /// The capture origin of the turn's rows ("wire" | "transcript"),
32 /// promoted from raw_turns.source.
33 pub source: String,
34
35 /// The contract's `span_count`.
36 pub span_count: i32,
37
38 /// The contract's `started_at`, an RFC 3339 timestamp.
39 pub started_at: String,
40
41 /// The contract's `status`.
42 pub status: String,
43
44 /// A typed deriver signal ("post-compaction" for a compaction
45 /// continuation, "shadow-opener" for a shadow-only opener), promoted out
46 /// of the old metadata grab-bag.
47 pub synthetic: String,
48
49 /// The contract's `trace_id`.
50 pub trace_id: String,
51
52 /// The contract's `usage`.
53 #[serde(deserialize_with = "super::null_default")]
54 pub usage: TraceUsage,
55
56 /// Served explicitly (not omitempty): a synthetic opener has an empty
57 /// prompt, and dropping the key turns the empty string into `undefined`
58 /// on the wire, which breaks consumers that expect a string (e.g.
59 pub user_prompt: String,
60}
61
62impl ContractModel for TraceItem {
63 const SCHEMA: &'static str = "TraceItem";
64}
65
66/// A trace's total token/cost rollup. Fields are pinned (no omitempty) so the
67/// object shape is uniform across traces.
68///
69/// Models the contract's `TraceUsage` schema.
70#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
71#[serde(default)]
72#[non_exhaustive]
73pub struct TraceUsage {
74 /// The contract's `cache_creation_tokens`.
75 pub cache_creation_tokens: i64,
76
77 /// The contract's `cache_read_tokens`.
78 pub cache_read_tokens: i64,
79
80 /// The contract's `cost_usd`.
81 pub cost_usd: f64,
82
83 /// The contract's `input_tokens`.
84 pub input_tokens: i64,
85
86 /// The contract's `output_tokens`.
87 pub output_tokens: i64,
88}
89
90impl ContractModel for TraceUsage {
91 const SCHEMA: &'static str = "TraceUsage";
92}
93
94/// The task token slice of a trace: the main agent and its subagents
95/// (call_kind=main across every thread), no cache split or cost (those live
96/// on the total Usage).
97///
98/// Models the contract's `MainUsage` schema.
99#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
100#[serde(default)]
101#[non_exhaustive]
102pub struct MainUsage {
103 /// The contract's `input_tokens`.
104 pub input_tokens: i64,
105
106 /// The contract's `output_tokens`.
107 pub output_tokens: i64,
108}
109
110impl ContractModel for MainUsage {
111 const SCHEMA: &'static str = "MainUsage";
112}
113
114/// One trace with its spans. In the composite session response links are
115/// session-scoped (top level); the single-trace endpoint sets Links to the
116/// edges touching that trace.
117///
118/// Models the contract's `TraceDetail` schema.
119#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
120#[serde(default)]
121#[non_exhaustive]
122pub struct TraceDetail {
123 /// The contract's `links`.
124 #[serde(deserialize_with = "super::null_default")]
125 pub links: Vec<SpanLinkItem>,
126
127 /// The contract's `schema`.
128 pub schema: String,
129
130 /// The contract's `spans`.
131 #[serde(deserialize_with = "super::null_default")]
132 pub spans: Vec<SpanItem>,
133
134 /// The contract's `trace`.
135 #[serde(deserialize_with = "super::null_default")]
136 pub trace: TraceItem,
137}
138
139impl ContractModel for TraceDetail {
140 const SCHEMA: &'static str = "TraceDetail";
141}
142
143/// The summaries list for one session. `schema` stamps the projection
144/// generation the rows were derived against — the same stamp the composite
145/// carries — so every trace-grain response is self-describing, not just the
146/// composite.
147///
148/// Models the contract's `TraceListResponse` schema.
149#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
150#[serde(default)]
151#[non_exhaustive]
152pub struct TraceListResponse {
153 /// The contract's `items`.
154 #[serde(deserialize_with = "super::null_default")]
155 pub items: Vec<TraceItem>,
156
157 /// The contract's `schema`.
158 pub schema: String,
159}
160
161impl ContractModel for TraceListResponse {
162 const SCHEMA: &'static str = "TraceListResponse";
163}