mirror of
https://gitcode.com/JianFeeeee/ModelRouter.git
synced 2026-09-20 08:57:57 +00:00
fix: anthropic tool-call round-trip, cache zero-hit parity, round-robin load balancing
anthropic.lua v3.0.0: - Issue 1: tool_result/tool_use round-trip - Issue 3: thinking default OFF (opt-in via extra_body.thinking) - Issue 4: tool_choice mapping - Issue 5: collect_blocks preserves unknown part types - message_stop no longer emits done=true (was overwriting tool_calls finish_reason) - cache_read_input_tokens normalized even at 0 gemini.lua: - transform_response was missing cachedContentTokenCount openai.lua (Issue 6): - transform_error handles flat envelopes, nginx HTML, bare text chat.go mergeUsage: - Keep PromptTokensDetails even when CachedTokens=0 scheduler.go: - Remove sort.SliceStable by Pref; round-robin cursor is the only LB mechanism provider.go ModelAvailable: - Also check Pref() > prefMin, persistently failing slots exit cands presets.go: - 17 built-in source templates Tests: 6 new test functions, 2 updated for new semantics
This commit is contained in:
@ -248,10 +248,9 @@ func (s *Scheduler) chainDrive(ctx context.Context, chain *Chain, req *types.Cha
|
||||
ce.Skipped = append(ce.Skipped, fmt.Sprintf("tier %d: no schedulable slot (cooling or quota exhausted)", tn.Tier))
|
||||
continue
|
||||
}
|
||||
// preference orders a same-tier run; stable so equal prefs keep order
|
||||
sort.SliceStable(cands, func(i, j int) bool {
|
||||
return cands[i].Prov.Pref(cands[i].Model) > cands[j].Prov.Pref(cands[j].Model)
|
||||
})
|
||||
// No Pref sort: load balancing is done by round-robin cursor.
|
||||
// Persistently failing slots are excluded by ModelAvailable
|
||||
// (which checks Pref > prefMin).
|
||||
base := tn.NextStart()
|
||||
res := runTier(ctx, tn, cands, base, req, stream)
|
||||
if res.resp != nil || res.chunks != nil {
|
||||
@ -350,9 +349,7 @@ func (s *Scheduler) ChainImage(ctx context.Context, chain *Chain, req *types.Ima
|
||||
ce.Skipped = append(ce.Skipped, fmt.Sprintf("tier %d: no schedulable slot (cooling)", tn.Tier))
|
||||
continue
|
||||
}
|
||||
sort.SliceStable(cands, func(i, j int) bool {
|
||||
return cands[i].Prov.Pref(cands[i].Model) > cands[j].Prov.Pref(cands[j].Model)
|
||||
})
|
||||
// No Pref sort: load balancing is done by round-robin cursor.
|
||||
base := tn.NextStart()
|
||||
var hard []TierError
|
||||
for i := 0; i < len(cands); i++ {
|
||||
|
||||
@ -177,14 +177,21 @@ func TestChainPreferenceSinksButStaysReachable(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("chain: %v", err)
|
||||
}
|
||||
// higher pref (good) is tried first and hard-fails; the pass moves on to
|
||||
// the negative-pref slot, which sinks but stays reachable: with the real
|
||||
// provider its success would RecordSuccess (+1 pref, self-heal)
|
||||
// Round-robin starts at index 0 (neg). neg is available (pref=-5 > -20)
|
||||
// and succeeds. good is never tried because neg already won.
|
||||
if src != "neg" || model != "n" || resp.Content != "neg" {
|
||||
t.Fatalf("served src=%q model=%q content=%q", src, model, resp.Content)
|
||||
}
|
||||
if good.chatHits.Load() == 0 {
|
||||
t.Fatal("higher-pref slot must be tried first")
|
||||
if good.chatHits.Load() != 0 {
|
||||
t.Fatal("neg was tried first and succeeded; good must not be attempted")
|
||||
}
|
||||
// Second request: cursor advances. neg wins again (good hard-fails).
|
||||
resp2, src2, _, err2 := s.ChainChat(context.Background(), ch, chatReq(), nil)
|
||||
if err2 != nil {
|
||||
t.Fatalf("second chain: %v", err2)
|
||||
}
|
||||
if src2 != "neg" || resp2.Content != "neg" {
|
||||
t.Fatalf("second req src=%q content=%q", src2, resp2.Content)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user