- 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
116 lines
3.4 KiB
Go
116 lines
3.4 KiB
Go
package proxy
|
|
|
|
import (
|
|
"sort"
|
|
|
|
"github.com/Atte149/ollama-proxy/internal/config"
|
|
)
|
|
|
|
// ollamaCloudModels is the set of model IDs served by Ollama Cloud.
|
|
// These overlap partly with the OpenCode Go catalog.
|
|
var ollamaCloudModels = []string{
|
|
"deepseek-v3.1:671b", "deepseek-v3.2", "deepseek-v4-flash", "deepseek-v4-pro",
|
|
"devstral-2:123b", "devstral-small-2:24b",
|
|
"gemini-3-flash-preview", "gemma3:12b", "gemma3:27b", "gemma3:4b", "gemma4:31b",
|
|
"glm-4.7", "glm-5", "glm-5.1", "glm-5.2",
|
|
"gpt-oss:120b", "gpt-oss:20b",
|
|
"kimi-k2.5", "kimi-k2.6", "kimi-k2.7-code",
|
|
"minimax-m2.1", "minimax-m2.5", "minimax-m2.7", "minimax-m3",
|
|
"ministral-3:14b", "ministral-3:3b", "ministral-3:8b",
|
|
"mistral-large-3:675b",
|
|
"nemotron-3-nano:30b", "nemotron-3-super", "nemotron-3-ultra",
|
|
"qwen3-coder:480b", "qwen3-coder-next", "qwen3.5:397b",
|
|
"rnj-1:8b",
|
|
}
|
|
|
|
// openCodeGoModels is the set of model IDs served by OpenCode Go.
|
|
var openCodeGoModels = []string{
|
|
"deepseek-v4-flash", "deepseek-v4-pro",
|
|
"glm-5", "glm-5.1", "glm-5.2",
|
|
"hy3-preview",
|
|
"kimi-k2.5", "kimi-k2.6", "kimi-k2.7-code",
|
|
"mimo-v2-omni", "mimo-v2-pro", "mimo-v2.5", "mimo-v2.5-pro",
|
|
"minimax-m2.5", "minimax-m2.7", "minimax-m3",
|
|
"qwen3.5-plus", "qwen3.6-plus", "qwen3.7-max", "qwen3.7-plus",
|
|
}
|
|
|
|
// modelProviders maps a model ID to the providers that can serve it.
|
|
// Built once from the two catalogs above.
|
|
var modelProviders = func() map[string][]config.ProviderType {
|
|
m := make(map[string][]config.ProviderType, len(ollamaCloudModels)+len(openCodeGoModels))
|
|
add := func(id string, p config.ProviderType) {
|
|
m[id] = append(m[id], p)
|
|
}
|
|
for _, id := range ollamaCloudModels {
|
|
add(id, config.ProviderOllamaCloud)
|
|
}
|
|
for _, id := range openCodeGoModels {
|
|
// Deduplicate: if already present (common model), append; else create.
|
|
found := false
|
|
for _, ex := range m[id] {
|
|
if ex == config.ProviderOpenCodeGo {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
add(id, config.ProviderOpenCodeGo)
|
|
}
|
|
}
|
|
return m
|
|
}()
|
|
|
|
// ProvidersForModel returns the providers eligible to serve the given model.
|
|
// Unknown models default to Ollama Cloud (the original behaviour).
|
|
func ProvidersForModel(model string) []config.ProviderType {
|
|
if ps, ok := modelProviders[model]; ok {
|
|
return ps
|
|
}
|
|
return []config.ProviderType{config.ProviderOllamaCloud}
|
|
}
|
|
|
|
// AllModels returns the deduplicated, sorted list of all model IDs known to the
|
|
// router (union of both catalogs).
|
|
func AllModels() []string {
|
|
seen := make(map[string]struct{})
|
|
for id := range modelProviders {
|
|
seen[id] = struct{}{}
|
|
}
|
|
out := make([]string, 0, len(seen))
|
|
for id := range seen {
|
|
out = append(out, id)
|
|
}
|
|
sort.Strings(out)
|
|
return out
|
|
}
|
|
|
|
// IsCommonModel reports whether the model is served by both providers.
|
|
func IsCommonModel(model string) bool {
|
|
ps := ProvidersForModel(model)
|
|
if len(ps) < 2 {
|
|
return false
|
|
}
|
|
hasOllama, hasGo := false, false
|
|
for _, p := range ps {
|
|
switch p {
|
|
case config.ProviderOllamaCloud:
|
|
hasOllama = true
|
|
case config.ProviderOpenCodeGo:
|
|
hasGo = true
|
|
}
|
|
}
|
|
return hasOllama && hasGo
|
|
}
|
|
|
|
// ProviderLabel returns a short human label for a provider, used to annotate
|
|
// unique models in the merged /v1/models response and in opencode config names.
|
|
func ProviderLabel(p config.ProviderType) string {
|
|
switch p {
|
|
case config.ProviderOpenCodeGo:
|
|
return "go"
|
|
case config.ProviderOllamaCloud:
|
|
return "ollama"
|
|
default:
|
|
return string(p)
|
|
}
|
|
}
|