返回 DeepSeek-Reasonix
turn_phase.go
根目录 / internal / agent / turn_phase.go
1 package agent
2
3 import (
4 "time"
5
6 "reasonix/internal/event"
7 )
8
9 type phaseClock struct {
10 last event.TurnPhaseName
11 at time.Time
12 }
13
14 // emitTurnPhase publishes a content-free host phase for the active turn.
15 func (a *Agent) emitTurnPhase(phase event.TurnPhaseName) {
16 if a == nil || a.svc.sink == nil || phase == "" {
17 return
18 }
19 now := time.Now()
20 a.recordOpenPhase(now)
21 a.turn.phase = phaseClock{last: phase, at: now}
22 a.svc.sink.Emit(event.Event{Kind: event.TurnPhase, PhaseName: phase, Text: string(phase)})
23 }
24
25 // recordOpenPhase bills the phase that is ending. A span is accounted for only
26 // when something closes it, so a phase nobody follows records nothing.
27 func (a *Agent) recordOpenPhase(now time.Time) {
28 if a.turn.phase.at.IsZero() || a.capabilityAudit == nil {
29 return
30 }
31 a.capabilityAudit.RecordPhaseMs(phaseAuditName(a.turn.phase.last), now.Sub(a.turn.phase.at).Milliseconds())
32 }
33
34 // closeTurnPhase bills the open phase without opening another one, for the end
35 // of a turn: beginRunTurn resets a.turn, so an unclosed tail span is lost.
36 func (a *Agent) closeTurnPhase() {
37 if a == nil {
38 return
39 }
40 a.recordOpenPhase(time.Now())
41 a.turn.phase = phaseClock{}
42 }
43
44 func phaseAuditName(phase event.TurnPhaseName) string {
45 switch phase {
46 case event.TurnPhaseWorking:
47 return "provider"
48 case event.TurnPhaseChecking, event.TurnPhaseVerifying:
49 return "tool"
50 case event.TurnPhaseReviewing:
51 return "review"
52 default:
53 return ""
54 }
55 }
56
56 lines GO