agtrace_engine/session/
assembler.rs1use super::stats::calculate_session_stats;
2use super::turn_builder::TurnBuilder;
3use super::types::*;
4use agtrace_types::{AgentEvent, EventPayload, StreamId};
5
6pub fn assemble_session(events: &[AgentEvent]) -> Option<AgentSession> {
7 if events.is_empty() {
8 return None;
9 }
10
11 let main_events: Vec<_> = events
14 .iter()
15 .filter(|e| matches!(e.stream_id, StreamId::Main))
16 .cloned()
17 .collect();
18
19 if main_events.is_empty() {
20 return None;
21 }
22
23 let session_id = main_events.first()?.session_id;
24 let start_time = main_events.first()?.timestamp;
25 let end_time = main_events.last().map(|e| e.timestamp);
26
27 let turns = build_turns(&main_events);
28 let stats = calculate_session_stats(&turns, start_time, end_time);
29
30 Some(AgentSession {
31 session_id,
32 start_time,
33 end_time,
34 turns,
35 stats,
36 })
37}
38
39fn build_turns(events: &[AgentEvent]) -> Vec<AgentTurn> {
40 let mut turns = Vec::new();
41 let mut current_turn: Option<TurnBuilder> = None;
42
43 for event in events {
44 match &event.payload {
45 EventPayload::User(user) => {
46 if let Some(builder) = current_turn.take()
47 && let Some(turn) = builder.build()
48 {
49 turns.push(turn);
50 }
51
52 current_turn = Some(TurnBuilder::new(
53 event.id,
54 event.timestamp,
55 UserMessage {
56 event_id: event.id,
57 content: user.clone(),
58 },
59 ));
60 }
61 _ => {
62 if let Some(ref mut builder) = current_turn {
63 builder.add_event(event);
64 }
65 }
66 }
67 }
68
69 if let Some(builder) = current_turn
70 && let Some(turn) = builder.build()
71 {
72 turns.push(turn);
73 }
74
75 turns
76}