Files
HomeAgent/internal/agent/api/router_test.go
root f960fde785 agent: 更智能的 LLM provider 调度(byModel 精确路由 + AUTO 优先级链)
吸收 llmsproxy 的调度思想适配 HomeAgent“一源一模型”结构:
- RoutableProvider{Model,Priority} 次级接口(不破坏既有 Provider 实现)
- ProviderManager.OrderedProviders 改为按 (优先级 desc, 可用, 默认优先) 稳定排序,
  AUTO/空模型走该优先级链
- 新增 ProviderManager.ResolveForModel:精确模型名路由到归属源,找不到回落 AUTO 链
- LuaAdaptedProvider 不再无条件覆写 req.Model;显式模型名原样转发
- LLMSource.Priority + core.llm.sources.<name>.priority 配置项
- process.go: 显式模型走 ResolveForModel,AUTO 走 OrderedProviders
- 新增路由单测(优先级排序 + byModel 解析)

验证: go test ./... 27 包 0 失败;Windows 交叉编译通过;部署后服务健康
2026-08-10 11:54:45 +08:00

66 lines
2.0 KiB
Go

package api
import (
"context"
"testing"
)
// stubRoutableProvider implements both Provider and RoutableProvider.
type stubRoutableProvider struct {
name string
model string
prio int
}
func (s *stubRoutableProvider) Name() string { return s.name }
func (s *stubRoutableProvider) Model() string { return s.model }
func (s *stubRoutableProvider) Priority() int { return s.prio }
func (s *stubRoutableProvider) MaxContextTokens() int { return 4096 }
func (s *stubRoutableProvider) Chat(context.Context, *CompletionRequest) (*CompletionResponse, error) {
return &CompletionResponse{Content: s.name}, nil
}
func (s *stubRoutableProvider) ChatStream(context.Context, *CompletionRequest) (<-chan StreamChunk, error) {
ch := make(chan StreamChunk, 1)
ch <- StreamChunk{Done: true}
return ch, nil
}
func TestProviderManagerPriorityOrder(t *testing.T) {
m := NewProviderManager()
m.Register("low", &stubRoutableProvider{name: "low", prio: 10})
m.Register("high", &stubRoutableProvider{name: "high", prio: 90})
m.Register("mid", &stubRoutableProvider{name: "mid", prio: 50})
got := m.OrderedProviders()
wantOrder := []string{"high", "mid", "low"}
for i, p := range got {
if p.Name() != wantOrder[i] {
t.Fatalf("order[%d] = %s, want %s (full=%v)", i, p.Name(), wantOrder[i], names(got))
}
}
}
func TestProviderManagerResolveForModel(t *testing.T) {
m := NewProviderManager()
m.Register("a", &stubRoutableProvider{name: "a", model: "gpt-5"})
m.Register("b", &stubRoutableProvider{name: "b", model: "deepseek"})
got := m.ResolveForModel("deepseek")
if len(got) == 0 || got[0].Name() != "b" {
t.Fatalf("resolve deepseek: got %v", names(got))
}
// 未知模型回落 AUTO 链(仍按优先级)
got2 := m.ResolveForModel("unknown")
if len(got2) == 0 {
t.Fatal("unknown model should fall back to auto chain")
}
}
func names(ps []Provider) []string {
out := make([]string, len(ps))
for i, p := range ps {
out[i] = p.Name()
}
return out
}