-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathescalation.go
More file actions
133 lines (112 loc) · 3.16 KB
/
Copy pathescalation.go
File metadata and controls
133 lines (112 loc) · 3.16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package llmkit
// EscalationChain defines the order of models to try when escalating.
type EscalationChain struct {
// Models in ascending order of capability (e.g., haiku, sonnet, opus)
Models []ModelName
// MaxAttempts is the maximum total attempts before giving up
MaxAttempts int
}
// DefaultEscalation is the standard escalation chain.
var DefaultEscalation = EscalationChain{
Models: []ModelName{ModelSonnet, ModelOpus},
MaxAttempts: 3,
}
// FullEscalation starts from haiku and goes through all tiers.
var FullEscalation = EscalationChain{
Models: []ModelName{ModelHaiku, ModelSonnet, ModelOpus},
MaxAttempts: 5,
}
// NoEscalation disables model escalation (retry same model).
var NoEscalation = EscalationChain{
Models: nil,
MaxAttempts: 3,
}
// Next returns the next model to try after a failure.
// Returns the next model in the chain and whether to continue.
// If already at the highest tier or max attempts reached, returns ("", false).
func (e *EscalationChain) Next(current ModelName, attempt int) (ModelName, bool) {
// Check if we've exhausted attempts
if attempt >= e.MaxAttempts {
return "", false
}
// No escalation chain = retry same model
if len(e.Models) == 0 {
return current, true
}
// Find current model in chain
idx := -1
for i, m := range e.Models {
if m == current {
idx = i
break
}
}
// Model not in chain - start at beginning if any attempts left
if idx < 0 {
if len(e.Models) > 0 {
return e.Models[0], true
}
return current, true
}
// Already at highest tier - stay there
if idx >= len(e.Models)-1 {
return current, true
}
// Escalate to next tier
return e.Models[idx+1], true
}
// CanEscalate returns true if the current model can escalate to a higher tier.
func (e *EscalationChain) CanEscalate(current ModelName) bool {
if len(e.Models) == 0 {
return false
}
for i, m := range e.Models {
if m == current {
return i < len(e.Models)-1
}
}
return false
}
// HighestModel returns the highest capability model in the chain.
func (e *EscalationChain) HighestModel() ModelName {
if len(e.Models) == 0 {
return ModelSonnet
}
return e.Models[len(e.Models)-1]
}
// EscalationState tracks the state of an escalation attempt.
type EscalationState struct {
Chain *EscalationChain
CurrentModel ModelName
Attempt int
LastError error
}
// NewEscalationState creates a new escalation state starting at the given model.
func NewEscalationState(chain *EscalationChain, startModel ModelName) *EscalationState {
if chain == nil {
chain = &DefaultEscalation
}
return &EscalationState{
Chain: chain,
CurrentModel: startModel,
Attempt: 0,
}
}
// RecordFailure records a failed attempt and escalates if possible.
// Returns true if escalation occurred and there are more attempts available.
func (s *EscalationState) RecordFailure(err error) bool {
s.Attempt++
s.LastError = err
next, ok := s.Chain.Next(s.CurrentModel, s.Attempt)
if !ok {
return false
}
if next != s.CurrentModel {
s.CurrentModel = next
}
return true
}
// Exhausted returns true if all attempts have been used.
func (s *EscalationState) Exhausted() bool {
return s.Attempt >= s.Chain.MaxAttempts
}