Skip to main content

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}