mirror of
https://github.com/coder/coder.git
synced 2026-10-31 19:36:41 +08:00
Two live `ai_providers` rows can share the same `base_url` hostname.
This is an intended configuration (AIGOV-97): the direct routing path
(`/api/v2/ai-gateway/{name}/...`) routes by provider name and fully
supports it. Only the MITM proxy path (`aibridgeproxyd`) cannot
disambiguate, because its `nameByHost` map is 1:1.
`classifyProviderRow` marked the second provider as
`ProviderStatusError`, which surfaced as a misleading log warning and a
`status=error` Prometheus label. The provider is not actually broken; it
is fully routable via the direct path. The error was only visible in
server logs and Prometheus, not in the API or UI.
This change:
- Extracts `BaseURLHostname` into `coderd/aibridged` and eliminates
three duplicate implementations (proxy classifier, chatprovider, and the
API layer now uses it for status checks).
- Downgrades the proxy classifier from `ProviderStatusError` to a new
`ProviderStatusProxyExcluded` status. The provider is not broken; it is
just not reachable via the MITM proxy path.
- Adds `Status *AIProviderStatus` to `codersdk.AIProvider` with
`Warnings []string`, matching the `WorkspaceProxy` precedent.
- Populates warnings on create, update, GET, and list when a provider's
hostname collides with another enabled provider.
- Adds an integration test proving that a duplicate provider is routable
via the direct path but not via the proxy path.
Refs AIGOV-596
Generated by Coder Agents
https://github.com/user-attachments/assets/bf323837-5121-4d1b-ab19-593f1d32374e
34 lines
924 B
Go
34 lines
924 B
Go
package aibridged_test
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/coder/coder/v2/coderd/aibridged"
|
|
)
|
|
|
|
func TestBaseURLHostname(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
baseURL string
|
|
want string
|
|
}{
|
|
{name: "URL", baseURL: "https://openrouter.ai/api/v1", want: "openrouter.ai"},
|
|
{name: "BareHost", baseURL: "openrouter.ai", want: "openrouter.ai"},
|
|
{name: "HostWithPort", baseURL: "https://openrouter.ai:443/api/v1", want: "openrouter.ai"},
|
|
{name: "UppercaseHost", baseURL: "https://API.OpenRouter.AI/v1", want: "api.openrouter.ai"},
|
|
{name: "IPv6", baseURL: "https://[::1]:8080/v1", want: "::1"},
|
|
{name: "Empty", baseURL: "", want: ""},
|
|
{name: "Invalid", baseURL: "://", want: ""},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
require.Equal(t, tt.want, aibridged.BaseURLHostname(tt.baseURL))
|
|
})
|
|
}
|
|
}
|