mirror of
https://github.com/coder/coder.git
synced 2026-10-31 02:43:21 +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
12 KiB
Generated
12 KiB
Generated
title
| title |
|---|
| AI Providers |
List AI providers
Code samples
# Example request using curl
curl -X GET http://coder-server:8080/api/v2/ai/providers \
-H 'Accept: application/json' \
-H 'Coder-Session-Token: API_KEY'
GET /api/v2/ai/providers
Example responses
200 Response
[
{
"api_keys": [
{
"created_at": "2019-08-24T14:15:22Z",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"masked": "string"
}
],
"base_url": "string",
"created_at": "2019-08-24T14:15:22Z",
"display_name": "string",
"enabled": true,
"icon": "string",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"name": "string",
"settings": {},
"status": {
"warnings": [
"string"
]
},
"type": "openai",
"updated_at": "2019-08-24T14:15:22Z"
}
]
Responses
| Status | Meaning | Description | Schema |
|---|---|---|---|
| 200 | OK | OK | array of codersdk.AIProvider |
Response Schema
Status Code 200
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
[array item] |
array | false | ||
» api_keys |
array | false | ||
»» created_at |
string(date-time) | false | ||
»» id |
string(uuid) | false | ||
»» masked |
string | false | ||
» base_url |
string | false | ||
» created_at |
string(date-time) | false | ||
» display_name |
string | false | ||
» enabled |
boolean | false | ||
» icon |
string | false | ||
» id |
string(uuid) | false | ||
» name |
string | false | ||
» settings |
codersdk.AIProviderSettings | false | ||
» status |
codersdk.AIProviderStatus | false | Status carries runtime routing status; nil when empty. | |
»» warnings |
array | false | ||
» type |
codersdk.AIProviderType | false | ||
» updated_at |
string(date-time) | false |
Enumerated Values
| Property | Value(s) |
|---|---|
type |
anthropic, azure, bedrock, copilot, google, openai, openai-compat, openrouter, vercel |
To perform this operation, you must be authenticated. Learn more.
Create an AI provider
Code samples
# Example request using curl
curl -X POST http://coder-server:8080/api/v2/ai/providers \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Coder-Session-Token: API_KEY'
POST /api/v2/ai/providers
Body parameter
{
"api_keys": [
"string"
],
"base_url": "string",
"display_name": "string",
"enabled": true,
"icon": "string",
"name": "string",
"settings": {},
"type": "openai"
}
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
body |
body | codersdk.CreateAIProviderRequest | true | Create AI provider request |
Example responses
201 Response
{
"api_keys": [
{
"created_at": "2019-08-24T14:15:22Z",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"masked": "string"
}
],
"base_url": "string",
"created_at": "2019-08-24T14:15:22Z",
"display_name": "string",
"enabled": true,
"icon": "string",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"name": "string",
"settings": {},
"status": {
"warnings": [
"string"
]
},
"type": "openai",
"updated_at": "2019-08-24T14:15:22Z"
}
Responses
| Status | Meaning | Description | Schema |
|---|---|---|---|
| 201 | Created | Created | codersdk.AIProvider |
To perform this operation, you must be authenticated. Learn more.
Get an AI provider
Code samples
# Example request using curl
curl -X GET http://coder-server:8080/api/v2/ai/providers/{idOrName} \
-H 'Accept: application/json' \
-H 'Coder-Session-Token: API_KEY'
GET /api/v2/ai/providers/{idOrName}
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
idOrName |
path | string | true | Provider ID or name |
Example responses
200 Response
{
"api_keys": [
{
"created_at": "2019-08-24T14:15:22Z",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"masked": "string"
}
],
"base_url": "string",
"created_at": "2019-08-24T14:15:22Z",
"display_name": "string",
"enabled": true,
"icon": "string",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"name": "string",
"settings": {},
"status": {
"warnings": [
"string"
]
},
"type": "openai",
"updated_at": "2019-08-24T14:15:22Z"
}
Responses
| Status | Meaning | Description | Schema |
|---|---|---|---|
| 200 | OK | OK | codersdk.AIProvider |
To perform this operation, you must be authenticated. Learn more.
Delete an AI provider
Code samples
# Example request using curl
curl -X DELETE http://coder-server:8080/api/v2/ai/providers/{idOrName} \
-H 'Coder-Session-Token: API_KEY'
DELETE /api/v2/ai/providers/{idOrName}
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
idOrName |
path | string | true | Provider ID or name |
Responses
| Status | Meaning | Description | Schema |
|---|---|---|---|
| 204 | No Content | No Content |
To perform this operation, you must be authenticated. Learn more.
Update an AI provider
Code samples
# Example request using curl
curl -X PATCH http://coder-server:8080/api/v2/ai/providers/{idOrName} \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Coder-Session-Token: API_KEY'
PATCH /api/v2/ai/providers/{idOrName}
Body parameter
{
"api_keys": [
{
"api_key": "string",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08"
}
],
"base_url": "string",
"display_name": "string",
"enabled": true,
"icon": "string",
"settings": {}
}
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
idOrName |
path | string | true | Provider ID or name |
body |
body | codersdk.UpdateAIProviderRequest | true | Update AI provider request |
Example responses
200 Response
{
"api_keys": [
{
"created_at": "2019-08-24T14:15:22Z",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"masked": "string"
}
],
"base_url": "string",
"created_at": "2019-08-24T14:15:22Z",
"display_name": "string",
"enabled": true,
"icon": "string",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"name": "string",
"settings": {},
"status": {
"warnings": [
"string"
]
},
"type": "openai",
"updated_at": "2019-08-24T14:15:22Z"
}
Responses
| Status | Meaning | Description | Schema |
|---|---|---|---|
| 200 | OK | OK | codersdk.AIProvider |
To perform this operation, you must be authenticated. Learn more.