MCPcopy Create free account
hub / github.com/atomicdotdev/atomic / build_turn_provenance

Function build_turn_provenance

atomic-agent/src/record/provenance.rs:26–156  ·  view source on GitHub ↗

Build a `Provenance` entry for an agent turn. Populates vendor, model, tool, suggestion type, session ID, and prompt hash. Token usage and cost can be added later when that data is available from the agent's transcript.

(options: &TurnRecordOptions<'_>)

Source from the content-addressed store, hash-verified

24/// Token usage and cost can be added later when that data is available from
25/// the agent's transcript.
26pub(crate) fn build_turn_provenance(options: &TurnRecordOptions<'_>) -> Provenance {
27 let vendor = if options.session.agent_vendor.is_empty() {
28 vendor_from_agent_name(&options.session.agent_name)
29 } else {
30 AIVendor::parse(&options.session.agent_vendor)
31 };
32
33 let model = if options.session.model.is_empty() {
34 "unknown".to_string()
35 } else {
36 options.session.model.clone()
37 };
38
39 let tool = AITool::Cli(options.session.agent_name.clone());
40
41 let prompt_content = match &options.prompt {
42 Some(prompt) if !prompt.is_empty() => PromptContent::Hashed(Hash::of(prompt.as_bytes())),
43 _ => PromptContent::None,
44 };
45
46 let timestamp = options.event.timestamp.timestamp();
47
48 // Extract enriched metadata from the raw JSON payload sent by the plugin.
49 // The plugin accumulates data across all events within a turn and sends
50 // it in the `stop` payload. All fields are optional — old plugins that
51 // don't send them will simply leave these as None/default.
52 let raw = options.event.raw_json.as_ref();
53
54 // Helper closures for extracting typed values from raw JSON
55 let raw_str = |key: &str| -> Option<String> {
56 raw.and_then(|r| r.get(key))
57 .and_then(|v| v.as_str())
58 .map(|s| s.to_string())
59 };
60 let raw_u64 =
61 |key: &str| -> Option<u64> { raw.and_then(|r| r.get(key)).and_then(|v| v.as_u64()) };
62 let raw_f64 =
63 |key: &str| -> Option<f64> { raw.and_then(|r| r.get(key)).and_then(|v| v.as_f64()) };
64 let raw_u32 = |key: &str| -> Option<u32> {
65 raw.and_then(|r| r.get(key))
66 .and_then(|v| v.as_u64())
67 .map(|n| n as u32)
68 };
69
70 // Token usage — now includes reasoning tokens
71 let input = raw_u64("input_tokens").unwrap_or(0);
72 let output = raw_u64("output_tokens").unwrap_or(0);
73 let reasoning = raw_u64("reasoning_tokens").unwrap_or(0);
74 let cache_read = raw_u64("cache_read_tokens").unwrap_or(0);
75 let cache_write = raw_u64("cache_write_tokens").unwrap_or(0);
76
77 let tokens = if input > 0 || output > 0 || reasoning > 0 || cache_read > 0 || cache_write > 0 {
78 TokenUsage::full(input, output, reasoning, cache_read, cache_write)
79 } else {
80 TokenUsage::default()
81 };
82
83 // Cost

Calls 13

parseFunction · 0.85
CliClass · 0.85
as_refMethod · 0.80
as_u64Method · 0.80
vendor_from_agent_nameFunction · 0.70
getMethod · 0.65
is_emptyMethod · 0.45
cloneMethod · 0.45
as_bytesMethod · 0.45
timestampMethod · 0.45
as_strMethod · 0.45
lenMethod · 0.45