This commit is contained in:
Elias 2026-01-29 11:33:41 -05:00 committed by GitHub
commit 8b59d2d101
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 533 additions and 6 deletions

View File

@ -230,6 +230,27 @@ Synthetic provides Anthropic-compatible models behind the `synthetic` provider:
}
```
### OVHcloud AI Endpoints
OVHcloud AI Endpoints provides OpenAI-compatible inference APIs for open-source models. All inferences run in Europe, offering GDPR compliance and data privacy.
- Provider: `ovhcloud`
- Auth: `OVHCLOUD_API_KEY`
- Example model: `ovhcloud/gpt-oss-120b`
- CLI: `clawdbot onboard --auth-choice ovhcloud-api-key`
Models are auto-discovered from the OVHcloud catalog. The recommended model is `gpt-oss-120b`, a 120 billion parameter model.
```json5
{
agents: {
defaults: { model: { primary: "ovhcloud/gpt-oss-120b" } }
}
}
```
The provider is automatically configured when `OVHCLOUD_API_KEY` is set. See [/providers/ovhcloud](/providers/ovhcloud) for setup details and troubleshooting.
### MiniMax
MiniMax is configured via `models.providers` because it uses custom endpoints:

View File

@ -45,6 +45,7 @@ See [Venice AI](/providers/venice).
- [GLM models](/providers/glm)
- [MiniMax](/providers/minimax)
- [Venius (Venice AI, privacy-focused)](/providers/venice)
- [OVHcloud AI Endpoints](/providers/ovhcloud)
- [Ollama (local models)](/providers/ollama)
## Transcription providers

View File

@ -0,0 +1,71 @@
---
summary: "Use OVHcloud AI Endpoints in Clawdbot"
read_when:
- You want OVHcloud AI Endpoints models in Clawdbot
- You need OVHcloud setup guidance
---
# OVHcloud AI Endpoints
OVHcloud is the leading cloud provider in Europe and provides AI Endpoints: inference APIs for a selection of open-source models, such as Llama, Qwen, GPT OSS, and more. All inferences run in Europe, offering GDPR compliance, sovereignty, and data privacy. Your prompt and the LLM response are neither used nor saved.
Source: [OVHcloud AI Endpoints](https://www.ovhcloud.com/en/public-cloud/ai-endpoints/)
## Model overview
OVHcloud AI Endpoints offers access to multiple open-weight models through a
serverless inference API. The recommended model is **gpt-oss-120b**, a powerful 120 billion parameter model made by OpenAI.
You can also explore our [catalog](https://www.ovhcloud.com/en/public-cloud/ai-endpoints/catalog) to browse all our available models.
## Setup
### Quick start
Configure via CLI:
```bash
clawdbot onboard --auth-choice ovhcloud-api-key
```
Or set the API key manually:
```bash
export OVHCLOUD_API_KEY="your-api-key-here"
```
### Config snippet
```json5
{
agents: {
defaults: {
model: { primary: "ovhcloud/gpt-oss-120b" }
}
}
}
```
## Notes
- Model refs use `ovhcloud/<model>` format.
- Your data is not reused or kept by OVHcloud; data privacy is guaranteed.
- Update pricing values in `models.json` if you need exact cost tracking.
- See [Model providers](/concepts/model-providers) for provider rules.
- Use `clawdbot models list` and `clawdbot models set ovhcloud/gpt-oss-120b` to switch models.
## Troubleshooting
### "Unknown model: ovhcloud/model-name"
This usually means the **OVHcloud provider isn't configured** (no provider entry and no OVHcloud auth profile/env key found). Fix by:
- Running `clawdbot configure` and selecting **OVHcloud**, or
- Adding the `models.providers.ovhcloud` block manually, or
- Setting `OVHCLOUD_API_KEY` (or an OVHcloud auth profile) so the provider can be injected.
Make sure the model id is **case-sensitive**: `ovhcloud/gpt-oss-120b`.
Then recheck with:
```bash
clawdbot models list
```

View File

@ -285,6 +285,7 @@ export function resolveEnvApiKey(provider: string): EnvApiKeyResult | null {
venice: "VENICE_API_KEY",
mistral: "MISTRAL_API_KEY",
opencode: "OPENCODE_API_KEY",
ovhcloud: "OVHCLOUD_API_KEY",
};
const envVar = envMap[normalized];
if (!envVar) return null;

View File

@ -24,6 +24,15 @@ describe("normalizeModelCompat", () => {
expect(normalized.compat?.supportsDeveloperRole).toBe(false);
});
it("forces supportsStore off for ovhcloud models", () => {
const model = baseModel();
model.provider = "ovhcloud";
model.baseUrl = "https://oai.endpoints.kepler.ai.cloud.ovh.net/v1";
delete (model as { compat?: unknown }).compat;
const normalized = normalizeModelCompat(model);
expect(normalized.compat?.supportsStore).toBe(false);
});
it("leaves non-zai models untouched", () => {
const model = {
...baseModel(),

View File

@ -5,16 +5,27 @@ function isOpenAiCompletionsModel(model: Model<Api>): model is Model<"openai-com
}
export function normalizeModelCompat(model: Model<Api>): Model<Api> {
if (!isOpenAiCompletionsModel(model)) return model;
const baseUrl = model.baseUrl ?? "";
const isZai = model.provider === "zai" || baseUrl.includes("api.z.ai");
if (!isZai || !isOpenAiCompletionsModel(model)) return model;
const isOvhcloud = model.provider === "ovhcloud" || baseUrl.includes("ovh.net");
if (!isZai && !isOvhcloud) return model;
const openaiModel = model as Model<"openai-completions">;
const compat = openaiModel.compat ?? undefined;
if (compat?.supportsDeveloperRole === false) return model;
openaiModel.compat = compat
? { ...compat, supportsDeveloperRole: false }
: { supportsDeveloperRole: false };
if (isZai) {
if (compat?.supportsDeveloperRole === false) return model;
openaiModel.compat = compat
? { ...compat, supportsDeveloperRole: false }
: { supportsDeveloperRole: false };
}
if (isOvhcloud) {
if (compat?.supportsStore === false) return model;
openaiModel.compat = compat ? { ...compat, supportsStore: false } : { supportsStore: false };
}
return openaiModel;
}

View File

@ -13,6 +13,7 @@ import {
SYNTHETIC_MODEL_CATALOG,
} from "./synthetic-models.js";
import { discoverVeniceModels, VENICE_BASE_URL } from "./venice-models.js";
import { discoverOvhcloudModels, OVHCLOUD_BASE_URL } from "./ovhcloud-models.js";
type ModelsConfig = NonNullable<MoltbotConfig["models"]>;
export type ProviderConfig = NonNullable<ModelsConfig["providers"]>[string];
@ -359,6 +360,15 @@ async function buildOllamaProvider(): Promise<ProviderConfig> {
};
}
async function buildOvhcloudProvider(): Promise<ProviderConfig> {
const models = await discoverOvhcloudModels();
return {
baseUrl: OVHCLOUD_BASE_URL,
api: "openai-completions",
models,
};
}
export async function resolveImplicitProviders(params: {
agentDir: string;
}): Promise<ModelsConfig["providers"]> {
@ -418,6 +428,13 @@ export async function resolveImplicitProviders(params: {
providers.ollama = { ...(await buildOllamaProvider()), apiKey: ollamaKey };
}
const ovhcloudKey =
resolveEnvApiKeyVarName("ovhcloud") ??
resolveApiKeyFromProfiles({ provider: "ovhcloud", store: authStore });
if (ovhcloudKey) {
providers.ovhcloud = { ...(await buildOvhcloudProvider()), apiKey: ovhcloudKey };
}
return providers;
}

View File

@ -0,0 +1,170 @@
import type { ModelDefinitionConfig } from "../config/types.js";
export const OVHCLOUD_BASE_URL = "https://oai.endpoints.kepler.ai.cloud.ovh.net/v1";
export const OVHCLOUD_DEFAULT_MODEL_ID = "gpt-oss-120b";
export const OVHCLOUD_DEFAULT_MODEL_REF = `ovhcloud/${OVHCLOUD_DEFAULT_MODEL_ID}`;
export const OVHCLOUD_DEFAULT_CONTEXT_WINDOW = 128000;
export const OVHCLOUD_DEFAULT_MAX_TOKENS = 8192;
export const OVHCLOUD_DEFAULT_COST = {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0,
};
// OVHcloud Catalog API response types
interface OvhcloudModelPricing {
prompt: string;
completion: string;
input_cache_reads: string;
input_cache_writes: string;
image: string;
request: string;
}
interface OvhcloudModel {
id: string;
name: string;
description?: string;
context_length: number;
max_output_length: number;
input_modalities: string[];
output_modalities: string[];
supported_features: string[];
pricing: OvhcloudModelPricing;
hugging_face_id?: string;
openrouter?: {
slug: string;
};
quantization?: string;
created?: number;
datacenters?: Array<{ country_code: string }>;
}
interface OvhcloudCatalogResponse {
data: OvhcloudModel[];
}
const OVHCLOUD_CATALOG_URL = "https://catalog.endpoints.ai.ovh.net/rest/v2/openrouter";
/**
* Discover models from OVHcloud AI Endpoints catalog with fallback to default model.
* The catalog endpoint is public and does not require authentication.
*/
export async function discoverOvhcloudModels(): Promise<ModelDefinitionConfig[]> {
// Skip API discovery in test environment
if (process.env.VITEST || process.env.NODE_ENV === "test") {
return [
{
id: OVHCLOUD_DEFAULT_MODEL_ID,
name: OVHCLOUD_DEFAULT_MODEL_ID,
reasoning: false,
input: ["text"],
cost: OVHCLOUD_DEFAULT_COST,
contextWindow: OVHCLOUD_DEFAULT_CONTEXT_WINDOW,
maxTokens: OVHCLOUD_DEFAULT_MAX_TOKENS,
},
];
}
try {
const response = await fetch(OVHCLOUD_CATALOG_URL, {
signal: AbortSignal.timeout(5000),
});
if (!response.ok) {
console.warn(
`[ovhcloud-models] Failed to discover models: HTTP ${response.status}, using default model`,
);
return [
{
id: OVHCLOUD_DEFAULT_MODEL_ID,
name: OVHCLOUD_DEFAULT_MODEL_ID,
reasoning: false,
input: ["text"],
cost: OVHCLOUD_DEFAULT_COST,
contextWindow: OVHCLOUD_DEFAULT_CONTEXT_WINDOW,
maxTokens: OVHCLOUD_DEFAULT_MAX_TOKENS,
},
];
}
const data = (await response.json()) as OvhcloudCatalogResponse;
if (!Array.isArray(data.data) || data.data.length === 0) {
console.warn("[ovhcloud-models] No models found from catalog, using default model");
return [
{
id: OVHCLOUD_DEFAULT_MODEL_ID,
name: OVHCLOUD_DEFAULT_MODEL_ID,
reasoning: false,
input: ["text"],
cost: OVHCLOUD_DEFAULT_COST,
contextWindow: OVHCLOUD_DEFAULT_CONTEXT_WINDOW,
maxTokens: OVHCLOUD_DEFAULT_MAX_TOKENS,
},
];
}
// Convert discovered models to ModelDefinitionConfig
const models: ModelDefinitionConfig[] = data.data.map((apiModel) => {
const isReasoning = apiModel.supported_features?.includes("reasoning") ?? false;
const hasVision = apiModel.input_modalities?.includes("image") ?? false;
// Parse pricing (values are strings representing cost per token, convert to cost per million tokens)
// Example: "0.00000009" per token = 0.09 per million tokens
const parsePricing = (value: string): number => {
if (!value || value === "") return 0;
const num = parseFloat(value);
if (isNaN(num) || num === 0) return 0;
// Convert per-token to per-million-tokens, round to nearest integer
// For very small values (< 0.5 per million), use Math.ceil to avoid rounding to 0
const perMillion = num * 1000000;
return perMillion < 0.5 ? Math.ceil(perMillion) : Math.round(perMillion);
};
const cost = {
input: parsePricing(apiModel.pricing.prompt),
output: parsePricing(apiModel.pricing.completion),
cacheRead: parsePricing(apiModel.pricing.input_cache_reads),
cacheWrite: parsePricing(apiModel.pricing.input_cache_writes),
};
return {
id: apiModel.id,
name: apiModel.name || apiModel.id,
reasoning: isReasoning,
input: hasVision ? ["text", "image"] : ["text"],
cost,
contextWindow: apiModel.context_length || OVHCLOUD_DEFAULT_CONTEXT_WINDOW,
maxTokens: apiModel.max_output_length || OVHCLOUD_DEFAULT_MAX_TOKENS,
};
});
return models.length > 0
? models
: [
{
id: OVHCLOUD_DEFAULT_MODEL_ID,
name: OVHCLOUD_DEFAULT_MODEL_ID,
reasoning: false,
input: ["text"],
cost: OVHCLOUD_DEFAULT_COST,
contextWindow: OVHCLOUD_DEFAULT_CONTEXT_WINDOW,
maxTokens: OVHCLOUD_DEFAULT_MAX_TOKENS,
},
];
} catch (error) {
console.warn(`[ovhcloud-models] Discovery failed: ${String(error)}, using default model`);
return [
{
id: OVHCLOUD_DEFAULT_MODEL_ID,
name: OVHCLOUD_DEFAULT_MODEL_ID,
reasoning: false,
input: ["text"],
cost: OVHCLOUD_DEFAULT_COST,
contextWindow: OVHCLOUD_DEFAULT_CONTEXT_WINDOW,
maxTokens: OVHCLOUD_DEFAULT_MAX_TOKENS,
},
];
}
}

View File

@ -52,7 +52,7 @@ export function registerOnboardCommand(program: Command) {
.option("--mode <mode>", "Wizard mode: local|remote")
.option(
"--auth-choice <choice>",
"Auth: setup-token|token|chutes|openai-codex|openai-api-key|openrouter-api-key|ai-gateway-api-key|moonshot-api-key|kimi-code-api-key|synthetic-api-key|venice-api-key|gemini-api-key|zai-api-key|apiKey|minimax-api|minimax-api-lightning|opencode-zen|skip",
"Auth: setup-token|token|chutes|openai-codex|openai-api-key|openrouter-api-key|ai-gateway-api-key|moonshot-api-key|kimi-code-api-key|synthetic-api-key|venice-api-key|ovhcloud-api-key|gemini-api-key|zai-api-key|apiKey|minimax-api|minimax-api-lightning|opencode-zen|skip",
)
.option(
"--token-provider <id>",
@ -75,6 +75,7 @@ export function registerOnboardCommand(program: Command) {
.option("--minimax-api-key <key>", "MiniMax API key")
.option("--synthetic-api-key <key>", "Synthetic API key")
.option("--venice-api-key <key>", "Venice API key")
.option("--ovhcloud-api-key <key>", "OVHcloud AI Endpoints API key")
.option("--opencode-zen-api-key <key>", "OpenCode Zen API key")
.option("--gateway-port <port>", "Gateway port")
.option("--gateway-bind <mode>", "Gateway bind: loopback|tailnet|lan|auto|custom")

View File

@ -20,6 +20,7 @@ export type AuthChoiceGroupId =
| "minimax"
| "synthetic"
| "venice"
| "ovhcloud"
| "qwen";
export type AuthChoiceGroup = {
@ -71,6 +72,12 @@ const AUTH_CHOICE_GROUP_DEFS: {
hint: "Privacy-focused (uncensored models)",
choices: ["venice-api-key"],
},
{
value: "ovhcloud",
label: "OVHcloud AI Endpoints",
hint: "European-based with sovereignty and data privacy",
choices: ["ovhcloud-api-key"],
},
{
value: "google",
label: "Google",
@ -147,6 +154,11 @@ export function buildAuthChoiceOptions(params: {
label: "Venice AI API key",
hint: "Privacy-focused inference (uncensored models)",
});
options.push({
value: "ovhcloud-api-key",
label: "OVHcloud AI Endpoints API key",
hint: "European-based with sovereignty and data privacy",
});
options.push({
value: "github-copilot",
label: "GitHub Copilot (GitHub device login)",

View File

@ -21,6 +21,8 @@ import {
applyOpencodeZenProviderConfig,
applyOpenrouterConfig,
applyOpenrouterProviderConfig,
applyOvhcloudConfig,
applyOvhcloudProviderConfig,
applySyntheticConfig,
applySyntheticProviderConfig,
applyVeniceConfig,
@ -31,6 +33,7 @@ import {
KIMI_CODE_MODEL_REF,
MOONSHOT_DEFAULT_MODEL_REF,
OPENROUTER_DEFAULT_MODEL_REF,
OVHCLOUD_DEFAULT_MODEL_REF,
SYNTHETIC_DEFAULT_MODEL_REF,
VENICE_DEFAULT_MODEL_REF,
VERCEL_AI_GATEWAY_DEFAULT_MODEL_REF,
@ -39,6 +42,7 @@ import {
setMoonshotApiKey,
setOpencodeZenApiKey,
setOpenrouterApiKey,
setOvhcloudApiKey,
setSyntheticApiKey,
setVeniceApiKey,
setVercelAiGatewayApiKey,
@ -83,6 +87,8 @@ export async function applyAuthChoiceApiProviders(
authChoice = "synthetic-api-key";
} else if (params.opts.tokenProvider === "venice") {
authChoice = "venice-api-key";
} else if (params.opts.tokenProvider === "ovhcloud") {
authChoice = "ovhcloud-api-key";
} else if (params.opts.tokenProvider === "opencode") {
authChoice = "opencode-zen";
}
@ -522,6 +528,65 @@ export async function applyAuthChoiceApiProviders(
return { config: nextConfig, agentModelOverride };
}
if (authChoice === "ovhcloud-api-key") {
let hasCredential = false;
if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "ovhcloud") {
await setOvhcloudApiKey(normalizeApiKeyInput(params.opts.token), params.agentDir);
hasCredential = true;
}
if (!hasCredential) {
await params.prompter.note(
[
"Access the control panel at https://www.ovhcloud.com/ and navigate to",
"Public Cloud > AI & Machine Learning > AI Endpoints > API Keys",
"Recommended model: gpt-oss-120b",
].join("\n"),
"OVHcloud AI Endpoints",
);
}
const envKey = resolveEnvApiKey("ovhcloud");
if (envKey) {
const useExisting = await params.prompter.confirm({
message: `Use existing OVHCLOUD_API_KEY (${envKey.source}, ${formatApiKeyPreview(envKey.apiKey)})?`,
initialValue: true,
});
if (useExisting) {
await setOvhcloudApiKey(envKey.apiKey, params.agentDir);
hasCredential = true;
}
}
if (!hasCredential) {
const key = await params.prompter.text({
message: "Enter OVHcloud AI Endpoints API key",
validate: validateApiKeyInput,
});
await setOvhcloudApiKey(normalizeApiKeyInput(String(key)), params.agentDir);
}
nextConfig = applyAuthProfileConfig(nextConfig, {
profileId: "ovhcloud:default",
provider: "ovhcloud",
mode: "api_key",
});
{
const applied = await applyDefaultModelChoice({
config: nextConfig,
setDefaultModel: params.setDefaultModel,
defaultModel: OVHCLOUD_DEFAULT_MODEL_REF,
applyDefaultConfig: applyOvhcloudConfig,
applyProviderConfig: applyOvhcloudProviderConfig,
noteDefault: OVHCLOUD_DEFAULT_MODEL_REF,
noteAgentModel,
prompter: params.prompter,
});
nextConfig = applied.config;
agentModelOverride = applied.agentModelOverride ?? agentModelOverride;
}
return { config: nextConfig, agentModelOverride };
}
if (authChoice === "opencode-zen") {
let hasCredential = false;
if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "opencode") {

View File

@ -20,6 +20,7 @@ const PREFERRED_PROVIDER_BY_AUTH_CHOICE: Partial<Record<AuthChoice, string>> = {
"zai-api-key": "zai",
"synthetic-api-key": "synthetic",
"venice-api-key": "venice",
"ovhcloud-api-key": "ovhcloud",
"github-copilot": "github-copilot",
"copilot-proxy": "copilot-proxy",
"minimax-cloud": "minimax",

View File

@ -19,12 +19,16 @@ import {
import {
buildKimiCodeModelDefinition,
buildMoonshotModelDefinition,
buildOvhcloudModelDefinition,
KIMI_CODE_BASE_URL,
KIMI_CODE_MODEL_ID,
KIMI_CODE_MODEL_REF,
MOONSHOT_BASE_URL,
MOONSHOT_DEFAULT_MODEL_ID,
MOONSHOT_DEFAULT_MODEL_REF,
OVHCLOUD_BASE_URL,
OVHCLOUD_DEFAULT_MODEL_ID,
OVHCLOUD_DEFAULT_MODEL_REF,
} from "./onboard-auth.models.js";
export function applyZaiConfig(cfg: MoltbotConfig): MoltbotConfig {
@ -411,6 +415,79 @@ export function applyVeniceConfig(cfg: MoltbotConfig): MoltbotConfig {
};
}
/**
* Apply OVHcloud provider configuration without changing the default model.
* Registers OVHcloud models and sets up the provider, but preserves existing model selection.
*/
export function applyOvhcloudProviderConfig(cfg: ClawdbotConfig): ClawdbotConfig {
const models = { ...cfg.agents?.defaults?.models };
models[OVHCLOUD_DEFAULT_MODEL_REF] = {
...models[OVHCLOUD_DEFAULT_MODEL_REF],
alias: models[OVHCLOUD_DEFAULT_MODEL_REF]?.alias ?? "gpt-oss-120b",
};
const providers = { ...cfg.models?.providers };
const existingProvider = providers.ovhcloud;
const existingModels = Array.isArray(existingProvider?.models) ? existingProvider.models : [];
const defaultModel = buildOvhcloudModelDefinition();
const hasDefaultModel = existingModels.some((model) => model.id === OVHCLOUD_DEFAULT_MODEL_ID);
const mergedModels = hasDefaultModel ? existingModels : [...existingModels, defaultModel];
const { apiKey: existingApiKey, ...existingProviderRest } = (existingProvider ?? {}) as Record<
string,
unknown
> as { apiKey?: string };
const resolvedApiKey = typeof existingApiKey === "string" ? existingApiKey : undefined;
const normalizedApiKey = resolvedApiKey?.trim();
providers.ovhcloud = {
...existingProviderRest,
baseUrl: OVHCLOUD_BASE_URL,
api: "openai-completions",
...(normalizedApiKey ? { apiKey: normalizedApiKey } : {}),
models: mergedModels.length > 0 ? mergedModels : [defaultModel],
};
return {
...cfg,
agents: {
...cfg.agents,
defaults: {
...cfg.agents?.defaults,
models,
},
},
models: {
mode: cfg.models?.mode ?? "merge",
providers,
},
};
}
/**
* Apply OVHcloud provider configuration AND set OVHcloud as the default model.
* Use this when OVHcloud is the primary provider choice during onboarding.
*/
export function applyOvhcloudConfig(cfg: ClawdbotConfig): ClawdbotConfig {
const next = applyOvhcloudProviderConfig(cfg);
const existingModel = next.agents?.defaults?.model;
return {
...next,
agents: {
...next.agents,
defaults: {
...next.agents?.defaults,
model: {
...(existingModel && "fallbacks" in (existingModel as Record<string, unknown>)
? {
fallbacks: (existingModel as { fallbacks?: string[] }).fallbacks,
}
: undefined),
primary: OVHCLOUD_DEFAULT_MODEL_REF,
},
},
},
};
}
export function applyAuthProfileConfig(
cfg: MoltbotConfig,
params: {

View File

@ -112,6 +112,19 @@ export async function setVeniceApiKey(key: string, agentDir?: string) {
});
}
export async function setOvhcloudApiKey(key: string, agentDir?: string) {
// Write to resolved agent dir so gateway finds credentials on startup.
upsertAuthProfile({
profileId: "ovhcloud:default",
credential: {
type: "api_key",
provider: "ovhcloud",
key,
},
agentDir: resolveAuthAgentDir(agentDir),
});
}
export const ZAI_DEFAULT_MODEL_REF = "zai/glm-4.7";
export const OPENROUTER_DEFAULT_MODEL_REF = "openrouter/auto";
export const VERCEL_AI_GATEWAY_DEFAULT_MODEL_REF = "vercel-ai-gateway/anthropic/claude-opus-4.5";

View File

@ -1,4 +1,12 @@
import type { ModelDefinitionConfig } from "../config/types.js";
import {
OVHCLOUD_BASE_URL,
OVHCLOUD_DEFAULT_COST,
OVHCLOUD_DEFAULT_CONTEXT_WINDOW,
OVHCLOUD_DEFAULT_MAX_TOKENS,
OVHCLOUD_DEFAULT_MODEL_ID,
OVHCLOUD_DEFAULT_MODEL_REF,
} from "../agents/ovhcloud-models.js";
export const DEFAULT_MINIMAX_BASE_URL = "https://api.minimax.io/v1";
export const MINIMAX_API_BASE_URL = "https://api.minimax.io/anthropic";
@ -52,6 +60,16 @@ export const KIMI_CODE_DEFAULT_COST = {
cacheWrite: 0,
};
// Re-export OVHcloud constants from ovhcloud-models.ts
export {
OVHCLOUD_BASE_URL,
OVHCLOUD_DEFAULT_COST,
OVHCLOUD_DEFAULT_CONTEXT_WINDOW,
OVHCLOUD_DEFAULT_MAX_TOKENS,
OVHCLOUD_DEFAULT_MODEL_ID,
OVHCLOUD_DEFAULT_MODEL_REF,
};
const MINIMAX_MODEL_CATALOG = {
"MiniMax-M2.1": { name: "MiniMax M2.1", reasoning: false },
"MiniMax-M2.1-lightning": {
@ -116,3 +134,15 @@ export function buildKimiCodeModelDefinition(): ModelDefinitionConfig {
compat: KIMI_CODE_COMPAT,
};
}
export function buildOvhcloudModelDefinition(): ModelDefinitionConfig {
return {
id: OVHCLOUD_DEFAULT_MODEL_ID,
name: "gpt-oss-120b",
reasoning: false,
input: ["text"],
cost: OVHCLOUD_DEFAULT_COST,
contextWindow: OVHCLOUD_DEFAULT_CONTEXT_WINDOW,
maxTokens: OVHCLOUD_DEFAULT_MAX_TOKENS,
};
}

View File

@ -11,6 +11,8 @@ export {
applyMoonshotProviderConfig,
applyOpenrouterConfig,
applyOpenrouterProviderConfig,
applyOvhcloudConfig,
applyOvhcloudProviderConfig,
applySyntheticConfig,
applySyntheticProviderConfig,
applyVeniceConfig,
@ -41,6 +43,7 @@ export {
setMoonshotApiKey,
setOpencodeZenApiKey,
setOpenrouterApiKey,
setOvhcloudApiKey,
setSyntheticApiKey,
setVeniceApiKey,
setVercelAiGatewayApiKey,
@ -64,4 +67,5 @@ export {
MOONSHOT_BASE_URL,
MOONSHOT_DEFAULT_MODEL_ID,
MOONSHOT_DEFAULT_MODEL_REF,
OVHCLOUD_DEFAULT_MODEL_REF,
} from "./onboard-auth.models.js";

View File

@ -14,6 +14,7 @@ import {
applyMoonshotConfig,
applyOpencodeZenConfig,
applyOpenrouterConfig,
applyOvhcloudConfig,
applySyntheticConfig,
applyVeniceConfig,
applyVercelAiGatewayConfig,
@ -25,6 +26,7 @@ import {
setMoonshotApiKey,
setOpencodeZenApiKey,
setOpenrouterApiKey,
setOvhcloudApiKey,
setSyntheticApiKey,
setVeniceApiKey,
setVercelAiGatewayApiKey,
@ -309,6 +311,25 @@ export async function applyNonInteractiveAuthChoice(params: {
return applyVeniceConfig(nextConfig);
}
if (authChoice === "ovhcloud-api-key") {
const resolved = await resolveNonInteractiveApiKey({
provider: "ovhcloud",
cfg: baseConfig,
flagValue: opts.ovhcloudApiKey,
flagName: "--ovhcloud-api-key",
envVar: "OVHCLOUD_API_KEY",
runtime,
});
if (!resolved) return null;
if (resolved.source !== "profile") await setOvhcloudApiKey(resolved.key);
nextConfig = applyAuthProfileConfig(nextConfig, {
profileId: "ovhcloud:default",
provider: "ovhcloud",
mode: "api_key",
});
return applyOvhcloudConfig(nextConfig);
}
if (
authChoice === "minimax-cloud" ||
authChoice === "minimax-api" ||

View File

@ -17,6 +17,7 @@ export type AuthChoice =
| "kimi-code-api-key"
| "synthetic-api-key"
| "venice-api-key"
| "ovhcloud-api-key"
| "codex-cli"
| "apiKey"
| "gemini-api-key"
@ -70,6 +71,7 @@ export type OnboardOptions = {
minimaxApiKey?: string;
syntheticApiKey?: string;
veniceApiKey?: string;
ovhcloudApiKey?: string;
opencodeZenApiKey?: string;
gatewayPort?: number;
gatewayBind?: GatewayBind;