sim_lib_agent_runner_core/
events.rs1use crate::{ModelRequest, ModelResponse, ModelUsage};
2use sim_kernel::{Cx, Datum, DatumStore, Event, EventKind, Expr, Ref, Result, Symbol};
3
4pub trait ModelEventSink {
6 fn emit(&mut self, event: ModelEvent) -> Result<()>;
8}
9
10#[derive(Clone, Debug, Default, PartialEq)]
12pub struct VecEventSink {
13 events: Vec<ModelEvent>,
14}
15
16impl VecEventSink {
17 pub fn new() -> Self {
19 Self::default()
20 }
21
22 pub fn into_events(self) -> Vec<ModelEvent> {
24 self.events
25 }
26
27 pub fn events(&self) -> &[ModelEvent] {
29 &self.events
30 }
31}
32
33impl ModelEventSink for VecEventSink {
34 fn emit(&mut self, event: ModelEvent) -> Result<()> {
35 self.events.push(event);
36 Ok(())
37 }
38}
39
40#[derive(Clone, Debug, PartialEq)]
42pub struct ModelEvent {
43 pub event: Symbol,
45 pub runner: Symbol,
47 pub model: String,
49 pub span_id: Expr,
51 pub response: Option<ModelResponse>,
53 pub extra: Vec<(Expr, Expr)>,
55}
56
57impl ModelEvent {
58 pub fn new(event: Symbol, runner: Symbol, model: impl Into<String>, span_id: Expr) -> Self {
60 Self {
61 event,
62 runner,
63 model: model.into(),
64 span_id,
65 response: None,
66 extra: Vec::new(),
67 }
68 }
69
70 pub fn start(runner: Symbol, model: impl Into<String>, span_id: Expr) -> Self {
72 Self::new(Symbol::new("start"), runner, model, span_id)
73 }
74
75 pub fn delta_text(
77 runner: Symbol,
78 model: impl Into<String>,
79 span_id: Expr,
80 text: impl Into<String>,
81 ) -> Self {
82 Self::new(Symbol::new("delta"), runner, model, span_id).with_text(text)
83 }
84
85 pub fn usage(
87 runner: Symbol,
88 model: impl Into<String>,
89 span_id: Expr,
90 usage: ModelUsage,
91 ) -> Self {
92 Self::new(Symbol::new("usage"), runner, model, span_id)
93 .with_field("usage", Expr::from(usage))
94 }
95
96 pub fn error_text(
98 runner: Symbol,
99 model: impl Into<String>,
100 span_id: Expr,
101 message: impl Into<String>,
102 ) -> Self {
103 Self::new(Symbol::new("error"), runner, model, span_id).with_text(message)
104 }
105
106 pub fn tool_call(
108 runner: Symbol,
109 model: impl Into<String>,
110 span_id: Expr,
111 tool_call: Expr,
112 ) -> Self {
113 Self::new(Symbol::new("tool-call"), runner, model, span_id)
114 .with_field("tool-call", tool_call)
115 }
116
117 pub fn final_of(response: &ModelResponse) -> Self {
119 Self {
120 event: Symbol::new("final"),
121 runner: response.runner.clone(),
122 model: response.model.clone(),
123 span_id: Expr::Symbol(Symbol::new("final")),
124 response: Some(response.clone()),
125 extra: Vec::new(),
126 }
127 }
128
129 pub fn with_field(mut self, key: &str, value: Expr) -> Self {
131 self.extra.push((Expr::Symbol(Symbol::new(key)), value));
132 self
133 }
134
135 fn with_text(self, text: impl Into<String>) -> Self {
136 self.with_field("text", Expr::String(text.into()))
137 }
138
139 pub fn to_kernel_event(&self, cx: &mut Cx, run: Ref, seq: u64) -> Result<Event> {
141 model_expr_event(cx, run, seq, Expr::from(self.clone()))
142 }
143}
144
145pub fn model_request_kernel_event(
147 cx: &mut Cx,
148 run: Ref,
149 seq: u64,
150 request: &ModelRequest,
151) -> Result<Event> {
152 model_expr_event(cx, run, seq, Expr::from(request.clone()))
153}
154
155fn model_expr_event(cx: &mut Cx, run: Ref, seq: u64, expr: Expr) -> Result<Event> {
156 let payload = Ref::Content(cx.datum_store_mut().intern(Datum::try_from(expr)?)?);
157 Event::new(run, seq, Vec::new(), EventKind::Chunk { payload })
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163 use sim_kernel::{DefaultFactory, NoopEvalPolicy};
164 use std::sync::Arc;
165
166 #[test]
167 fn model_request_and_delta_convert_to_kernel_chunk_events() {
168 let mut cx = Cx::new(Arc::new(NoopEvalPolicy), Arc::new(DefaultFactory));
169 let run = Ref::Symbol(Symbol::qualified("test", "run"));
170 let request = ModelRequest::new(Expr::String("task".to_owned()), Vec::new());
171 let request_event = model_request_kernel_event(&mut cx, run.clone(), 0, &request).unwrap();
172 let delta = ModelEvent::delta_text(
173 Symbol::qualified("runner", "fake"),
174 "fake/model",
175 Expr::String("span-1".to_owned()),
176 "hel",
177 );
178 let delta_event = delta.to_kernel_event(&mut cx, run.clone(), 1).unwrap();
179
180 assert!(matches!(request_event.kind, EventKind::Chunk { .. }));
181 assert!(matches!(delta_event.kind, EventKind::Chunk { .. }));
182 assert_eq!(delta_event.run, run);
183 assert_eq!(delta_event.seq, 1);
184 }
185}