@@ -281,8 +281,9 @@ func TestSimple(t *testing.T) {
281281 AgentChoice ("root" , sess .ID , "Hello" ),
282282 MessageAdded (sess .ID , msgAdded .Message , "root" ),
283283 NewTokenUsageEvent (sess .ID , "root" , & Usage {InputTokens : 3 , OutputTokens : 2 , ContextLength : 5 , LastMessage : & MessageUsage {
284- Usage : chat.Usage {InputTokens : 3 , OutputTokens : 2 },
285- Model : "test/mock-model" ,
284+ Usage : chat.Usage {InputTokens : 3 , OutputTokens : 2 },
285+ Model : "test/mock-model" ,
286+ FinishReason : chat .FinishReasonStop ,
286287 }}),
287288 StreamStopped (sess .ID , "root" ),
288289 }
@@ -324,8 +325,9 @@ func TestMultipleContentChunks(t *testing.T) {
324325 AgentChoice ("root" , sess .ID , "you?" ),
325326 MessageAdded (sess .ID , msgAdded .Message , "root" ),
326327 NewTokenUsageEvent (sess .ID , "root" , & Usage {InputTokens : 8 , OutputTokens : 12 , ContextLength : 20 , LastMessage : & MessageUsage {
327- Usage : chat.Usage {InputTokens : 8 , OutputTokens : 12 },
328- Model : "test/mock-model" ,
328+ Usage : chat.Usage {InputTokens : 8 , OutputTokens : 12 },
329+ Model : "test/mock-model" ,
330+ FinishReason : chat .FinishReasonStop ,
329331 }}),
330332 StreamStopped (sess .ID , "root" ),
331333 }
@@ -363,8 +365,9 @@ func TestWithReasoning(t *testing.T) {
363365 AgentChoice ("root" , sess .ID , "Hello, how can I help you?" ),
364366 MessageAdded (sess .ID , msgAdded .Message , "root" ),
365367 NewTokenUsageEvent (sess .ID , "root" , & Usage {InputTokens : 10 , OutputTokens : 15 , ContextLength : 25 , LastMessage : & MessageUsage {
366- Usage : chat.Usage {InputTokens : 10 , OutputTokens : 15 },
367- Model : "test/mock-model" ,
368+ Usage : chat.Usage {InputTokens : 10 , OutputTokens : 15 },
369+ Model : "test/mock-model" ,
370+ FinishReason : chat .FinishReasonStop ,
368371 }}),
369372 StreamStopped (sess .ID , "root" ),
370373 }
@@ -404,8 +407,9 @@ func TestMixedContentAndReasoning(t *testing.T) {
404407 AgentChoice ("root" , sess .ID , " How can I help you today?" ),
405408 MessageAdded (sess .ID , msgAdded .Message , "root" ),
406409 NewTokenUsageEvent (sess .ID , "root" , & Usage {InputTokens : 15 , OutputTokens : 20 , ContextLength : 35 , LastMessage : & MessageUsage {
407- Usage : chat.Usage {InputTokens : 15 , OutputTokens : 20 },
408- Model : "test/mock-model" ,
410+ Usage : chat.Usage {InputTokens : 15 , OutputTokens : 20 },
411+ Model : "test/mock-model" ,
412+ FinishReason : chat .FinishReasonStop ,
409413 }}),
410414 StreamStopped (sess .ID , "root" ),
411415 }
@@ -982,6 +986,59 @@ func TestEmitStartupInfo_CostIncludesSubSessions(t *testing.T) {
982986 "cost should include sub-session costs (TotalCost, not OwnCost)" )
983987}
984988
989+ func TestEmitStartupInfo_LastMessageFinishReason (t * testing.T ) {
990+ // When restoring a session whose last assistant message has a
991+ // FinishReason, the emitted TokenUsageEvent.LastMessage must carry
992+ // that FinishReason so the UI can identify the final response.
993+ prov := & mockProvider {id : "test/startup-model" , stream : & mockStream {}}
994+ root := agent .New ("root" , "agent" ,
995+ agent .WithModel (prov ),
996+ agent .WithDescription ("Root" ),
997+ )
998+ tm := team .New (team .WithAgents (root ))
999+
1000+ rt , err := NewLocalRuntime (tm , WithCurrentAgent ("root" ),
1001+ WithModelStore (mockModelStoreWithLimit {limit : 128_000 }))
1002+ require .NoError (t , err )
1003+
1004+ sess := session .New ()
1005+ sess .InputTokens = 500
1006+ sess .OutputTokens = 200
1007+
1008+ sess .Messages = append (sess .Messages , session.Item {
1009+ Message : & session.Message {
1010+ AgentName : "root" ,
1011+ Message : chat.Message {
1012+ Role : chat .MessageRoleAssistant ,
1013+ Content : "final answer" ,
1014+ Cost : 0.02 ,
1015+ Model : "test/startup-model" ,
1016+ FinishReason : chat .FinishReasonStop ,
1017+ Usage : & chat.Usage {InputTokens : 500 , OutputTokens : 200 },
1018+ },
1019+ },
1020+ })
1021+
1022+ events := make (chan Event , 20 )
1023+ rt .EmitStartupInfo (t .Context (), sess , events )
1024+ close (events )
1025+
1026+ var tokenEvent * TokenUsageEvent
1027+ for event := range events {
1028+ if te , ok := event .(* TokenUsageEvent ); ok {
1029+ tokenEvent = te
1030+ }
1031+ }
1032+
1033+ require .NotNil (t , tokenEvent , "should emit TokenUsageEvent" )
1034+ require .NotNil (t , tokenEvent .Usage .LastMessage , "LastMessage should be populated on session restore" )
1035+ assert .Equal (t , chat .FinishReasonStop , tokenEvent .Usage .LastMessage .FinishReason )
1036+ assert .Equal (t , "test/startup-model" , tokenEvent .Usage .LastMessage .Model )
1037+ assert .InDelta (t , 0.02 , tokenEvent .Usage .LastMessage .Cost , 0.0001 )
1038+ assert .Equal (t , int64 (500 ), tokenEvent .Usage .LastMessage .InputTokens )
1039+ assert .Equal (t , int64 (200 ), tokenEvent .Usage .LastMessage .OutputTokens )
1040+ }
1041+
9851042func TestEmitStartupInfo_NilSessionNoTokenEvent (t * testing.T ) {
9861043 // When sess is nil, no TokenUsageEvent should be emitted.
9871044 prov := & mockProvider {id : "test/startup-model" , stream : & mockStream {}}
0 commit comments