feat(providers): an OpenAI-compatible preset for any vLLM, LM Studio or gateway URL

Step 8 of the provider refactor plan, a new provider made of one record.
'custom' runs on @ai-sdk/openai-compatible with <think> extraction, merges
system messages (open-source chat templates reject several), has no default
URL (so a key alone does not count as connected, and the chat endpoint is
shown under the URL field) and lists models from its /models. Server side:
CUSTOM_API_KEY and CUSTOM_BASE_URL, ADMIN_CUSTOM_* in the admin panel.
env.example and the three provider docs gained a section; the picker count
in the e2e test is 25 and a new e2e test walks through the preset. The
behavior snapshot gained the new provider's 146 rows; its two 'No AI provider
configured' rows list CUSTOM_API_KEY now.
This commit is contained in:
dayuan.jiang
2026-10-12 21:17:51 +09:00
parent 717f7e9445
commit b2cadfa5bf
9 changed files with 246 additions and 9 deletions
+12
View File
@@ -323,6 +323,18 @@ AI_MODEL=mimo-v2.5-pro
MIMO_BASE_URL=https://token-plan-cn.xiaomimimo.com/v1 MIMO_BASE_URL=https://token-plan-cn.xiaomimimo.com/v1
``` ```
### 任何 OpenAI 兼容接口(custom)
上面没有列出的服务(vLLM、LM Studio、LiteLLM、API 网关),在设置里选择 **OpenAI-compatible**,填入它的地址和 key;服务端不校验 key 时填任意非空内容即可。多条 system 消息会合并成一条(开源模型的对话模板通常要求这样),`<think>` 里的推理内容会从回复里分离出来。
```bash
CUSTOM_API_KEY=your_api_key
CUSTOM_BASE_URL=http://127.0.0.1:8000/v1
AI_MODEL=your_model_id
```
---
## 自动检测 ## 自动检测
如果您只配置了**一个**提供商的 API 密钥,系统将自动检测并使用该提供商。无需设置 `AI_PROVIDER`。 如果您只配置了**一个**提供商的 API 密钥,系统将自动检测并使用该提供商。无需设置 `AI_PROVIDER`。
+12
View File
@@ -338,6 +338,18 @@ Optional custom endpoint (Token Plan subscribers should set their dedicated Base
MIMO_BASE_URL=https://token-plan-cn.xiaomimimo.com/v1 MIMO_BASE_URL=https://token-plan-cn.xiaomimimo.com/v1
``` ```
### Any OpenAI-compatible API (custom)
For a server not listed above (vLLM, LM Studio, LiteLLM, an API gateway), pick **OpenAI-compatible** in the settings and enter its base URL and a key; a server that ignores keys takes any non-empty value. Several system messages are merged into one, as open-source chat templates expect, and `<think>` reasoning is split from the reply.
```bash
CUSTOM_API_KEY=your_api_key
CUSTOM_BASE_URL=http://127.0.0.1:8000/v1
AI_MODEL=your_model_id
```
---
## Auto-Detection ## Auto-Detection
If you only configure **one** provider's API key, the system will automatically detect and use that provider. No need to set `AI_PROVIDER`. If you only configure **one** provider's API key, the system will automatically detect and use that provider. No need to set `AI_PROVIDER`.
+12
View File
@@ -323,6 +323,18 @@ AI_MODEL=mimo-v2.5-pro
MIMO_BASE_URL=https://token-plan-cn.xiaomimimo.com/v1 MIMO_BASE_URL=https://token-plan-cn.xiaomimimo.com/v1
``` ```
### 任意の OpenAI 互換 API(custom)
上に挙がっていないサーバー(vLLM、LM Studio、LiteLLM、API ゲートウェイ)は、設定で **OpenAI-compatible** を選び、そのベース URL とキーを入力します。キーを確認しないサーバーには空でない任意の値を入れてください。複数の system メッセージは 1 つにまとめられ(オープンソースモデルのチャットテンプレートはそれを前提にしています)、`<think>` 内の推論は返答から分離されます。
```bash
CUSTOM_API_KEY=your_api_key
CUSTOM_BASE_URL=http://127.0.0.1:8000/v1
AI_MODEL=your_model_id
```
---
## 自動検出 ## 自動検出
**1つ**のプロバイダーの API キーのみを設定した場合、システムはそのプロバイダーを自動的に検出して使用します。`AI_PROVIDER` を設定する必要はありません。 **1つ**のプロバイダーの API キーのみを設定した場合、システムはそのプロバイダーを自動的に検出して使用します。`AI_PROVIDER` を設定する必要はありません。
+5 -1
View File
@@ -1,6 +1,6 @@
# AI Provider Configuration # AI Provider Configuration
# AI_PROVIDER: Which provider to use # AI_PROVIDER: Which provider to use
# Options: bedrock, openai, anthropic, google, vertexai, azure, ollama, openrouter, aihubmix, deepseek, siliconflow, sglang, gateway, edgeone, doubao, modelscope, glm, qwen, qiniu, kimi, minimax, novita, mimo, atlascloud # Options: bedrock, openai, anthropic, google, vertexai, azure, ollama, openrouter, aihubmix, deepseek, siliconflow, sglang, gateway, edgeone, doubao, modelscope, glm, qwen, qiniu, kimi, minimax, novita, mimo, atlascloud, custom
# Default: bedrock # Default: bedrock
AI_PROVIDER=bedrock AI_PROVIDER=bedrock
@@ -94,6 +94,10 @@ AI_MODEL=global.anthropic.claude-sonnet-4-5-20250929-v1:0
# SGLANG_API_KEY=your-sglang-api-key # SGLANG_API_KEY=your-sglang-api-key
# SGLANG_BASE_URL=http://127.0.0.1:8000/v1 # Your SGLang endpoint # SGLANG_BASE_URL=http://127.0.0.1:8000/v1 # Your SGLang endpoint
# Any other OpenAI-compatible API (vLLM, LM Studio, LiteLLM, a gateway)
# CUSTOM_API_KEY=your-api-key # Any non-empty value if the server ignores keys
# CUSTOM_BASE_URL=http://127.0.0.1:8000/v1 # Required: the API's base URL
# ModelScope Configuration # ModelScope Configuration
# MODELSCOPE_API_KEY=ms-... # MODELSCOPE_API_KEY=ms-...
# MODELSCOPE_BASE_URL=https://api-inference.modelscope.cn/v1 # Optional: Custom endpoint # MODELSCOPE_BASE_URL=https://api-inference.modelscope.cn/v1 # Optional: Custom endpoint
+21
View File
@@ -516,6 +516,27 @@ const defs = {
"zh-Hant": "填服務位址", "zh-Hant": "填服務位址",
}, },
}, },
// Any OpenAI-compatible API: vLLM, LM Studio, LiteLLM, a gateway. The
// user enters the URL; a server that ignores keys takes any non-empty
// one (a custom URL always needs a key, see getAIModel's guard)
custom: {
label: "OpenAI-compatible",
sdk: "openai-compatible",
credential: "apiKey",
group: "selfHosted",
envPrefix: "CUSTOM",
modelList: "openai",
icon: "server",
optionsOpen: true,
// Open-source chat templates reject several system messages
singleSystem: true,
note: {
en: "Any OpenAI-compatible URL and key",
zh: "任何 OpenAI 兼容地址和 key",
ja: "OpenAI 互換の URL とキー",
"zh-Hant": "任何 OpenAI 相容位址和 key",
},
},
} satisfies Record<string, ProviderDef> } satisfies Record<string, ProviderDef>
export type ProviderName = keyof typeof defs export type ProviderName = keyof typeof defs
+29 -1
View File
@@ -127,7 +127,7 @@ test("the provider picker searches, marks added ones, and Enter picks", async ({
dialog.getByTestId("pick-provider-anthropic"), dialog.getByTestId("pick-provider-anthropic"),
).not.toContainText("Added") ).not.toContainText("Added")
await expect(dialog.locator('[data-testid^="pick-provider-"]')).toHaveCount( await expect(dialog.locator('[data-testid^="pick-provider-"]')).toHaveCount(
24, 25,
) )
await search.fill("zzz") await search.fill("zzz")
@@ -596,6 +596,34 @@ test("Azure needs its resource URL before it counts as connected", async ({
await expect(dialog.getByTestId("test-models")).toBeEnabled() await expect(dialog.getByTestId("test-models")).toBeEnabled()
}) })
test("the OpenAI-compatible preset takes any URL and shows where requests go", async ({
page,
}) => {
await openApp(page)
await openSettings(page)
const dialog = settingsDialog(page)
// Not among the popular ones: it is in the full picker
await dialog.getByRole("button", { name: /^See all \d+ providers/ }).click()
await dialog.getByTestId("pick-provider-custom").click()
await expect(
dialog.getByRole("heading", { name: "OpenAI-compatible", exact: true }),
).toBeVisible()
// No URL yet: a key alone does not count as connected
await dialog.locator("#api-key").fill("any")
await expect(dialog.getByTestId("step-1-todo")).toBeVisible()
await expect(
dialog.getByText("Fill in the API key and the base URL first."),
).toBeVisible()
// Its options start open; the URL field shows the chat endpoint
await dialog.locator("#base-url").fill("http://127.0.0.1:1234/v1")
await expect(
dialog.getByText(
"Requests go to http://127.0.0.1:1234/v1/chat/completions",
),
).toBeVisible()
await expect(dialog.getByTestId("step-1-done")).toBeVisible()
})
test("a model id typed for one provider does not follow to the next", async ({ test("a model id typed for one provider does not follow to the next", async ({
page, page,
}) => { }) => {
+148 -2
View File
@@ -3454,6 +3454,150 @@
{"provider":"atlascloud","modelId":"qwen/qwen3.5-flash","shape":"keyAndPrivateUrl","env":"adminAws","getAIModel":{"provider":"atlascloud","modelId":"qwen/qwen3.5-flash","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"qwen/qwen3.5-flash","options":{"name":"atlascloud","apiKey":"client-key","baseURL":"http://10.0.0.5:8000/v1","fetch":"guarded","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":false,"usesServerEndpoint":true,"chatRequestUrl":"http://10.0.0.5:8000/v1/chat/completions"} {"provider":"atlascloud","modelId":"qwen/qwen3.5-flash","shape":"keyAndPrivateUrl","env":"adminAws","getAIModel":{"provider":"atlascloud","modelId":"qwen/qwen3.5-flash","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"qwen/qwen3.5-flash","options":{"name":"atlascloud","apiKey":"client-key","baseURL":"http://10.0.0.5:8000/v1","fetch":"guarded","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":false,"usesServerEndpoint":true,"chatRequestUrl":"http://10.0.0.5:8000/v1/chat/completions"}
{"provider":"atlascloud","modelId":"qwen/qwen3.5-flash","shape":"keyAndPrivateUrl","env":"desktop","getAIModel":{"provider":"atlascloud","modelId":"qwen/qwen3.5-flash","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"qwen/qwen3.5-flash","options":{"name":"atlascloud","apiKey":"client-key","baseURL":"http://10.0.0.5:8000/v1","fetch":"guarded","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":"http://10.0.0.5:8000/v1/chat/completions"} {"provider":"atlascloud","modelId":"qwen/qwen3.5-flash","shape":"keyAndPrivateUrl","env":"desktop","getAIModel":{"provider":"atlascloud","modelId":"qwen/qwen3.5-flash","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"qwen/qwen3.5-flash","options":{"name":"atlascloud","apiKey":"client-key","baseURL":"http://10.0.0.5:8000/v1","fetch":"guarded","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":"http://10.0.0.5:8000/v1/chat/completions"}
{"provider":"atlascloud","modelId":"qwen/qwen3.5-flash","shape":"keyAndPrivateUrl","env":"everything","getAIModel":{"provider":"atlascloud","modelId":"qwen/qwen3.5-flash","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"qwen/qwen3.5-flash","options":{"name":"atlascloud","apiKey":"client-key","baseURL":"http://10.0.0.5:8000/v1","fetch":"guarded","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":false,"usesServerEndpoint":true,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":"http://10.0.0.5:8000/v1/chat/completions"} {"provider":"atlascloud","modelId":"qwen/qwen3.5-flash","shape":"keyAndPrivateUrl","env":"everything","getAIModel":{"provider":"atlascloud","modelId":"qwen/qwen3.5-flash","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"qwen/qwen3.5-flash","options":{"name":"atlascloud","apiKey":"client-key","baseURL":"http://10.0.0.5:8000/v1","fetch":"guarded","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":false,"usesServerEndpoint":true,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":"http://10.0.0.5:8000/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"none","env":"empty","getAIModel":{"error":"CUSTOM_API_KEY environment variable is required for custom provider. Please set it in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"none","env":"serverKey","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"none","env":"serverKeyAndUrl","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"server-key","baseURL":"https://server.example/v1","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":true,"usesServerEndpoint":false,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"none","env":"ollamaKey","getAIModel":{"error":"CUSTOM_API_KEY environment variable is required for custom provider. Please set it in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"none","env":"azureResource","getAIModel":{"error":"CUSTOM_API_KEY environment variable is required for custom provider. Please set it in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"none","env":"anthropicToken","getAIModel":{"error":"CUSTOM_API_KEY environment variable is required for custom provider. Please set it in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"none","env":"adminAws","getAIModel":{"error":"CUSTOM_API_KEY environment variable is required for custom provider. Please set it in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"none","env":"desktop","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"none","env":"everything","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"server-key","baseURL":"https://server.example/v1","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":true,"usesServerEndpoint":false,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"key","env":"empty","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"key","env":"serverKey","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"key","env":"serverKeyAndUrl","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":false,"usesServerEndpoint":false,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"key","env":"ollamaKey","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"key","env":"azureResource","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"key","env":"anthropicToken","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"key","env":"adminAws","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"key","env":"desktop","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"key","env":"everything","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":false,"usesServerEndpoint":false,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"url","env":"empty","getAIModel":{"error":"API key is required when using a custom base URL. Please provide your own API key in Settings."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"url","env":"serverKey","getAIModel":{"error":"API key is required when using a custom base URL. Please provide your own API key in Settings."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"url","env":"serverKeyAndUrl","getAIModel":{"error":"API key is required when using a custom base URL. Please provide your own API key in Settings."},"usesServerCredentials":true,"usesServerEndpoint":false,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"url","env":"ollamaKey","getAIModel":{"error":"API key is required when using a custom base URL. Please provide your own API key in Settings."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"url","env":"azureResource","getAIModel":{"error":"API key is required when using a custom base URL. Please provide your own API key in Settings."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"url","env":"anthropicToken","getAIModel":{"error":"API key is required when using a custom base URL. Please provide your own API key in Settings."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"url","env":"adminAws","getAIModel":{"error":"API key is required when using a custom base URL. Please provide your own API key in Settings."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"url","env":"desktop","getAIModel":{"error":"API key is required when using a custom base URL. Please provide your own API key in Settings."},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"url","env":"everything","getAIModel":{"error":"API key is required when using a custom base URL. Please provide your own API key in Settings."},"usesServerCredentials":true,"usesServerEndpoint":false,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"keyAndUrl","env":"empty","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"client-key","baseURL":"https://u.example/v1","fetch":"guarded","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"keyAndUrl","env":"serverKey","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"client-key","baseURL":"https://u.example/v1","fetch":"guarded","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"keyAndUrl","env":"serverKeyAndUrl","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"client-key","baseURL":"https://u.example/v1","fetch":"guarded","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":false,"usesServerEndpoint":false,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"keyAndUrl","env":"ollamaKey","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"client-key","baseURL":"https://u.example/v1","fetch":"guarded","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"keyAndUrl","env":"azureResource","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"client-key","baseURL":"https://u.example/v1","fetch":"guarded","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"keyAndUrl","env":"anthropicToken","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"client-key","baseURL":"https://u.example/v1","fetch":"guarded","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"keyAndUrl","env":"adminAws","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"client-key","baseURL":"https://u.example/v1","fetch":"guarded","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"keyAndUrl","env":"desktop","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"client-key","baseURL":"https://u.example/v1","fetch":"guarded","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"keyAndUrl","env":"everything","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"client-key","baseURL":"https://u.example/v1","fetch":"guarded","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":false,"usesServerEndpoint":false,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"keyAndSlash","env":"empty","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyAndSlash","env":"serverKey","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyAndSlash","env":"serverKeyAndUrl","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":false,"usesServerEndpoint":false,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyAndSlash","env":"ollamaKey","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyAndSlash","env":"azureResource","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyAndSlash","env":"anthropicToken","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyAndSlash","env":"adminAws","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyAndSlash","env":"desktop","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyAndSlash","env":"everything","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":false,"usesServerEndpoint":false,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyAndPastedPath","env":"empty","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"client-key","baseURL":"https://u.example/v1/chat/completions","fetch":"guarded","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":"https://u.example/v1/chat/completions/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"keyAndPastedPath","env":"serverKey","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"client-key","baseURL":"https://u.example/v1/chat/completions","fetch":"guarded","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":"https://u.example/v1/chat/completions/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"keyAndPastedPath","env":"serverKeyAndUrl","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"client-key","baseURL":"https://u.example/v1/chat/completions","fetch":"guarded","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":false,"usesServerEndpoint":false,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":"https://u.example/v1/chat/completions/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"keyAndPastedPath","env":"ollamaKey","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"client-key","baseURL":"https://u.example/v1/chat/completions","fetch":"guarded","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":"https://u.example/v1/chat/completions/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"keyAndPastedPath","env":"azureResource","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"client-key","baseURL":"https://u.example/v1/chat/completions","fetch":"guarded","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":"https://u.example/v1/chat/completions/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"keyAndPastedPath","env":"anthropicToken","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"client-key","baseURL":"https://u.example/v1/chat/completions","fetch":"guarded","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":"https://u.example/v1/chat/completions/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"keyAndPastedPath","env":"adminAws","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"client-key","baseURL":"https://u.example/v1/chat/completions","fetch":"guarded","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":"https://u.example/v1/chat/completions/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"keyAndPastedPath","env":"desktop","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"client-key","baseURL":"https://u.example/v1/chat/completions","fetch":"guarded","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":"https://u.example/v1/chat/completions/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"keyAndPastedPath","env":"everything","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"client-key","baseURL":"https://u.example/v1/chat/completions","fetch":"guarded","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":false,"usesServerEndpoint":false,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":"https://u.example/v1/chat/completions/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"keyEnv","env":"empty","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnv","env":"serverKey","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnv","env":"serverKeyAndUrl","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"custom-env-key","baseURL":"https://server.example/v1","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":true,"usesServerEndpoint":false,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnv","env":"ollamaKey","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnv","env":"azureResource","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnv","env":"anthropicToken","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnv","env":"adminAws","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnv","env":"desktop","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnv","env":"everything","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"custom-env-key","baseURL":"https://server.example/v1","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":true,"usesServerEndpoint":false,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvEmpty","env":"empty","getAIModel":{"error":"CUSTOM_KEY_EMPTY environment variable is required for custom provider. Please set it in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvEmpty","env":"serverKey","getAIModel":{"error":"CUSTOM_KEY_EMPTY environment variable is required for custom provider. Please set it in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvEmpty","env":"serverKeyAndUrl","getAIModel":{"error":"CUSTOM_KEY_EMPTY environment variable is required for custom provider. Please set it in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvEmpty","env":"ollamaKey","getAIModel":{"error":"CUSTOM_KEY_EMPTY environment variable is required for custom provider. Please set it in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvEmpty","env":"azureResource","getAIModel":{"error":"CUSTOM_KEY_EMPTY environment variable is required for custom provider. Please set it in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvEmpty","env":"anthropicToken","getAIModel":{"error":"CUSTOM_KEY_EMPTY environment variable is required for custom provider. Please set it in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvEmpty","env":"adminAws","getAIModel":{"error":"CUSTOM_KEY_EMPTY environment variable is required for custom provider. Please set it in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvEmpty","env":"desktop","getAIModel":{"error":"CUSTOM_KEY_EMPTY environment variable is required for custom provider. Please set it in your .env.local file."},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvEmpty","env":"everything","getAIModel":{"error":"CUSTOM_KEY_EMPTY environment variable is required for custom provider. Please set it in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvArrayPartial","env":"empty","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvArrayPartial","env":"serverKey","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvArrayPartial","env":"serverKeyAndUrl","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"custom-env-key","baseURL":"https://server.example/v1","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":true,"usesServerEndpoint":false,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvArrayPartial","env":"ollamaKey","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvArrayPartial","env":"azureResource","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvArrayPartial","env":"anthropicToken","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvArrayPartial","env":"adminAws","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvArrayPartial","env":"desktop","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvArrayPartial","env":"everything","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"custom-env-key","baseURL":"https://server.example/v1","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":true,"usesServerEndpoint":false,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvArrayEmpty","env":"empty","getAIModel":{"error":"At least one of [CUSTOM_KEY_EMPTY, CUSTOM_KEY_EMPTY_2] environment variables is required for custom provider. Please set at least one in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvArrayEmpty","env":"serverKey","getAIModel":{"error":"At least one of [CUSTOM_KEY_EMPTY, CUSTOM_KEY_EMPTY_2] environment variables is required for custom provider. Please set at least one in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvArrayEmpty","env":"serverKeyAndUrl","getAIModel":{"error":"At least one of [CUSTOM_KEY_EMPTY, CUSTOM_KEY_EMPTY_2] environment variables is required for custom provider. Please set at least one in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvArrayEmpty","env":"ollamaKey","getAIModel":{"error":"At least one of [CUSTOM_KEY_EMPTY, CUSTOM_KEY_EMPTY_2] environment variables is required for custom provider. Please set at least one in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvArrayEmpty","env":"azureResource","getAIModel":{"error":"At least one of [CUSTOM_KEY_EMPTY, CUSTOM_KEY_EMPTY_2] environment variables is required for custom provider. Please set at least one in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvArrayEmpty","env":"anthropicToken","getAIModel":{"error":"At least one of [CUSTOM_KEY_EMPTY, CUSTOM_KEY_EMPTY_2] environment variables is required for custom provider. Please set at least one in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvArrayEmpty","env":"adminAws","getAIModel":{"error":"At least one of [CUSTOM_KEY_EMPTY, CUSTOM_KEY_EMPTY_2] environment variables is required for custom provider. Please set at least one in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvArrayEmpty","env":"desktop","getAIModel":{"error":"At least one of [CUSTOM_KEY_EMPTY, CUSTOM_KEY_EMPTY_2] environment variables is required for custom provider. Please set at least one in your .env.local file."},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvArrayEmpty","env":"everything","getAIModel":{"error":"At least one of [CUSTOM_KEY_EMPTY, CUSTOM_KEY_EMPTY_2] environment variables is required for custom provider. Please set at least one in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvAndUrlEnv","env":"empty","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"custom-env-key","baseURL":"https://custom-env.example/v1","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvAndUrlEnv","env":"serverKey","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"custom-env-key","baseURL":"https://custom-env.example/v1","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvAndUrlEnv","env":"serverKeyAndUrl","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"custom-env-key","baseURL":"https://custom-env.example/v1","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":true,"usesServerEndpoint":false,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvAndUrlEnv","env":"ollamaKey","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"custom-env-key","baseURL":"https://custom-env.example/v1","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvAndUrlEnv","env":"azureResource","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"custom-env-key","baseURL":"https://custom-env.example/v1","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvAndUrlEnv","env":"anthropicToken","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"custom-env-key","baseURL":"https://custom-env.example/v1","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvAndUrlEnv","env":"adminAws","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"custom-env-key","baseURL":"https://custom-env.example/v1","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvAndUrlEnv","env":"desktop","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"custom-env-key","baseURL":"https://custom-env.example/v1","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"keyEnvAndUrlEnv","env":"everything","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"custom-env-key","baseURL":"https://custom-env.example/v1","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":true,"usesServerEndpoint":false,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"awsPair","env":"empty","getAIModel":{"error":"CUSTOM_API_KEY environment variable is required for custom provider. Please set it in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"awsPair","env":"serverKey","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"awsPair","env":"serverKeyAndUrl","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"server-key","baseURL":"https://server.example/v1","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":true,"usesServerEndpoint":false,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"awsPair","env":"ollamaKey","getAIModel":{"error":"CUSTOM_API_KEY environment variable is required for custom provider. Please set it in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"awsPair","env":"azureResource","getAIModel":{"error":"CUSTOM_API_KEY environment variable is required for custom provider. Please set it in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"awsPair","env":"anthropicToken","getAIModel":{"error":"CUSTOM_API_KEY environment variable is required for custom provider. Please set it in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"awsPair","env":"adminAws","getAIModel":{"error":"CUSTOM_API_KEY environment variable is required for custom provider. Please set it in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"awsPair","env":"desktop","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"awsPair","env":"everything","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"server-key","baseURL":"https://server.example/v1","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":true,"usesServerEndpoint":false,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"awsIdOnly","env":"empty","getAIModel":{"error":"CUSTOM_API_KEY environment variable is required for custom provider. Please set it in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"awsIdOnly","env":"serverKey","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"awsIdOnly","env":"serverKeyAndUrl","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"server-key","baseURL":"https://server.example/v1","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":true,"usesServerEndpoint":false,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"awsIdOnly","env":"ollamaKey","getAIModel":{"error":"CUSTOM_API_KEY environment variable is required for custom provider. Please set it in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"awsIdOnly","env":"azureResource","getAIModel":{"error":"CUSTOM_API_KEY environment variable is required for custom provider. Please set it in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"awsIdOnly","env":"anthropicToken","getAIModel":{"error":"CUSTOM_API_KEY environment variable is required for custom provider. Please set it in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"awsIdOnly","env":"adminAws","getAIModel":{"error":"CUSTOM_API_KEY environment variable is required for custom provider. Please set it in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"awsIdOnly","env":"desktop","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"awsIdOnly","env":"everything","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"server-key","baseURL":"https://server.example/v1","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":true,"usesServerEndpoint":false,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":null}
{"provider":"custom","modelId":"test-model","shape":"vertexKeyAndUrl","env":"empty","getAIModel":{"error":"API key is required when using a custom base URL. Please provide your own API key in Settings."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"vertexKeyAndUrl","env":"serverKey","getAIModel":{"error":"API key is required when using a custom base URL. Please provide your own API key in Settings."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"vertexKeyAndUrl","env":"serverKeyAndUrl","getAIModel":{"error":"API key is required when using a custom base URL. Please provide your own API key in Settings."},"usesServerCredentials":true,"usesServerEndpoint":false,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"vertexKeyAndUrl","env":"ollamaKey","getAIModel":{"error":"API key is required when using a custom base URL. Please provide your own API key in Settings."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"vertexKeyAndUrl","env":"azureResource","getAIModel":{"error":"API key is required when using a custom base URL. Please provide your own API key in Settings."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"vertexKeyAndUrl","env":"anthropicToken","getAIModel":{"error":"API key is required when using a custom base URL. Please provide your own API key in Settings."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"vertexKeyAndUrl","env":"adminAws","getAIModel":{"error":"API key is required when using a custom base URL. Please provide your own API key in Settings."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"vertexKeyAndUrl","env":"desktop","getAIModel":{"error":"API key is required when using a custom base URL. Please provide your own API key in Settings."},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"vertexKeyAndUrl","env":"everything","getAIModel":{"error":"API key is required when using a custom base URL. Please provide your own API key in Settings."},"usesServerCredentials":true,"usesServerEndpoint":false,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"trustedUrl","env":"empty","getAIModel":{"error":"CUSTOM_API_KEY environment variable is required for custom provider. Please set it in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"trustedUrl","env":"serverKey","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"server-key","baseURL":"https://u.example/v1","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"trustedUrl","env":"serverKeyAndUrl","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"server-key","baseURL":"https://u.example/v1","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":true,"usesServerEndpoint":false,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"trustedUrl","env":"ollamaKey","getAIModel":{"error":"CUSTOM_API_KEY environment variable is required for custom provider. Please set it in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"trustedUrl","env":"azureResource","getAIModel":{"error":"CUSTOM_API_KEY environment variable is required for custom provider. Please set it in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"trustedUrl","env":"anthropicToken","getAIModel":{"error":"CUSTOM_API_KEY environment variable is required for custom provider. Please set it in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"trustedUrl","env":"adminAws","getAIModel":{"error":"CUSTOM_API_KEY environment variable is required for custom provider. Please set it in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"trustedUrl","env":"desktop","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"server-key","baseURL":"https://u.example/v1","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"trustedUrl","env":"everything","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"server-key","baseURL":"https://u.example/v1","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":true,"usesServerEndpoint":false,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":"https://u.example/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"keyAndPrivateUrl","env":"empty","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"client-key","baseURL":"http://10.0.0.5:8000/v1","fetch":"guarded","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":false,"usesServerEndpoint":true,"chatRequestUrl":"http://10.0.0.5:8000/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"keyAndPrivateUrl","env":"serverKey","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"client-key","baseURL":"http://10.0.0.5:8000/v1","fetch":"guarded","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":false,"usesServerEndpoint":true,"chatRequestUrl":"http://10.0.0.5:8000/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"keyAndPrivateUrl","env":"serverKeyAndUrl","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"client-key","baseURL":"http://10.0.0.5:8000/v1","fetch":"guarded","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":false,"usesServerEndpoint":true,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":"http://10.0.0.5:8000/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"keyAndPrivateUrl","env":"ollamaKey","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"client-key","baseURL":"http://10.0.0.5:8000/v1","fetch":"guarded","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":false,"usesServerEndpoint":true,"chatRequestUrl":"http://10.0.0.5:8000/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"keyAndPrivateUrl","env":"azureResource","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"client-key","baseURL":"http://10.0.0.5:8000/v1","fetch":"guarded","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":false,"usesServerEndpoint":true,"chatRequestUrl":"http://10.0.0.5:8000/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"keyAndPrivateUrl","env":"anthropicToken","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"client-key","baseURL":"http://10.0.0.5:8000/v1","fetch":"guarded","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":false,"usesServerEndpoint":true,"chatRequestUrl":"http://10.0.0.5:8000/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"keyAndPrivateUrl","env":"adminAws","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"client-key","baseURL":"http://10.0.0.5:8000/v1","fetch":"guarded","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":false,"usesServerEndpoint":true,"chatRequestUrl":"http://10.0.0.5:8000/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"keyAndPrivateUrl","env":"desktop","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"client-key","baseURL":"http://10.0.0.5:8000/v1","fetch":"guarded","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":false,"usesServerEndpoint":false,"chatRequestUrl":"http://10.0.0.5:8000/v1/chat/completions"}
{"provider":"custom","modelId":"test-model","shape":"keyAndPrivateUrl","env":"everything","getAIModel":{"provider":"custom","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"custom","apiKey":"client-key","baseURL":"http://10.0.0.5:8000/v1","fetch":"guarded","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}},"usesServerCredentials":false,"usesServerEndpoint":true,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":"http://10.0.0.5:8000/v1/chat/completions"}
{"noProvider":true,"env":"only OPENAI_API_KEY","serverProvider":"openai","getAIModel":{"provider":"openai","modelId":"test-model","model":{"factory":"openai","method":"default","modelId":"test-model","options":{"apiKey":"server-key"}}}} {"noProvider":true,"env":"only OPENAI_API_KEY","serverProvider":"openai","getAIModel":{"provider":"openai","modelId":"test-model","model":{"factory":"openai","method":"default","modelId":"test-model","options":{"apiKey":"server-key"}}}}
{"noProvider":true,"env":"only ANTHROPIC_API_KEY","serverProvider":"anthropic","getAIModel":{"provider":"anthropic","modelId":"test-model","model":{"factory":"anthropic","method":"default","modelId":"test-model","options":{"apiKey":"server-key","baseURL":"https://api.anthropic.com/v1"}}}} {"noProvider":true,"env":"only ANTHROPIC_API_KEY","serverProvider":"anthropic","getAIModel":{"provider":"anthropic","modelId":"test-model","model":{"factory":"anthropic","method":"default","modelId":"test-model","options":{"apiKey":"server-key","baseURL":"https://api.anthropic.com/v1"}}}}
{"noProvider":true,"env":"only GOOGLE_GENERATIVE_AI_API_KEY","serverProvider":"google","getAIModel":{"provider":"google","modelId":"test-model","model":{"factory":"google","method":"default","modelId":"test-model","options":{"apiKey":"server-key"}}}} {"noProvider":true,"env":"only GOOGLE_GENERATIVE_AI_API_KEY","serverProvider":"google","getAIModel":{"provider":"google","modelId":"test-model","model":{"factory":"google","method":"default","modelId":"test-model","options":{"apiKey":"server-key"}}}}
@@ -3475,9 +3619,10 @@
{"noProvider":true,"env":"only NOVITA_API_KEY","serverProvider":"novita","getAIModel":{"provider":"novita","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"novita","apiKey":"server-key","baseURL":"https://api.novita.ai/openai","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}}} {"noProvider":true,"env":"only NOVITA_API_KEY","serverProvider":"novita","getAIModel":{"provider":"novita","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"novita","apiKey":"server-key","baseURL":"https://api.novita.ai/openai","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}}}
{"noProvider":true,"env":"only MIMO_API_KEY","serverProvider":"mimo","getAIModel":{"provider":"mimo","modelId":"test-model","model":{"factory":"deepseek","method":"default","modelId":"test-model","options":{"apiKey":"server-key","baseURL":"https://api.xiaomimimo.com/v1"}}}} {"noProvider":true,"env":"only MIMO_API_KEY","serverProvider":"mimo","getAIModel":{"provider":"mimo","modelId":"test-model","model":{"factory":"deepseek","method":"default","modelId":"test-model","options":{"apiKey":"server-key","baseURL":"https://api.xiaomimimo.com/v1"}}}}
{"noProvider":true,"env":"only ATLASCLOUD_API_KEY","serverProvider":"atlascloud","getAIModel":{"provider":"atlascloud","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"atlascloud","apiKey":"server-key","baseURL":"https://api.atlascloud.ai/v1","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}}} {"noProvider":true,"env":"only ATLASCLOUD_API_KEY","serverProvider":"atlascloud","getAIModel":{"provider":"atlascloud","modelId":"test-model","model":{"wrapped":{"factory":"openai-compatible","method":"default","modelId":"test-model","options":{"name":"atlascloud","apiKey":"server-key","baseURL":"https://api.atlascloud.ai/v1","includeUsage":true}},"middleware":{"middleware":"extractReasoning","tagName":"think"}}}}
{"noProvider":true,"env":"only CUSTOM_API_KEY","serverProvider":"custom","getAIModel":{"error":"OpenAI-compatible needs a base URL. Add it in the model settings."}}
{"noProvider":true,"env":"vertex and openai","serverProvider":null,"getAIModel":{"error":"Multiple AI providers configured (openai, vertexai). Please set AI_PROVIDER to specify which one to use."}} {"noProvider":true,"env":"vertex and openai","serverProvider":null,"getAIModel":{"error":"Multiple AI providers configured (openai, vertexai). Please set AI_PROVIDER to specify which one to use."}}
{"noProvider":true,"env":"only OLLAMA_API_KEY","serverProvider":null,"getAIModel":{"error":"No AI provider configured. Please set one of the following API keys in your .env.local file:\n- OPENAI_API_KEY for openai\n- ANTHROPIC_API_KEY for anthropic\n- GOOGLE_GENERATIVE_AI_API_KEY for google\n- DEEPSEEK_API_KEY for deepseek\n- QWEN_API_KEY for qwen\n- DOUBAO_API_KEY for doubao\n- GLM_API_KEY for glm\n- KIMI_API_KEY for kimi\n- MINIMAX_API_KEY for minimax\n- MIMO_API_KEY for mimo\n- OPENROUTER_API_KEY for openrouter\n- AIHUBMIX_API_KEY for aihubmix\n- SILICONFLOW_API_KEY for siliconflow\n- MODELSCOPE_API_KEY for modelscope\n- NOVITA_API_KEY for novita\n- ATLASCLOUD_API_KEY for atlascloud\n- QINIU_API_KEY for qiniu\n- AI_GATEWAY_API_KEY for gateway\n- GOOGLE_VERTEX_API_KEY for vertexai\n- AZURE_API_KEY for azure\n- SGLANG_API_KEY for sglang\n- AWS_ACCESS_KEY_ID for bedrock\nOr set AI_PROVIDER=ollama for local Ollama."}} {"noProvider":true,"env":"only OLLAMA_API_KEY","serverProvider":null,"getAIModel":{"error":"No AI provider configured. Please set one of the following API keys in your .env.local file:\n- OPENAI_API_KEY for openai\n- ANTHROPIC_API_KEY for anthropic\n- GOOGLE_GENERATIVE_AI_API_KEY for google\n- DEEPSEEK_API_KEY for deepseek\n- QWEN_API_KEY for qwen\n- DOUBAO_API_KEY for doubao\n- GLM_API_KEY for glm\n- KIMI_API_KEY for kimi\n- MINIMAX_API_KEY for minimax\n- MIMO_API_KEY for mimo\n- OPENROUTER_API_KEY for openrouter\n- AIHUBMIX_API_KEY for aihubmix\n- SILICONFLOW_API_KEY for siliconflow\n- MODELSCOPE_API_KEY for modelscope\n- NOVITA_API_KEY for novita\n- ATLASCLOUD_API_KEY for atlascloud\n- QINIU_API_KEY for qiniu\n- AI_GATEWAY_API_KEY for gateway\n- GOOGLE_VERTEX_API_KEY for vertexai\n- AZURE_API_KEY for azure\n- SGLANG_API_KEY for sglang\n- CUSTOM_API_KEY for custom\n- AWS_ACCESS_KEY_ID for bedrock\nOr set AI_PROVIDER=ollama for local Ollama."}}
{"noProvider":true,"env":"no key at all","serverProvider":null,"getAIModel":{"error":"No AI provider configured. Please set one of the following API keys in your .env.local file:\n- OPENAI_API_KEY for openai\n- ANTHROPIC_API_KEY for anthropic\n- GOOGLE_GENERATIVE_AI_API_KEY for google\n- DEEPSEEK_API_KEY for deepseek\n- QWEN_API_KEY for qwen\n- DOUBAO_API_KEY for doubao\n- GLM_API_KEY for glm\n- KIMI_API_KEY for kimi\n- MINIMAX_API_KEY for minimax\n- MIMO_API_KEY for mimo\n- OPENROUTER_API_KEY for openrouter\n- AIHUBMIX_API_KEY for aihubmix\n- SILICONFLOW_API_KEY for siliconflow\n- MODELSCOPE_API_KEY for modelscope\n- NOVITA_API_KEY for novita\n- ATLASCLOUD_API_KEY for atlascloud\n- QINIU_API_KEY for qiniu\n- AI_GATEWAY_API_KEY for gateway\n- GOOGLE_VERTEX_API_KEY for vertexai\n- AZURE_API_KEY for azure\n- SGLANG_API_KEY for sglang\n- AWS_ACCESS_KEY_ID for bedrock\nOr set AI_PROVIDER=ollama for local Ollama."}} {"noProvider":true,"env":"no key at all","serverProvider":null,"getAIModel":{"error":"No AI provider configured. Please set one of the following API keys in your .env.local file:\n- OPENAI_API_KEY for openai\n- ANTHROPIC_API_KEY for anthropic\n- GOOGLE_GENERATIVE_AI_API_KEY for google\n- DEEPSEEK_API_KEY for deepseek\n- QWEN_API_KEY for qwen\n- DOUBAO_API_KEY for doubao\n- GLM_API_KEY for glm\n- KIMI_API_KEY for kimi\n- MINIMAX_API_KEY for minimax\n- MIMO_API_KEY for mimo\n- OPENROUTER_API_KEY for openrouter\n- AIHUBMIX_API_KEY for aihubmix\n- SILICONFLOW_API_KEY for siliconflow\n- MODELSCOPE_API_KEY for modelscope\n- NOVITA_API_KEY for novita\n- ATLASCLOUD_API_KEY for atlascloud\n- QINIU_API_KEY for qiniu\n- AI_GATEWAY_API_KEY for gateway\n- GOOGLE_VERTEX_API_KEY for vertexai\n- AZURE_API_KEY for azure\n- SGLANG_API_KEY for sglang\n- CUSTOM_API_KEY for custom\n- AWS_ACCESS_KEY_ID for bedrock\nOr set AI_PROVIDER=ollama for local Ollama."}}
{"noProvider":true,"env":"AI_PROVIDER=openai","getAIModel":{"error":"OPENAI_API_KEY environment variable is required for openai provider. Please set it in your .env.local file."}} {"noProvider":true,"env":"AI_PROVIDER=openai","getAIModel":{"error":"OPENAI_API_KEY environment variable is required for openai provider. Please set it in your .env.local file."}}
{"noProvider":true,"env":"AI_PROVIDER=anthropic","getAIModel":{"error":"Either ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN environment variable is required for anthropic provider. Please set one in your .env.local file."}} {"noProvider":true,"env":"AI_PROVIDER=anthropic","getAIModel":{"error":"Either ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN environment variable is required for anthropic provider. Please set one in your .env.local file."}}
{"noProvider":true,"env":"AI_PROVIDER=google","getAIModel":{"error":"GOOGLE_GENERATIVE_AI_API_KEY environment variable is required for google provider. Please set it in your .env.local file."}} {"noProvider":true,"env":"AI_PROVIDER=google","getAIModel":{"error":"GOOGLE_GENERATIVE_AI_API_KEY environment variable is required for google provider. Please set it in your .env.local file."}}
@@ -3502,6 +3647,7 @@
{"noProvider":true,"env":"AI_PROVIDER=novita","getAIModel":{"error":"NOVITA_API_KEY environment variable is required for novita provider. Please set it in your .env.local file."}} {"noProvider":true,"env":"AI_PROVIDER=novita","getAIModel":{"error":"NOVITA_API_KEY environment variable is required for novita provider. Please set it in your .env.local file."}}
{"noProvider":true,"env":"AI_PROVIDER=mimo","getAIModel":{"error":"MIMO_API_KEY environment variable is required for mimo provider. Please set it in your .env.local file."}} {"noProvider":true,"env":"AI_PROVIDER=mimo","getAIModel":{"error":"MIMO_API_KEY environment variable is required for mimo provider. Please set it in your .env.local file."}}
{"noProvider":true,"env":"AI_PROVIDER=atlascloud","getAIModel":{"error":"ATLASCLOUD_API_KEY environment variable is required for atlascloud provider. Please set it in your .env.local file."}} {"noProvider":true,"env":"AI_PROVIDER=atlascloud","getAIModel":{"error":"ATLASCLOUD_API_KEY environment variable is required for atlascloud provider. Please set it in your .env.local file."}}
{"noProvider":true,"env":"AI_PROVIDER=custom","getAIModel":{"error":"CUSTOM_API_KEY environment variable is required for custom provider. Please set it in your .env.local file."}}
{"provider":"openai","modelId":"gpt-5","shape":"none","env":"empty","getAIModel":{"error":"OPENAI_API_KEY environment variable is required for openai provider. Please set it in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null} {"provider":"openai","modelId":"gpt-5","shape":"none","env":"empty","getAIModel":{"error":"OPENAI_API_KEY environment variable is required for openai provider. Please set it in your .env.local file."},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"openai","modelId":"gpt-5","shape":"none","env":"serverKey","getAIModel":{"provider":"openai","modelId":"gpt-5","model":{"factory":"openai","method":"default","modelId":"gpt-5","options":{"apiKey":"server-key"}},"providerOptions":{"openai":{"reasoningSummary":"auto"}}},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null} {"provider":"openai","modelId":"gpt-5","shape":"none","env":"serverKey","getAIModel":{"provider":"openai","modelId":"gpt-5","model":{"factory":"openai","method":"default","modelId":"gpt-5","options":{"apiKey":"server-key"}},"providerOptions":{"openai":{"reasoningSummary":"auto"}}},"usesServerCredentials":true,"usesServerEndpoint":false,"chatRequestUrl":null}
{"provider":"openai","modelId":"gpt-5","shape":"none","env":"serverKeyAndUrl","getAIModel":{"provider":"openai","modelId":"gpt-5","model":{"factory":"openai","method":"chat","modelId":"gpt-5","options":{"apiKey":"server-key","baseURL":"https://server.example/v1"}},"providerOptions":{"openai":{"reasoningSummary":"auto"}}},"usesServerCredentials":true,"usesServerEndpoint":false,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":null} {"provider":"openai","modelId":"gpt-5","shape":"none","env":"serverKeyAndUrl","getAIModel":{"provider":"openai","modelId":"gpt-5","model":{"factory":"openai","method":"chat","modelId":"gpt-5","options":{"apiKey":"server-key","baseURL":"https://server.example/v1"}},"providerOptions":{"openai":{"reasoningSummary":"auto"}}},"usesServerCredentials":true,"usesServerEndpoint":false,"globalBaseUrl":"https://server.example/v1","chatRequestUrl":null}
+3 -1
View File
@@ -158,6 +158,7 @@ const MATRIX_PROVIDERS: ProviderName[] = [
"novita", "novita",
"mimo", "mimo",
"atlascloud", "atlascloud",
"custom",
] ]
/** The model id each provider runs in the matrix (the first suggested one) */ /** The model id each provider runs in the matrix (the first suggested one) */
@@ -186,6 +187,7 @@ const MATRIX_MODEL_IDS: Record<ProviderName, string> = {
novita: "minimax/minimax-m3", novita: "minimax/minimax-m3",
mimo: "mimo-v2.5-pro", mimo: "mimo-v2.5-pro",
atlascloud: "qwen/qwen3.5-flash", atlascloud: "qwen/qwen3.5-flash",
custom: "test-model",
} }
/** Model ids whose providerOptions the snapshot records for openai and google */ /** Model ids whose providerOptions the snapshot records for openai and google */
@@ -449,7 +451,7 @@ describe("provider behavior snapshot", () => {
}) })
it("matches the recorded behavior", async () => { it("matches the recorded behavior", async () => {
// The matrix: 24 providers, 16 shapes, 9 environments // The matrix: 25 providers, 16 shapes, 9 environments
for (const provider of MATRIX_PROVIDERS) { for (const provider of MATRIX_PROVIDERS) {
await matrix(provider, MATRIX_MODEL_IDS[provider]) await matrix(provider, MATRIX_MODEL_IDS[provider])
} }
+4 -4
View File
@@ -24,16 +24,16 @@ const sorted = (list: Iterable<string>) => [...list].sort()
describe("the provider record", () => { describe("the provider record", () => {
it("is what the rest of the app imports as PROVIDER_INFO", () => { it("is what the rest of the app imports as PROVIDER_INFO", () => {
expect(PROVIDER_INFO).toBe(PROVIDERS) expect(PROVIDER_INFO).toBe(PROVIDERS)
expect(PROVIDER_NAMES).toHaveLength(24) expect(PROVIDER_NAMES).toHaveLength(25)
}) })
it("has 21 providers taking part in server key auto-detection", () => { it("has 22 providers taking part in server key auto-detection", () => {
// credential apiKey or vertexKey: the providers whose <P>_API_KEY the // credential apiKey or vertexKey: the providers whose <P>_API_KEY the
// server must have; aws, optionalKey and none need no key // server must have; aws, optionalKey and none need no key
const detected = PROVIDER_NAMES.filter((p) => const detected = PROVIDER_NAMES.filter((p) =>
["apiKey", "vertexKey"].includes(PROVIDERS[p].credential), ["apiKey", "vertexKey"].includes(PROVIDERS[p].credential),
) )
expect(detected).toHaveLength(21) expect(detected).toHaveLength(22)
// Ollama has a key variable too, an optional one // Ollama has a key variable too, an optional one
const withKeyVar = PROVIDER_NAMES.filter((p) => !!envVarsOf(p).apiKey) const withKeyVar = PROVIDER_NAMES.filter((p) => !!envVarsOf(p).apiKey)
expect(sorted(detected)).toEqual( expect(sorted(detected)).toEqual(
@@ -142,7 +142,7 @@ describe("the picker's order", () => {
id: "cloud", id: "cloud",
providers: ["bedrock", "vertexai", "azure", "edgeone"], providers: ["bedrock", "vertexai", "azure", "edgeone"],
}, },
{ id: "selfHosted", providers: ["ollama", "sglang"] }, { id: "selfHosted", providers: ["ollama", "sglang", "custom"] },
]) ])
}) })