feat: multi-provider support — Ollama Cloud + OpenCode Go
- Account.Provider field (ollama-cloud | opencode-go), backward compatible - Model-based routing: common models served by combined pool, unique models routed to their provider only - /go/v1/* path forces OpenCode Go provider (prefix stripped upstream) - Merged /v1/models endpoint returns union of both catalogs (44 models) - Failover: 429/402 → cooldown + failover; 5xx → retry without cooldown - CLI: accounts add --provider flag, list shows provider column - Body buffering: request body buffered (8 MiB cap) for failover replay - opencode integration: unified provider 'oc' with all merged models - 64 tests pass (unit + integration) - Verified: glm-5 → ollama, mimo-v2.5 → go, gpt-oss:20b → ollama
This commit is contained in:
parent
98bbc96bf5
commit
4fe15324c8
11 changed files with 973 additions and 125 deletions
99
internal/proxy/model_router_test.go
Normal file
99
internal/proxy/model_router_test.go
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
package proxy
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/Atte149/ollama-proxy/internal/config"
|
||||
)
|
||||
|
||||
func TestProvidersForModel_Common(t *testing.T) {
|
||||
for _, m := range []string{"glm-5", "glm-5.1", "glm-5.2", "kimi-k2.5", "kimi-k2.6",
|
||||
"kimi-k2.7-code", "deepseek-v4-flash", "deepseek-v4-pro",
|
||||
"minimax-m2.5", "minimax-m2.7", "minimax-m3"} {
|
||||
ps := ProvidersForModel(m)
|
||||
if len(ps) != 2 {
|
||||
t.Errorf("ProvidersForModel(%q) = %v, want 2 providers", m, ps)
|
||||
continue
|
||||
}
|
||||
if !IsCommonModel(m) {
|
||||
t.Errorf("IsCommonModel(%q) = false, want true", m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvidersForModel_UniqueOllama(t *testing.T) {
|
||||
for _, m := range []string{"gpt-oss:20b", "gpt-oss:120b", "qwen3-coder:480b",
|
||||
"gemma3:4b", "nemotron-3-ultra", "devstral-2:123b", "rnj-1:8b",
|
||||
"mistral-large-3:675b", "minimax-m2.1", "glm-4.7", "gemini-3-flash-preview",
|
||||
"qwen3.5:397b", "qwen3-coder-next", "ministral-3:3b",
|
||||
"deepseek-v3.1:671b", "deepseek-v3.2"} {
|
||||
ps := ProvidersForModel(m)
|
||||
if len(ps) != 1 || ps[0] != config.ProviderOllamaCloud {
|
||||
t.Errorf("ProvidersForModel(%q) = %v, want [ollama-cloud]", m, ps)
|
||||
}
|
||||
if IsCommonModel(m) {
|
||||
t.Errorf("IsCommonModel(%q) = true, want false", m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvidersForModel_UniqueGo(t *testing.T) {
|
||||
for _, m := range []string{"mimo-v2.5", "mimo-v2.5-pro", "mimo-v2-omni", "mimo-v2-pro",
|
||||
"qwen3.5-plus", "qwen3.6-plus", "qwen3.7-max", "qwen3.7-plus", "hy3-preview"} {
|
||||
ps := ProvidersForModel(m)
|
||||
if len(ps) != 1 || ps[0] != config.ProviderOpenCodeGo {
|
||||
t.Errorf("ProvidersForModel(%q) = %v, want [opencode-go]", m, ps)
|
||||
}
|
||||
if IsCommonModel(m) {
|
||||
t.Errorf("IsCommonModel(%q) = true, want false", m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvidersForModel_UnknownDefaultsOllama(t *testing.T) {
|
||||
ps := ProvidersForModel("bogus-model")
|
||||
if len(ps) != 1 || ps[0] != config.ProviderOllamaCloud {
|
||||
t.Errorf("ProvidersForModel(unknown) = %v, want [ollama-cloud]", ps)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllModels_DedupSorted(t *testing.T) {
|
||||
all := AllModels()
|
||||
if len(all) == 0 {
|
||||
t.Fatal("AllModels returned empty list")
|
||||
}
|
||||
// Verify sorted.
|
||||
for i := 1; i < len(all); i++ {
|
||||
if all[i-1] >= all[i] {
|
||||
t.Errorf("AllModels not sorted at %d: %q >= %q", i, all[i-1], all[i])
|
||||
}
|
||||
}
|
||||
// Verify no duplicates.
|
||||
seen := make(map[string]bool)
|
||||
for _, m := range all {
|
||||
if seen[m] {
|
||||
t.Errorf("duplicate model in AllModels: %s", m)
|
||||
}
|
||||
seen[m] = true
|
||||
}
|
||||
// Common models must appear once, not twice.
|
||||
common := "glm-5"
|
||||
count := 0
|
||||
for _, m := range all {
|
||||
if m == common {
|
||||
count++
|
||||
}
|
||||
}
|
||||
if count != 1 {
|
||||
t.Errorf("common model %s appeared %d times, want 1", common, count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProviderLabel(t *testing.T) {
|
||||
if ProviderLabel(config.ProviderOllamaCloud) != "ollama" {
|
||||
t.Error("ollama label wrong")
|
||||
}
|
||||
if ProviderLabel(config.ProviderOpenCodeGo) != "go" {
|
||||
t.Error("go label wrong")
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue