Skip to main content

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}