a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
113 lines
3.8 KiB
Go
113 lines
3.8 KiB
Go
package svc
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/zzet/gortex/internal/llm"
|
|
)
|
|
|
|
// fakeAgentProvider is a minimal llm.Provider that always answers a
|
|
// tool-call request by calling final_answer — enough to drive one
|
|
// RunAgent turn to completion without a real model.
|
|
type fakeAgentProvider struct{ calls int }
|
|
|
|
func (p *fakeAgentProvider) Name() string { return "fake" }
|
|
func (p *fakeAgentProvider) Close() error { return nil }
|
|
func (p *fakeAgentProvider) Complete(_ context.Context, _ llm.CompletionRequest) (llm.CompletionResponse, error) {
|
|
p.calls++
|
|
return llm.CompletionResponse{Text: `{"tool":"final_answer","args":{"text":"done"}}`}, nil
|
|
}
|
|
|
|
func TestProviderForModel_BaseAndRouted(t *testing.T) {
|
|
t.Setenv("ANTHROPIC_API_KEY", "test-key")
|
|
cfg := llm.Config{Provider: "anthropic", Anthropic: llm.AnthropicConfig{RemoteConfig: llm.RemoteConfig{Model: "claude-sonnet-4-6"}}}.ApplyDefaults()
|
|
s := NewService(cfg, llm.MockBackend{})
|
|
if !s.Enabled() {
|
|
t.Fatal("service should be enabled with an API key set")
|
|
}
|
|
defer s.Close()
|
|
|
|
// Empty model id, or the active model, returns the base provider.
|
|
if p, err := s.providerForModel(""); err != nil || p != s.provider {
|
|
t.Errorf("providerForModel(\"\") = (%v, %v), want the base provider", p, err)
|
|
}
|
|
if p, err := s.providerForModel("claude-sonnet-4-6"); err != nil || p != s.provider {
|
|
t.Errorf("providerForModel(active model) must return the base provider")
|
|
}
|
|
|
|
// A different model id constructs and caches a distinct provider.
|
|
routed, err := s.providerForModel("claude-haiku-4-5")
|
|
if err != nil {
|
|
t.Fatalf("providerForModel(routed): %v", err)
|
|
}
|
|
if routed == s.provider {
|
|
t.Error("a routed model must yield a distinct provider, not the base")
|
|
}
|
|
again, err := s.providerForModel("claude-haiku-4-5")
|
|
if err != nil || again != routed {
|
|
t.Errorf("providerForModel must cache: got %v (err %v), want the first routed provider", again, err)
|
|
}
|
|
}
|
|
|
|
func TestProviderForModel_ClosedByClose(t *testing.T) {
|
|
t.Setenv("ANTHROPIC_API_KEY", "test-key")
|
|
cfg := llm.Config{Provider: "anthropic", Anthropic: llm.AnthropicConfig{RemoteConfig: llm.RemoteConfig{Model: "claude-sonnet-4-6"}}}.ApplyDefaults()
|
|
s := NewService(cfg, llm.MockBackend{})
|
|
if _, err := s.providerForModel("claude-haiku-4-5"); err != nil {
|
|
t.Fatalf("providerForModel: %v", err)
|
|
}
|
|
if len(s.routedProviders) != 1 {
|
|
t.Fatalf("routedProviders has %d entries, want 1", len(s.routedProviders))
|
|
}
|
|
if err := s.Close(); err != nil {
|
|
t.Errorf("Close: %v", err)
|
|
}
|
|
if len(s.routedProviders) != 0 {
|
|
t.Errorf("Close must clear routedProviders, %d left", len(s.routedProviders))
|
|
}
|
|
}
|
|
|
|
func TestRunAgent_RoutingClassifiesComplexity(t *testing.T) {
|
|
fake := &fakeAgentProvider{}
|
|
s := newFakeService(fake)
|
|
s.cfg.Routing.Enabled = true // tier models empty → both route to the base provider
|
|
|
|
complex, err := s.RunAgent(context.Background(), llm.RunAgentOptions{
|
|
Question: "trace the request across systems",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("RunAgent: %v", err)
|
|
}
|
|
if complex.Complexity != "complex" {
|
|
t.Errorf("Complexity = %q, want complex (\"trace ... across\")", complex.Complexity)
|
|
}
|
|
|
|
simple, err := s.RunAgent(context.Background(), llm.RunAgentOptions{
|
|
Question: "who calls NewServer",
|
|
Scope: llm.Scope{Repo: "gortex"},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("RunAgent: %v", err)
|
|
}
|
|
if simple.Complexity != "simple" {
|
|
t.Errorf("Complexity = %q, want simple", simple.Complexity)
|
|
}
|
|
}
|
|
|
|
func TestRunAgent_NoRoutingWhenDisabled(t *testing.T) {
|
|
fake := &fakeAgentProvider{}
|
|
s := newFakeService(fake)
|
|
// Routing left disabled.
|
|
|
|
ans, err := s.RunAgent(context.Background(), llm.RunAgentOptions{
|
|
Question: "trace the request across every system everywhere",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("RunAgent: %v", err)
|
|
}
|
|
if ans.Complexity != "" {
|
|
t.Errorf("Complexity = %q, want empty when routing is disabled", ans.Complexity)
|
|
}
|
|
}
|