mirror of
https://github.com/docker/docker-agent.git
synced 2026-11-04 10:02:15 +00:00
feat: add OrcaRouter as a built-in OpenAI-compatible provider
Adds orcarouter as a first-class built-in alias provider, mirroring the existing openrouter entry: - aliases.go: register orcarouter with base_url https://api.orcarouter.ai/v1 and ORCAROUTER_API_KEY - config/auto.go: register orcarouter in cloudProviders (auto-selection) and DefaultModels (orcarouter/auto adaptive router) - openai client: treat orcarouter as an open-model host so consecutive system messages are merged before sending - docs: new providers/orcarouter page, plus rows in the provider overview, and configuration/models provider list - agent-schema.json: document the new alias in the provider description - tests: cover the alias config, auto-selection, default model, and max tokens for orcarouter Rebased onto current main, retaining canonical provider IDs and adding navigation, a runnable example, and hermetic provider wire regressions. Gate the OrcaRouter live-API test behind explicit opt-in so configured credentials alone never trigger paid inference during normal tests. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: JinhaoSong322 <jinhao.song@myflashcloud.com> Signed-off-by: Arnaud Héritier <aheritier@users.noreply.github.com>
This commit is contained in:
1 parent
e0c3a3c3f9
commit
bb8b58db16
14 files changed
+185
-9
No files matched your search
+1
-1
@@ -224,7 +224,7 @@
|
||||
"properties": {
|
||||
"provider": {
|
||||
"type": "string",
|
||||
"description": "The underlying provider type. Defaults to \"openai\" when not set. Supported values: openai, anthropic, google, amazon-bedrock, dmr, and any built-in alias (requesty, openrouter, opper, runcrate, api-route, atlascloud, azure, xai, ollama, mistral, baseten, daoxe, ovhcloud, groq, fireworks-ai, deepseek, cerebras, togetherai, huggingface, moonshotai, vercel, cloudflare-workers-ai, cloudflare-ai-gateway, nvidia, github-copilot, chatgpt, opencode, opencode-go, etc.).",
|
||||
"description": "The underlying provider type. Defaults to \"openai\" when not set. Supported values: openai, anthropic, google, amazon-bedrock, dmr, and any built-in alias (requesty, openrouter, opper, runcrate, api-route, orcarouter, atlascloud, azure, xai, ollama, mistral, baseten, daoxe, ovhcloud, groq, fireworks-ai, deepseek, cerebras, togetherai, huggingface, moonshotai, vercel, cloudflare-workers-ai, cloudflare-ai-gateway, nvidia, github-copilot, chatgpt, opencode, opencode-go, etc.).",
|
||||
"examples": [
|
||||
"openai",
|
||||
"anthropic",
|
||||
|
||||
@@ -18,7 +18,7 @@ models:
|
||||
first_available: [list] # Optional: candidate model refs, tried in order by available credentials.
|
||||
# Mutually exclusive with other model settings.
|
||||
provider: string # Required unless using first_available. One of: openai, anthropic, google, amazon-bedrock,
|
||||
# dmr, mistral, xai, nebius, nvidia, minimax, baseten, daoxe, ovhcloud, groq, fireworks-ai, deepseek, cerebras, togetherai, huggingface, moonshotai, vercel, cloudflare-workers-ai, cloudflare-ai-gateway, requesty, openrouter, opper, runcrate, api-route, atlascloud,
|
||||
# dmr, mistral, xai, nebius, nvidia, minimax, baseten, daoxe, ovhcloud, groq, fireworks-ai, deepseek, cerebras, togetherai, huggingface, moonshotai, vercel, cloudflare-workers-ai, cloudflare-ai-gateway, requesty, openrouter, opper, runcrate, api-route, orcarouter, atlascloud,
|
||||
# azure, ollama, github-copilot, or a named provider defined
|
||||
# under the top-level `providers:` section.
|
||||
model: string # Required: model identifier
|
||||
@@ -60,7 +60,7 @@ models:
|
||||
| Property | Type | Required | Description |
|
||||
| --------------------- | ---------- | -------- | ------------------------------------------------------------------------------------- |
|
||||
| `first_available` | array | ✗ | Candidate model references tried in order; selects the first whose credentials are configured. Mutually exclusive with other model settings. |
|
||||
| `provider` | string | ✓/✗ | Required for regular model definitions; omitted for `first_available` selectors. Provider: `openai`, `anthropic`, `google`, `amazon-bedrock`, `dmr`, `mistral`, `xai`, `nebius`, `nvidia`, `minimax`, `baseten`, `daoxe`, `ovhcloud`, `groq`, `fireworks-ai`, `deepseek`, `cerebras`, `togetherai`, `huggingface`, `moonshotai`, `vercel`, `cloudflare-workers-ai`, `cloudflare-ai-gateway`, `requesty`, `openrouter`, `opper`, `runcrate`, `api-route`, `atlascloud`, `azure`, `ollama`, `github-copilot`, `chatgpt`, `opencode`, `opencode-go`, or any [named provider](../../providers/custom/index.md). |
|
||||
| `provider` | string | ✓/✗ | Required for regular model definitions; omitted for `first_available` selectors. Provider: `openai`, `anthropic`, `google`, `amazon-bedrock`, `dmr`, `mistral`, `xai`, `nebius`, `nvidia`, `minimax`, `baseten`, `daoxe`, `ovhcloud`, `groq`, `fireworks-ai`, `deepseek`, `cerebras`, `togetherai`, `huggingface`, `moonshotai`, `vercel`, `cloudflare-workers-ai`, `cloudflare-ai-gateway`, `requesty`, `openrouter`, `opper`, `runcrate`, `api-route`, `orcarouter`, `atlascloud`, `azure`, `ollama`, `github-copilot`, `chatgpt`, `opencode`, `opencode-go`, or any [named provider](../../providers/custom/index.md). |
|
||||
| `model` | string | ✓/✗ | Required for regular model definitions; omitted for `first_available` selectors. Model name (e.g., `gpt-4o`, `claude-sonnet-4-5`, `gemini-3.5-flash`) |
|
||||
| `description` | string | ✗ | Informational, human-readable summary of the model's purpose or strengths (e.g., "fast and cheap, good for summaries"). Not sent to the model. Can be combined with `first_available` (a selector's description is kept when it resolves). |
|
||||
| `temperature` | float | ✗ | Sampling randomness. Range is provider-dependent — typically `0.0–2.0` (Anthropic caps at `1.0`). `0.0` is deterministic. |
|
||||
@@ -558,7 +558,7 @@ for a complete local-server configuration.
|
||||
## Custom HTTP Headers
|
||||
|
||||
For OpenAI-compatible providers (`openai`, `github-copilot`, `mistral`, `xai`,
|
||||
`nebius`, `nvidia`, `minimax`, `baseten`, `daoxe`, `ovhcloud`, `groq`, `fireworks-ai`, `deepseek`, `cerebras`, `togetherai`, `huggingface`, `moonshotai`, `vercel`, `cloudflare-workers-ai`, `cloudflare-ai-gateway`, `requesty`, `openrouter`, `opper`, `runcrate`, `atlascloud`, `ollama`, and any custom provider using the OpenAI API),
|
||||
`nebius`, `nvidia`, `minimax`, `baseten`, `daoxe`, `ovhcloud`, `groq`, `fireworks-ai`, `deepseek`, `cerebras`, `togetherai`, `huggingface`, `moonshotai`, `vercel`, `cloudflare-workers-ai`, `cloudflare-ai-gateway`, `requesty`, `openrouter`, `opper`, `runcrate`, `orcarouter`, `atlascloud`, `ollama`, and any custom provider using the OpenAI API),
|
||||
`provider_opts.http_headers` adds arbitrary HTTP headers to every outgoing
|
||||
request:
|
||||
|
||||
|
||||
@@ -231,6 +231,8 @@
|
||||
url: /providers/opper/
|
||||
- title: OpenRouter
|
||||
url: /providers/openrouter/
|
||||
- title: OrcaRouter
|
||||
url: /providers/orcarouter/
|
||||
- title: OVHcloud
|
||||
url: /providers/ovhcloud/
|
||||
- title: Runcrate
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
---
|
||||
title: "OrcaRouter"
|
||||
description: "Use OrcaRouter models with Docker Agent."
|
||||
keywords: docker agent, ai agents, model providers, llm, orcarouter
|
||||
weight: 231
|
||||
canonical: https://docs.docker.com/ai/docker-agent/providers/orcarouter/
|
||||
---
|
||||
|
||||
_Use OrcaRouter models with Docker Agent._
|
||||
|
||||
## Overview
|
||||
|
||||
[OrcaRouter](https://www.orcarouter.ai) provides access to models from many providers through an OpenAI-compatible API. Docker Agent includes built-in support for OrcaRouter as an alias provider.
|
||||
|
||||
## Setup
|
||||
|
||||
1. Get an API key from [OrcaRouter](https://www.orcarouter.ai)
|
||||
2. Set the environment variable:
|
||||
|
||||
```bash
|
||||
$ export ORCAROUTER_API_KEY=your-api-key
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Inline Syntax
|
||||
|
||||
Set the provider and model in the agent definition:
|
||||
|
||||
```yaml
|
||||
agents:
|
||||
root:
|
||||
model: orcarouter/orcarouter/auto
|
||||
description: Assistant using OrcaRouter
|
||||
instruction: You are a helpful assistant.
|
||||
```
|
||||
|
||||
`orcarouter/auto` is OrcaRouter's adaptive router. You can also reference a model served by the gateway with its upstream provider prefix, such as `orcarouter/anthropic/claude-sonnet-4.5` or `orcarouter/deepseek/deepseek-v4-pro`. Docker Agent splits only the first slash, so the full upstream model ID is preserved.
|
||||
|
||||
### Named Model
|
||||
|
||||
For more control over parameters:
|
||||
|
||||
```yaml
|
||||
models:
|
||||
orca_router:
|
||||
provider: orcarouter
|
||||
model: orcarouter/auto
|
||||
temperature: 0.7
|
||||
max_tokens: 8192
|
||||
|
||||
agents:
|
||||
root:
|
||||
model: orca_router
|
||||
description: Assistant using OrcaRouter
|
||||
instruction: You are a helpful assistant.
|
||||
```
|
||||
|
||||
## Pricing and Model Metadata
|
||||
|
||||
Docker Agent fetches OrcaRouter model metadata from [models.dev](https://models.dev/), including pricing per 1M input/output tokens, cache pricing when available, context limits, output limits, and modalities. This powers cost tracking and the model picker in the same way as other first-class providers.
|
||||
|
||||
If models.dev is unavailable, Docker Agent falls back to its embedded catalog snapshot.
|
||||
|
||||
## How It Works
|
||||
|
||||
OrcaRouter is implemented as a built-in alias in Docker Agent:
|
||||
|
||||
- **API Type:** OpenAI-compatible (`openai`)
|
||||
- **Base URL:** `https://api.orcarouter.ai/v1`
|
||||
- **Token Variable:** `ORCAROUTER_API_KEY`
|
||||
|
||||
## Example: Code Assistant
|
||||
|
||||
```yaml
|
||||
agents:
|
||||
coder:
|
||||
model: orcarouter/orcarouter/auto
|
||||
description: Code assistant using OrcaRouter
|
||||
instruction: |
|
||||
You are an expert programmer.
|
||||
Write clean, maintainable code.
|
||||
Explain trade-offs when helpful.
|
||||
toolsets:
|
||||
- type: filesystem
|
||||
- type: shell
|
||||
- type: think
|
||||
```
|
||||
@@ -69,6 +69,7 @@ Use this table to find a built-in provider's config key and authentication metho
|
||||
| OpenRouter | `openrouter` | `OPENROUTER_API_KEY` |
|
||||
| Opper | `opper` | `OPPER_API_KEY` |
|
||||
| API Route | `api-route` | `API_ROUTE_API_KEY` |
|
||||
| OrcaRouter | `orcarouter` | `ORCAROUTER_API_KEY` |
|
||||
| Atlas Cloud | [`atlascloud`](../atlascloud/index.md) | `ATLASCLOUD_API_KEY` |
|
||||
| Runcrate | [`runcrate`](../runcrate/index.md) | `RUNCRATE_API_KEY` |
|
||||
| Azure OpenAI | `azure` | `AZURE_API_KEY` + `base_url` (override the key with `token_key`) |
|
||||
|
||||
@@ -211,6 +211,7 @@ remote MCP endpoints.
|
||||
| [`baseten.yaml`](baseten.yaml) | Baseten cloud provider. |
|
||||
| [`daoxe.yaml`](daoxe.yaml) | DaoXE multi-model gateway provider. |
|
||||
| [`opper.yaml`](opper.yaml) | Opper multi-model gateway provider. |
|
||||
| [`orcarouter.yaml`](orcarouter.yaml) | OrcaRouter multi-model gateway provider. |
|
||||
| [`ovhcloud.yaml`](ovhcloud.yaml) | OVHcloud AI Endpoints provider. |
|
||||
| [`runcrate.yaml`](runcrate.yaml) | Runcrate open-model host (`runcrate`). |
|
||||
| [`groq.yaml`](groq.yaml) | Groq fast-inference provider. |
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# yaml-language-server: $schema=../agent-schema.json
|
||||
|
||||
models:
|
||||
orcarouter_model:
|
||||
provider: orcarouter
|
||||
model: orcarouter/auto
|
||||
|
||||
agents:
|
||||
root:
|
||||
model: orcarouter_model
|
||||
description: Assistant using OrcaRouter
|
||||
instruction: |
|
||||
You are a helpful assistant.
|
||||
toolsets:
|
||||
- type: filesystem
|
||||
- type: shell
|
||||
- type: think
|
||||
@@ -63,6 +63,7 @@ var cloudProviders = []providerConfig{
|
||||
{"mistral", []string{"MISTRAL_API_KEY"}, "MISTRAL_API_KEY", "MISTRAL_API_KEY"},
|
||||
{"openrouter", []string{"OPENROUTER_API_KEY"}, "OPENROUTER_API_KEY", "OPENROUTER_API_KEY"},
|
||||
{"api-route", []string{"API_ROUTE_API_KEY"}, "API_ROUTE_API_KEY", "API_ROUTE_API_KEY"},
|
||||
{"orcarouter", []string{"ORCAROUTER_API_KEY"}, "ORCAROUTER_API_KEY", "ORCAROUTER_API_KEY"},
|
||||
{"atlascloud", []string{"ATLASCLOUD_API_KEY"}, "ATLASCLOUD_API_KEY", "ATLASCLOUD_API_KEY"},
|
||||
{"opper", []string{"OPPER_API_KEY"}, "OPPER_API_KEY", "OPPER_API_KEY"},
|
||||
{"runcrate", []string{"RUNCRATE_API_KEY"}, "RUNCRATE_API_KEY", "RUNCRATE_API_KEY"},
|
||||
@@ -164,6 +165,7 @@ var DefaultModels = map[string]string{
|
||||
"mistral": "mistral-small-latest",
|
||||
"openrouter": "meta-llama/llama-4-maverick",
|
||||
"api-route": "gpt-6.1-sol",
|
||||
"orcarouter": "orcarouter/auto",
|
||||
"atlascloud": "qwen/qwen3.8-max",
|
||||
"opper": "claude-sonnet-4-6",
|
||||
"runcrate": "deepseek-ai/DeepSeek-V4.1-Flash",
|
||||
|
||||
+23
-2
@@ -81,6 +81,13 @@ func TestAvailableProviders_NoGateway(t *testing.T) {
|
||||
},
|
||||
expectedProvider: "api-route",
|
||||
},
|
||||
{
|
||||
name: "orcarouter api key present",
|
||||
envVars: map[string]string{
|
||||
"ORCAROUTER_API_KEY": "test-key",
|
||||
},
|
||||
expectedProvider: "orcarouter",
|
||||
},
|
||||
{
|
||||
name: "atlascloud api key present",
|
||||
envVars: map[string]string{
|
||||
@@ -368,6 +375,15 @@ func TestAutoModelConfig(t *testing.T) {
|
||||
expectedModel: "deepseek-ai/DeepSeek-V4.1-Flash",
|
||||
expectedMaxTokens: 32000,
|
||||
},
|
||||
{
|
||||
name: "orcarouter provider",
|
||||
envVars: map[string]string{
|
||||
"ORCAROUTER_API_KEY": "test-key",
|
||||
},
|
||||
expectedProvider: "orcarouter",
|
||||
expectedModel: "orcarouter/auto",
|
||||
expectedMaxTokens: 32000,
|
||||
},
|
||||
{
|
||||
name: "baseten provider",
|
||||
envVars: map[string]string{
|
||||
@@ -549,7 +565,7 @@ func TestDefaultModels(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Test that DefaultModels map has all expected providers
|
||||
expectedProviders := []string{"openai", "anthropic", "google", "dmr", "mistral", "openrouter", "atlascloud", "baseten", "ovhcloud", "groq", "fireworks-ai", "deepseek", "cerebras", "togetherai", "huggingface", "moonshotai", "vercel", "amazon-bedrock", "opencode", "opencode-go", "github-copilot"}
|
||||
expectedProviders := []string{"openai", "anthropic", "google", "dmr", "mistral", "openrouter", "api-route", "orcarouter", "atlascloud", "baseten", "ovhcloud", "groq", "fireworks-ai", "deepseek", "cerebras", "togetherai", "huggingface", "moonshotai", "vercel", "amazon-bedrock", "opencode", "opencode-go", "github-copilot"}
|
||||
|
||||
for _, provider := range expectedProviders {
|
||||
t.Run(provider, func(t *testing.T) {
|
||||
@@ -567,6 +583,7 @@ func TestDefaultModels(t *testing.T) {
|
||||
assert.Equal(t, "ai/qwen3:latest", DefaultModels["dmr"])
|
||||
assert.Equal(t, "mistral-small-latest", DefaultModels["mistral"])
|
||||
assert.Equal(t, "meta-llama/llama-4-maverick", DefaultModels["openrouter"])
|
||||
assert.Equal(t, "orcarouter/auto", DefaultModels["orcarouter"])
|
||||
assert.Equal(t, "qwen/qwen3.8-max", DefaultModels["atlascloud"])
|
||||
assert.Equal(t, "deepseek-ai/DeepSeek-V4-Pro-0813", DefaultModels["baseten"])
|
||||
assert.Equal(t, "Qwen3.5-397B-A17B", DefaultModels["ovhcloud"])
|
||||
@@ -587,7 +604,7 @@ func TestAutoModelConfig_IntegrationWithDefaultModels(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Verify that AutoModelConfig always returns a model from DefaultModels
|
||||
providers := []string{"openai", "anthropic", "google", "mistral", "openrouter", "atlascloud", "baseten", "ovhcloud", "groq", "fireworks-ai", "deepseek", "cerebras", "togetherai", "huggingface", "moonshotai", "vercel", "opencode", "github-copilot"}
|
||||
providers := []string{"openai", "anthropic", "google", "mistral", "openrouter", "api-route", "orcarouter", "atlascloud", "baseten", "ovhcloud", "groq", "fireworks-ai", "deepseek", "cerebras", "togetherai", "huggingface", "moonshotai", "vercel", "opencode", "github-copilot"}
|
||||
|
||||
for _, provider := range providers {
|
||||
t.Run(provider, func(t *testing.T) {
|
||||
@@ -609,6 +626,10 @@ func TestAutoModelConfig_IntegrationWithDefaultModels(t *testing.T) {
|
||||
envVars["MISTRAL_API_KEY"] = "test-key"
|
||||
case "openrouter":
|
||||
envVars["OPENROUTER_API_KEY"] = "test-key"
|
||||
case "api-route":
|
||||
envVars["API_ROUTE_API_KEY"] = "test-key"
|
||||
case "orcarouter":
|
||||
envVars["ORCAROUTER_API_KEY"] = "test-key"
|
||||
case "atlascloud":
|
||||
envVars["ATLASCLOUD_API_KEY"] = "test-key"
|
||||
case "baseten":
|
||||
|
||||
@@ -75,6 +75,11 @@ var Aliases = map[string]Alias{
|
||||
BaseURL: "https://api.runcrate.ai/v1",
|
||||
TokenEnvVar: "RUNCRATE_API_KEY",
|
||||
},
|
||||
"orcarouter": {
|
||||
APIType: "openai",
|
||||
BaseURL: "https://api.orcarouter.ai/v1",
|
||||
TokenEnvVar: "ORCAROUTER_API_KEY",
|
||||
},
|
||||
"atlascloud": {
|
||||
APIType: "openai",
|
||||
BaseURL: "https://api.atlascloud.ai/v1",
|
||||
|
||||
@@ -40,6 +40,7 @@ func TestCatalogAliases(t *testing.T) {
|
||||
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"},
|
||||
"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"},
|
||||
|
||||
@@ -283,6 +283,7 @@ var openModelHostProviders = map[string]bool{
|
||||
"openrouter": true,
|
||||
"opper": true,
|
||||
"runcrate": true,
|
||||
"orcarouter": true,
|
||||
"nebius": true,
|
||||
"nvidia": true,
|
||||
"cerebras": true,
|
||||
|
||||
@@ -35,6 +35,16 @@ func TestChatCompletions_MergesConsecutiveSystemMessages(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestOrcaRouter_MergesConsecutiveSystemMessages(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assertMergesConsecutiveMessages(t, &latest.ModelConfig{
|
||||
Provider: "orcarouter",
|
||||
Model: "orcarouter/auto",
|
||||
TokenKey: "MY_TOKEN",
|
||||
})
|
||||
}
|
||||
|
||||
func TestDaoxe_MergesConsecutiveSystemMessages(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -64,6 +64,15 @@ var openAIAliasProviders = []openAIAliasProvider{
|
||||
mergesSystemMessages: true,
|
||||
liveOptInEnvVar: "DOCKER_AGENT_RUN_API_ROUTE_LIVE_TEST",
|
||||
},
|
||||
{
|
||||
provider: "orcarouter",
|
||||
envVar: "ORCAROUTER_API_KEY",
|
||||
testKey: "test-orcarouter-key",
|
||||
model: "orcarouter/auto",
|
||||
greeting: "Hello from OrcaRouter",
|
||||
mergesSystemMessages: true,
|
||||
liveOptInEnvVar: "DOCKER_AGENT_RUN_ORCAROUTER_LIVE_TEST",
|
||||
},
|
||||
{
|
||||
provider: "atlascloud",
|
||||
envVar: "ATLASCLOUD_API_KEY",
|
||||
@@ -319,7 +328,8 @@ func TestOpenAIAliasProvider_EndToEndRequest(t *testing.T) {
|
||||
//
|
||||
// DEEPSEEK_API_KEY=sk-... go test -run TestOpenAIAliasProvider_LiveAPI ./pkg/model/provider/
|
||||
//
|
||||
// API Route additionally requires DOCKER_AGENT_RUN_API_ROUTE_LIVE_TEST=1.
|
||||
// API Route additionally requires DOCKER_AGENT_RUN_API_ROUTE_LIVE_TEST=1;
|
||||
// OrcaRouter requires DOCKER_AGENT_RUN_ORCAROUTER_LIVE_TEST=1.
|
||||
func TestOpenAIAliasProvider_LiveAPI(t *testing.T) {
|
||||
for _, p := range openAIAliasProviders {
|
||||
t.Run(p.provider, func(t *testing.T) {
|
||||
@@ -329,17 +339,31 @@ func TestOpenAIAliasProvider_LiveAPI(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAPIRouteLiveAPIRequiresExplicitOptIn(t *testing.T) {
|
||||
assertLiveAPIRequiresExplicitOptIn(t, "api-route")
|
||||
}
|
||||
|
||||
func TestOrcaRouterLiveAPIRequiresExplicitOptIn(t *testing.T) {
|
||||
assertLiveAPIRequiresExplicitOptIn(t, "orcarouter")
|
||||
}
|
||||
|
||||
func assertLiveAPIRequiresExplicitOptIn(t *testing.T, providerName string) {
|
||||
t.Helper()
|
||||
var p openAIAliasProvider
|
||||
for _, candidate := range openAIAliasProviders {
|
||||
if candidate.provider == "api-route" {
|
||||
if candidate.provider == providerName {
|
||||
p = candidate
|
||||
break
|
||||
}
|
||||
}
|
||||
require.NotEmpty(t, p.liveOptInEnvVar)
|
||||
var requests atomic.Int32
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
var mu sync.Mutex
|
||||
var hosts []string
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
requests.Add(1)
|
||||
mu.Lock()
|
||||
hosts = append(hosts, r.Host)
|
||||
mu.Unlock()
|
||||
w.Header().Set("Content-Type", "text/event-stream")
|
||||
writeSSEChunk(w, map[string]any{
|
||||
"id": "chatcmpl-test", "object": "chat.completion.chunk", "model": p.model,
|
||||
@@ -364,6 +388,9 @@ func TestAPIRouteLiveAPIRequiresExplicitOptIn(t *testing.T) {
|
||||
runOpenAIAliasProviderLiveAPI(t, p, server.URL)
|
||||
})
|
||||
require.Equal(t, int32(1), requests.Load())
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
require.Equal(t, []string{strings.TrimPrefix(server.URL, "http://")}, hosts)
|
||||
}
|
||||
|
||||
func runOpenAIAliasProviderLiveAPI(t *testing.T, p openAIAliasProvider, baseURL string) {
|
||||
|
||||
Reference in new issue
Block a user