Files
docker-agent/pkg/model/provider/aliases_test.go
T
Arnaud Héritier fa4b9fec0e feat(provider): add Lunaroute OpenAI-compatible provider
Register Lunaroute at https://gw.lunaroute.com/v1 with LUNAROUTE_API_KEY
and glm-5.3 as its default model. Support credential-based provider
selection, direct authenticated model discovery, and the existing OpenAI-compatible request path.

Document provider setup and limitations, add an example configuration,
and cover credential handling, model discovery, and provider requests
with tests.

Closes #4749
2026-10-28 15:00:43 +01:00

168 lines
6.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package provider
import (
"maps"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/docker/docker-agent/pkg/modelsdev"
)
func TestLookupAlias(t *testing.T) {
t.Parallel()
// Every entry in the table is reachable.
for name, expected := range Aliases {
got, ok := LookupAlias(name)
assert.True(t, ok, "alias %q should be found", name)
assert.Equal(t, expected, got)
}
// Unknown name yields the zero Alias and false.
got, ok := LookupAlias("does-not-exist")
assert.False(t, ok)
assert.Equal(t, Alias{}, got)
// Lookup is case-sensitive (callers normalise themselves).
if _, ok := LookupAlias("MISTRAL"); ok {
t.Errorf("LookupAlias should be case-sensitive")
}
}
// TestCatalogAliases asserts each self-contained OpenAI-compatible alias
// resolves to its expected configuration and is reported as both a known and a
// catalog provider. New aliases of the same shape only need a row here.
func TestCatalogAliases(t *testing.T) {
t.Parallel()
expected := map[string]Alias{
"openrouter": {APIType: "openai", BaseURL: "https://openrouter.ai/api/v1", TokenEnvVar: "OPENROUTER_API_KEY"},
"api-route": {APIType: "openai", BaseURL: "https://global.api-route.com/v1", TokenEnvVar: "API_ROUTE_API_KEY"},
"lunaroute": {APIType: "openai", BaseURL: "https://gw.lunaroute.com/v1", TokenEnvVar: "LUNAROUTE_API_KEY"},
"orcarouter": {APIType: "openai", BaseURL: "https://api.orcarouter.ai/v1", TokenEnvVar: "ORCAROUTER_API_KEY"},
"atlascloud": {APIType: "openai", BaseURL: "https://api.atlascloud.ai/v1", TokenEnvVar: "ATLASCLOUD_API_KEY"},
"runcrate": {APIType: "openai", BaseURL: "https://api.runcrate.ai/v1", TokenEnvVar: "RUNCRATE_API_KEY"},
"opper": {APIType: "openai", BaseURL: "https://api.opper.ai/v3/compat", TokenEnvVar: "OPPER_API_KEY"},
"baseten": {APIType: "openai", BaseURL: "https://inference.baseten.co/v1", TokenEnvVar: "BASETEN_API_KEY"},
"daoxe": {APIType: "openai", BaseURL: "https://api.daoxe.com/v1", TokenEnvVar: "DAOXE_API_KEY"},
"ovhcloud": {APIType: "openai", BaseURL: "https://oai.endpoints.kepler.ai.cloud.ovh.net/v1", TokenEnvVar: "OVH_AI_ENDPOINTS_ACCESS_TOKEN"},
"groq": {APIType: "openai", BaseURL: "https://api.groq.com/openai/v1", TokenEnvVar: "GROQ_API_KEY"},
"deepseek": {APIType: "openai", BaseURL: "https://api.deepseek.com/v1", TokenEnvVar: "DEEPSEEK_API_KEY"},
"cerebras": {APIType: "openai", BaseURL: "https://api.cerebras.ai/v1", TokenEnvVar: "CEREBRAS_API_KEY"},
"fireworks-ai": {APIType: "openai", BaseURL: "https://api.fireworks.ai/inference/v1", TokenEnvVar: "FIREWORKS_API_KEY"},
"togetherai": {APIType: "openai", BaseURL: "https://api.together.xyz/v1", TokenEnvVar: "TOGETHER_API_KEY"},
"huggingface": {APIType: "openai", BaseURL: "https://router.huggingface.co/v1", TokenEnvVar: "HF_TOKEN"},
"moonshotai": {APIType: "openai", BaseURL: "https://api.moonshot.ai/v1", TokenEnvVar: "MOONSHOT_API_KEY"},
"nvidia": {APIType: "openai", BaseURL: "https://integrate.api.nvidia.com/v1", TokenEnvVar: "NVIDIA_API_KEY"},
"vercel": {APIType: "openai", BaseURL: "https://ai-gateway.vercel.sh/v1", TokenEnvVar: "AI_GATEWAY_API_KEY"},
}
for name, want := range expected {
t.Run(name, func(t *testing.T) {
t.Parallel()
alias, ok := LookupAlias(name)
require.True(t, ok)
assert.Equal(t, want, alias)
assert.True(t, IsKnownProvider(name))
assert.True(t, IsCatalogProvider(name))
})
}
}
// TestCloudflareAliases asserts the two Cloudflare aliases resolve to their
// account/gateway-scoped, env-templated base URLs. Unlike the static aliases
// above, their BaseURL carries ${...} references that are expanded at
// provider-build time (see cloudflare_alias_test.go for the resolution path).
func TestCloudflareAliases(t *testing.T) {
t.Parallel()
expected := map[string]Alias{
"cloudflare-workers-ai": {
APIType: "openai",
BaseURL: "https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/ai/v1",
TokenEnvVar: "CLOUDFLARE_API_TOKEN",
},
"cloudflare-ai-gateway": {
APIType: "openai",
BaseURL: "https://gateway.ai.cloudflare.com/v1/${CLOUDFLARE_ACCOUNT_ID}/${CLOUDFLARE_GATEWAY_ID}/compat",
TokenEnvVar: "CLOUDFLARE_API_TOKEN",
},
}
for name, want := range expected {
t.Run(name, func(t *testing.T) {
t.Parallel()
alias, ok := LookupAlias(name)
require.True(t, ok)
assert.Equal(t, want, alias)
assert.True(t, IsKnownProvider(name))
})
}
}
func TestEachAlias(t *testing.T) {
t.Parallel()
// Iterator yields every entry exactly once.
collected := maps.Collect(EachAlias())
assert.Equal(t, Aliases, collected)
}
func TestEachAlias_EarlyTermination(t *testing.T) {
t.Parallel()
// Iterator must respect a false return from the yield function.
require.NotEmpty(t, Aliases, "test requires the alias table to be non-empty")
count := 0
for range EachAlias() {
count++
if count == 1 {
break
}
}
assert.Equal(t, 1, count, "iteration should stop when consumer breaks out")
}
func TestProviderIDsMatchEmbeddedCatalog(t *testing.T) {
t.Parallel()
db := modelsdev.EmbeddedSnapshot()
absent := map[string]string{
"api-route": "model IDs come from API Route's /v1/models endpoint, not models.dev",
"atlascloud": "model IDs come from Atlas Cloud's /v1/models endpoint, not models.dev",
"chatgpt": "subscription backend, distinct from OpenAI",
"lunaroute": "model IDs come from Lunaroute’s /v1/models endpoint, not models.dev",
"runcrate": "model IDs come from Runcrate's /v1/models endpoint, not models.dev",
"dmr": "local Docker Model Runner",
"ollama": "local Ollama server",
}
for _, name := range AllProviders() {
t.Run(name, func(t *testing.T) {
t.Parallel()
assert.Equal(t, name, modelsdev.CanonicalProviderID(name))
_, exists := db.Providers[name]
if reason, exempt := absent[name]; exempt {
assert.False(t, exists, "remove stale exemption: %s", reason)
} else {
assert.True(t, exists, "built-in provider must use its models.dev ID")
}
})
}
for _, legacy := range []string{"fireworks", "together", "moonshot", "opencode-zen"} {
canonical := modelsdev.CanonicalProviderID(legacy)
assert.NotEqual(t, legacy, canonical)
assert.NotContains(t, Aliases, legacy)
assert.NotContains(t, AllProviders(), legacy)
assert.NotContains(t, db.Providers, legacy, "legacy ID now collides with an upstream service")
require.Contains(t, Aliases, canonical)
got, ok := LookupAlias(legacy)
require.True(t, ok)
assert.Equal(t, Aliases[canonical], got)
assert.True(t, IsKnownProvider(legacy))
}
}