Skip to main content

lash_remote_protocol/
usage_activity.rs

1//! Token usage accounting and the streaming turn-activity event vocabulary.
2
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5
6use crate::ensure_protocol_version;
7use crate::registry_errors::{RemoteProtocolError, require_non_empty};
8
9#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
10pub struct RemoteUsage {
11    pub input_tokens: i64,
12    pub output_tokens: i64,
13    pub cache_read_input_tokens: i64,
14    pub cache_write_input_tokens: i64,
15    pub reasoning_output_tokens: i64,
16}
17
18impl RemoteUsage {
19    pub fn add(&mut self, other: &Self) {
20        self.input_tokens += other.input_tokens;
21        self.output_tokens += other.output_tokens;
22        self.cache_read_input_tokens += other.cache_read_input_tokens;
23        self.cache_write_input_tokens += other.cache_write_input_tokens;
24        self.reasoning_output_tokens += other.reasoning_output_tokens;
25    }
26}
27
28#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
29pub struct RemoteTokenLedgerEntry {
30    pub source: String,
31    pub model: String,
32    pub usage: RemoteUsage,
33}
34
35#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
36pub struct RemoteTurnActivity {
37    pub protocol_version: u32,
38    pub sequence: u64,
39    pub id: String,
40    pub correlation_id: String,
41    #[serde(flatten)]
42    pub event: RemoteTurnEvent,
43}
44
45impl RemoteTurnActivity {
46    pub fn validate(&self) -> Result<(), RemoteProtocolError> {
47        ensure_protocol_version(self.protocol_version)?;
48        require_non_empty("RemoteTurnActivity", "id", &self.id)?;
49        require_non_empty("RemoteTurnActivity", "correlation_id", &self.correlation_id)
50    }
51}
52
53#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
54#[serde(tag = "type", rename_all = "snake_case")]
55pub enum RemoteTurnEvent {
56    ModelRequestStarted {
57        protocol_iteration: usize,
58    },
59    AssistantProseDelta {
60        text: String,
61    },
62    ReasoningDelta {
63        text: String,
64    },
65    CodeBlockStarted {
66        language: String,
67        code: String,
68        #[serde(default, skip_serializing_if = "Option::is_none")]
69        graph_key: Option<String>,
70    },
71    CodeBlockCompleted {
72        language: String,
73        output: String,
74        #[serde(default, skip_serializing_if = "Option::is_none")]
75        error: Option<String>,
76        success: bool,
77        duration_ms: u64,
78        tool_call_ids: Vec<String>,
79        #[serde(default, skip_serializing_if = "Option::is_none")]
80        graph_key: Option<String>,
81    },
82    ToolCallStarted {
83        #[serde(default, skip_serializing_if = "Option::is_none")]
84        call_id: Option<String>,
85        name: String,
86        args: serde_json::Value,
87    },
88    ToolCallCompleted {
89        #[serde(default, skip_serializing_if = "Option::is_none")]
90        call_id: Option<String>,
91        name: String,
92        args: serde_json::Value,
93        output: serde_json::Value,
94        duration_ms: u64,
95    },
96    FinalValue {
97        value: serde_json::Value,
98    },
99    ToolValue {
100        tool_name: String,
101        value: serde_json::Value,
102    },
103    Usage {
104        protocol_iteration: usize,
105        usage: RemoteUsage,
106        cumulative: RemoteUsage,
107    },
108    ChildUsage {
109        session_id: String,
110        source: String,
111        model: String,
112        protocol_iteration: usize,
113        usage: RemoteUsage,
114        cumulative: RemoteUsage,
115    },
116    RetryStatus {
117        wait_seconds: u64,
118        attempt: usize,
119        max_attempts: usize,
120        reason: String,
121    },
122    RuntimeDiagnostic {
123        kind: String,
124        data: serde_json::Value,
125    },
126    Error {
127        message: String,
128    },
129}