Files
boxmaker/crates/bxcore/src/log.rs
T

79 lines
2.2 KiB
Rust

//! Session log record types. One JSON object per line; assistant messages are stored verbatim.
use serde::{Deserialize, Serialize};
use crate::{CallId, DataClass, Epoch, Hash32, SessionId, Timestamp};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ToolCall {
pub id: String,
pub name: String,
pub arguments: String,
}
/// What one completion cost. The same five numbers as `LogRecord::Usage`, without the time.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Usage {
pub cache_n: u64,
pub prompt_n: u64,
pub predicted_n: u64,
pub reasoning_tokens: u64,
pub thinking_capped: bool,
}
// JSON: {"type":"user","time":"…","content":"…"} ; the tag sits beside the fields; types are snake_case
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case", deny_unknown_fields)]
pub enum LogRecord {
SessionStart {
time: Timestamp,
session: SessionId,
epoch: Epoch,
slot: u32,
baseline: Hash32,
},
User {
time: Timestamp,
content: String,
/// The turn's speaker (M5c spec, section 11). Written only when present, so a log written
/// before M5c reads as it is, and a turn without a speaker is logged as before.
#[serde(default, skip_serializing_if = "Option::is_none")]
speaker: Option<String>,
},
Assistant {
time: Timestamp,
content: Option<String>,
reasoning_content: Option<String>,
tool_calls: Vec<ToolCall>,
},
ToolResult {
time: Timestamp,
call: CallId,
tool_call_id: String,
content: String,
class: DataClass,
untrusted: bool,
truncated: bool,
},
Usage {
time: Timestamp,
cache_n: u64,
prompt_n: u64,
predicted_n: u64,
reasoning_tokens: u64,
thinking_capped: bool,
},
CacheLoss {
time: Timestamp,
expected: u64,
got: u64,
},
EpochEnd {
time: Timestamp,
next: Epoch,
summary: String,
},
}