tapes_client/core/models/span.rs
1//! Span shapes: the observed units of work and their edges.
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-mode span so the console drills in for the
47 /// full payload: `"preview"` when input/output are the stored previews,
48 /// `"preview_pending"` when the row has no stored preview yet (input and
49 /// output are then `[]`). Absent in full mode.
50 pub payload: String,
51
52 /// The contract's `raw_turn_id`.
53 pub raw_turn_id: i64,
54
55 /// The span's presentation ordinal within its trace; spans arrive sorted
56 /// by it (started_at ties inside one llm call — parallel tool batches
57 /// share an instant).
58 pub seq: i64,
59
60 /// The contract's `span_id`.
61 pub span_id: String,
62
63 /// The contract's `started_at`, an RFC 3339 timestamp.
64 pub started_at: String,
65
66 /// The contract's `status`.
67 pub status: String,
68
69 /// The contract's `stop_reason`.
70 pub stop_reason: String,
71
72 /// The contract's `thread_id`.
73 pub thread_id: String,
74
75 /// The contract's `trace_id`.
76 pub trace_id: String,
77
78 /// Usage (was `metrics`) is an llm.Usage object on the wire — {}-pinned
79 /// for usage-less spans.
80 pub usage: Value,
81
82 /// The typed security-monitor disposition (null off permission-check
83 /// spans), deriver-written.
84 pub verdict: Option<Value>,
85}
86
87impl ContractModel for SpanItem {
88 const SCHEMA: &'static str = "SpanItem";
89}
90
91/// A dataflow edge. kind is a typed top-level field (rejoin / verdict /
92/// compaction-seam / emits / feeds); from/to trace ids differ on cross-trace
93/// causality.
94///
95/// Models the contract's `SpanLinkItem` schema.
96#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
97#[serde(default)]
98#[non_exhaustive]
99pub struct SpanLinkItem {
100 /// The contract's `from_io`.
101 pub from_io: String,
102
103 /// The contract's `from_span_id`.
104 pub from_span_id: String,
105
106 /// The contract's `from_trace_id`.
107 pub from_trace_id: String,
108
109 /// The contract's `kind`.
110 pub kind: String,
111
112 /// The contract's `to_io`.
113 pub to_io: String,
114
115 /// The contract's `to_span_id`.
116 pub to_span_id: String,
117
118 /// The contract's `to_trace_id`.
119 pub to_trace_id: String,
120}
121
122impl ContractModel for SpanLinkItem {
123 const SCHEMA: &'static str = "SpanLinkItem";
124}