mirror of
https://github.com/BaaRai/ARTEX.git
synced 2026-11-13 11:40:53 +08:00
Seam fix after merging #111 (escalation) with #114 (ctx-threaded hooks.current): the escalation test predates the signature change and kept compiling on its own branch, so the merge left main red.
81 lines
2.7 KiB
Go
81 lines
2.7 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"iter"
|
|
"testing"
|
|
|
|
"github.com/Autumn-27/artex/db"
|
|
"github.com/Autumn-27/norma/llm"
|
|
)
|
|
|
|
type failingProvider struct{ err error }
|
|
|
|
func (f *failingProvider) Stream(ctx context.Context, req llm.CompletionRequest) iter.Seq2[llm.StreamEvent, error] {
|
|
return nil
|
|
}
|
|
|
|
func (f *failingProvider) Complete(ctx context.Context, req llm.CompletionRequest) (llm.Message, string, llm.Usage, error) {
|
|
return llm.Message{}, "", llm.Usage{}, f.err
|
|
}
|
|
|
|
func TestEscalationAdvancesAfterConsecutiveFailures(t *testing.T) {
|
|
resetLLMFailStreaksForTest()
|
|
prov := &failingProvider{err: errors.New("boom: upstream 500")}
|
|
profile := int64(7)
|
|
nextProfile := int64(8)
|
|
exhaustCalls := 0
|
|
hooks := taskLLMStreamHooks{
|
|
current: func(ctx context.Context) (taskLLMSelection, error) {
|
|
return taskLLMSelection{task: &Task{ID: "1"}, profileID: profile, revision: 1, provider: prov}, nil
|
|
},
|
|
exhaust: func(sel taskLLMSelection, cause error) (db.TaskLLMTransition, error) {
|
|
exhaustCalls++
|
|
if exhaustCalls == 1 {
|
|
return db.TaskLLMTransition{Advanced: true, NextProfileID: &nextProfile}, nil
|
|
}
|
|
return db.TaskLLMTransition{ChainExhausted: true}, nil
|
|
},
|
|
}
|
|
// first two failures: passthrough, chain untouched
|
|
for i := 1; i <= 2; i++ {
|
|
_, _, _, err := completeTaskLLM(context.Background(), "1", llm.CompletionRequest{}, hooks)
|
|
if err == nil || errors.Is(err, err) == false {
|
|
t.Fatalf("call %d: expected the raw failure", i)
|
|
}
|
|
if exhaustCalls != 0 {
|
|
t.Fatalf("call %d: exhaust fired early (%d)", i, exhaustCalls)
|
|
}
|
|
}
|
|
// third consecutive failure: escalates, advances to profile 8, which itself
|
|
// fails (streak 1) and returns the raw error
|
|
_, _, _, err := completeTaskLLM(context.Background(), "1", llm.CompletionRequest{}, hooks)
|
|
if err == nil {
|
|
t.Fatal("expected the follow-up failure from the next profile")
|
|
}
|
|
if exhaustCalls != 1 {
|
|
t.Fatalf("exhaust calls=%d want 1", exhaustCalls)
|
|
}
|
|
// The escalation inside call 3 already replayed once on the same profile
|
|
// (streak 1); the fake current() keeps reporting profile 7, so the streak
|
|
// key is unchanged. One more failure reaches streak 2, and the NEXT one
|
|
// fires the second escalation, which the fake exhaust answers with a fully
|
|
// exhausted chain.
|
|
_, _, _, err = completeTaskLLM(context.Background(), "1", llm.CompletionRequest{}, hooks)
|
|
if err == nil {
|
|
t.Fatal("expected raw failure at streak 2")
|
|
}
|
|
if exhaustCalls != 1 {
|
|
t.Fatalf("exhaust calls=%d want 1 at streak 2", exhaustCalls)
|
|
}
|
|
_, _, _, err = completeTaskLLM(context.Background(), "1", llm.CompletionRequest{}, hooks)
|
|
if !isTaskLLMChainExhausted(err) {
|
|
t.Fatalf("expected chain-exhausted classification, got %v", err)
|
|
}
|
|
if exhaustCalls != 2 {
|
|
t.Fatalf("exhaust calls=%d want 2", exhaustCalls)
|
|
}
|
|
}
|
|
|