tapes_client/core/models/span.rs
1//! Span shapes: the observed units of work, their edges, and search hits.
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6use super::ContractModel;
7
8/// One observed unit of work. Every field is a deriver output, formatting-
9/// only: the harness-taxonomy fields (call_kind, model, stop_reason,
10/// thread_id, verdict) are typed rather than bagged in a metadata map, and
11/// input/output are uniform content-block arrays for ALL kinds — the console
12/// owns per-kind rendering.
13///
14/// Models the contract's `SpanItem` schema.
15#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
16#[serde(default)]
17#[non_exhaustive]
18pub struct SpanItem {
19 /// Deriver-written taxonomy, promoted from the old metadata grab-bag.
20 pub call_kind: String,
21
22 /// The contract's `duration_ns`.
23 pub duration_ns: i64,
24
25 /// Input/Output are content-block arrays (llm.ContentBlock), uniform for
26 /// every kind (tool spans included — no unwrapping).
27 #[serde(deserialize_with = "super::null_default")]
28 pub input: Vec<Value>,
29
30 /// The contract's `kind`.
31 pub kind: String,
32
33 /// The contract's `model`.
34 pub model: String,
35
36 /// The contract's `name`.
37 pub name: String,
38
39 /// The contract's `output`.
40 #[serde(deserialize_with = "super::null_default")]
41 pub output: Vec<Value>,
42
43 /// The contract's `parent_span_id`.
44 pub parent_span_id: String,
45
46 /// Payload marks a preview-truncated span so the console drills in for
47 /// the full payload; absent in full mode.
48 pub payload: String,
49
50 /// The contract's `raw_turn_id`.
51 pub raw_turn_id: i64,
52
53 /// The span's presentation ordinal within its trace; spans arrive sorted
54 /// by it (started_at ties inside one llm call — parallel tool batches
55 /// share an instant).
56 pub seq: i64,
57
58 /// The contract's `span_id`.
59 pub span_id: String,
60
61 /// The contract's `started_at`, an RFC 3339 timestamp.
62 pub started_at: String,
63
64 /// The contract's `status`.
65 pub status: String,
66
67 /// The contract's `stop_reason`.
68 pub stop_reason: String,
69
70 /// The contract's `thread_id`.
71 pub thread_id: String,
72
73 /// The contract's `trace_id`.
74 pub trace_id: String,
75
76 /// Usage (was `metrics`) is an llm.Usage object on the wire — {}-pinned
77 /// for usage-less spans.
78 pub usage: Value,
79
80 /// The typed security-monitor disposition (null off permission-check
81 /// spans), deriver-written.
82 pub verdict: Option<Value>,
83}
84
85impl ContractModel for SpanItem {
86 const SCHEMA: &'static str = "SpanItem";
87}
88
89/// A dataflow edge. kind is a typed top-level field (rejoin / verdict /
90/// compaction-seam / emits / feeds); from/to trace ids differ on cross-trace
91/// causality.
92///
93/// Models the contract's `SpanLinkItem` schema.
94#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
95#[serde(default)]
96#[non_exhaustive]
97pub struct SpanLinkItem {
98 /// The contract's `from_io`.
99 pub from_io: String,
100
101 /// The contract's `from_span_id`.
102 pub from_span_id: String,
103
104 /// The contract's `from_trace_id`.
105 pub from_trace_id: String,
106
107 /// The contract's `kind`.
108 pub kind: String,
109
110 /// The contract's `to_io`.
111 pub to_io: String,
112
113 /// The contract's `to_span_id`.
114 pub to_span_id: String,
115
116 /// The contract's `to_trace_id`.
117 pub to_trace_id: String,
118}
119
120impl ContractModel for SpanLinkItem {
121 const SCHEMA: &'static str = "SpanLinkItem";
122}
123
124/// The span search response.
125///
126/// Models the contract's `SpanSearchOutput` schema.
127#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
128#[serde(default)]
129#[non_exhaustive]
130pub struct SpanSearchOutput {
131 /// The contract's `count`.
132 pub count: i32,
133
134 /// The contract's `query`.
135 pub query: String,
136
137 /// The contract's `results`.
138 #[serde(deserialize_with = "super::null_default")]
139 pub results: Vec<SpanSearchResult>,
140}
141
142impl ContractModel for SpanSearchOutput {
143 const SCHEMA: &'static str = "SpanSearchOutput";
144}
145
146/// One span hit with its trace/turn context.
147///
148/// Models the contract's `SpanSearchResult` schema.
149#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
150#[serde(default)]
151#[non_exhaustive]
152pub struct SpanSearchResult {
153 /// The contract's `model`.
154 pub model: String,
155
156 /// The contract's `score`.
157 pub score: f32,
158
159 /// The contract's `session_id`.
160 pub session_id: String,
161
162 /// Snippet previews the matched span's delta-only text.
163 pub snippet: String,
164
165 /// The contract's `span_id`.
166 pub span_id: String,
167
168 /// The contract's `started_at`, an RFC 3339 timestamp.
169 pub started_at: String,
170
171 /// The contract's `trace_id`.
172 pub trace_id: String,
173
174 /// The prompt of the turn (trace) the span belongs to. Served explicitly
175 /// (not omitempty) so a synthetic turn's empty prompt reaches consumers
176 /// as "" rather than a dropped key — see TraceItem.
177 pub user_prompt: String,
178}
179
180impl ContractModel for SpanSearchResult {
181 const SCHEMA: &'static str = "SpanSearchResult";
182}