diff --git a/management/internals/modules/agentnetwork/catalog/catalog.go b/management/internals/modules/agentnetwork/catalog/catalog.go index f82e94bf3..fe47a33a2 100644 --- a/management/internals/modules/agentnetwork/catalog/catalog.go +++ b/management/internals/modules/agentnetwork/catalog/catalog.go @@ -25,8 +25,8 @@ type Model struct { // These typically need NetBird identity stamped onto upstream // requests so the gateway's analytics and budgets attribute to the // real caller; that's what IdentityInjection is for. -// - KindCustom: the catch-all "OpenAI-compatible self-hosted endpoint" -// entry (vLLM, Ollama, custom inference servers). +// - KindCustom: named and generic OpenAI-compatible self-hosted +// endpoints (vLLM, Ollama, custom inference servers). // // Frontend uses Kind to group the provider Select in the modal so an // operator can spot at a glance which catalog entries proxy other @@ -701,11 +701,26 @@ var providers = []Provider{ BrandColor: "#30A2FF", Models: []Model{}, }, + { + // Ollama exposes an OpenAI-compatible /v1 API. Like vLLM, it gets a + // dedicated catalog id for provider-specific setup guidance while + // retaining the generic custom-provider routing and auth behavior. + ID: "ollama", + Kind: KindCustom, + Name: "Ollama", + Description: "Self-hosted Ollama (OpenAI-compatible)", + DefaultHost: "", + AuthHeaderName: "Authorization", + AuthHeaderTemplate: "Bearer ${API_KEY}", + DefaultContentType: "application/json", + BrandColor: "#000000", + Models: []Model{}, + }, { ID: "custom", Kind: KindCustom, Name: "Custom / Self-hosted", - Description: "OpenAI-compatible endpoint (vLLM, Ollama, …)", + Description: "Other OpenAI-compatible endpoint", DefaultHost: "", AuthHeaderName: "Authorization", AuthHeaderTemplate: "Bearer ${API_KEY}", diff --git a/management/internals/modules/agentnetwork/catalog/catalog_test.go b/management/internals/modules/agentnetwork/catalog/catalog_test.go new file mode 100644 index 000000000..96b950833 --- /dev/null +++ b/management/internals/modules/agentnetwork/catalog/catalog_test.go @@ -0,0 +1,31 @@ +package catalog + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/netbirdio/netbird/shared/management/http/api" +) + +func TestOllamaCatalogEntry(t *testing.T) { + entry, ok := Lookup("ollama") + require.True(t, ok, "Ollama must be available as a dedicated catalog provider") + + assert.Equal(t, KindCustom, entry.Kind) + assert.Equal(t, "Ollama", entry.Name) + assert.Equal(t, "Self-hosted Ollama (OpenAI-compatible)", entry.Description) + assert.Empty(t, entry.DefaultHost, "the dashboard owns Ollama's scheme-aware HTTP placeholder") + assert.Equal(t, "Authorization", entry.AuthHeaderName) + assert.Equal(t, "Bearer ${API_KEY}", entry.AuthHeaderTemplate) + assert.Equal(t, "application/json", entry.DefaultContentType) + assert.Empty(t, entry.ParserID, "phase one must preserve the untagged vLLM/custom routing behavior") + assert.Empty(t, entry.Models, "Ollama models are installed dynamically on the configured endpoint") + + wire := entry.ToAPIResponse() + assert.Equal(t, "ollama", wire.Id) + assert.Equal(t, api.AgentNetworkCatalogProviderKindCustom, wire.Kind) + assert.NotNil(t, wire.Models) + assert.Empty(t, wire.Models) +}