@@ -63,20 +63,29 @@ func SessionFromEvents(events []map[string]any, title string, questions []string
6363
6464 // Add user questions as initial messages.
6565 // For multi-turn evals, these are interleaved with agent responses
66- // as they appear in the event stream. The first question is added
67- // upfront; subsequent questions are inserted when a stream_stopped
68- // event indicates the agent finished processing the previous turn.
66+ // as they appear in the event stream. User messages are added when
67+ // a "user_message" event is encountered (which carries the correct
68+ // timestamp), or when a "stream_stopped" event indicates the agent
69+ // finished processing the previous turn in a multi-turn eval.
70+ // If no "user_message" event is found before the first agent response,
71+ // the question is added with the timestamp of that first response.
6972 questionIdx := 0
70- addNextQuestion := func () {
73+ userMessageAdded := false
74+ addNextQuestion := func (timestamp string ) {
7175 if questionIdx < len (questions ) {
72- sess .AddMessage (session .UserMessage (questions [questionIdx ]))
76+ msg := & session.Message {
77+ Message : chat.Message {
78+ Role : chat .MessageRoleUser ,
79+ Content : questions [questionIdx ],
80+ CreatedAt : timestamp ,
81+ },
82+ }
83+ sess .AddMessage (msg )
7384 questionIdx ++
85+ userMessageAdded = true
7486 }
7587 }
7688
77- // Add the first question
78- addNextQuestion ()
79-
8089 // Track current assistant message being built
8190 var currentContent strings.Builder
8291 var currentReasoningContent strings.Builder
@@ -122,7 +131,19 @@ func SessionFromEvents(events []map[string]any, title string, questions []string
122131 eventTimestamp := parseEventTimestamp (event )
123132
124133 switch eventType {
134+ case "user_message" :
135+ // Use the event timestamp for the user message instead of time.Now()
136+ if ! userMessageAdded {
137+ addNextQuestion (eventTimestamp )
138+ }
139+
125140 case "agent_choice" :
141+ // Ensure a user message has been added before the first agent response.
142+ // This handles event streams that lack a "user_message" event.
143+ if ! userMessageAdded {
144+ addNextQuestion (eventTimestamp )
145+ }
146+
126147 // Accumulate agent response content
127148 if content , ok := event ["content" ].(string ); ok {
128149 currentContent .WriteString (content )
@@ -237,14 +258,22 @@ func SessionFromEvents(events []map[string]any, title string, questions []string
237258 // Flush final assistant message
238259 flushAssistantMessage ()
239260
240- // In multi-turn evals, add the next user question after each turn
241- addNextQuestion ()
261+ // In multi-turn evals, add the next user question after each turn.
262+ // Reset the flag so the next user_message event (or agent_choice
263+ // fallback) will add the question for the next turn.
264+ userMessageAdded = false
242265 }
243266 }
244267
245268 // Flush any remaining content
246269 flushAssistantMessage ()
247270
271+ // Add any remaining questions that weren't added via user_message or
272+ // agent_choice events (e.g. when the event stream is empty).
273+ for questionIdx < len (questions ) {
274+ addNextQuestion (time .Now ().Format (time .RFC3339 ))
275+ }
276+
248277 return sess
249278}
250279
0 commit comments