diff --git a/CHANGELOG.md b/CHANGELOG.md index 8809332bba..a55579d071 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Docs: https://docs.clawd.bot - Ollama: provider discovery + docs. (#1606) Thanks @abhaymundhara. https://docs.clawd.bot/providers/ollama ### Changes +- Models: add Venice AI provider support (onboarding, non-interactive auth, docs). (#1666) Thanks @jonisjongithub. https://docs.clawd.bot/providers/venice - Docs: expand FAQ (migration, scheduling, concurrency, model recommendations, OpenAI subscription auth, Pi sizing, hackable install, docs SSL workaround). - Docs: add verbose installer troubleshooting guidance. - Docs: update Fly.io guide notes. diff --git a/README.md b/README.md index a20c60f450..0cf30294a3 100644 --- a/README.md +++ b/README.md @@ -477,30 +477,31 @@ Special thanks to [Mario Zechner](https://mariozechner.at/) for his support and Thanks to all clawtributors:

- steipete bohdanpodvirnyi joaohlisboa mneves75 MatthieuBizien MaudeBot rahthakor vrknetha radek-paclt Tobias Bischoff - joshp123 mukhtharcm maxsumrall xadenryan juanpablodlc hsrvc magimetal meaningfool patelhiren NicholasSpisak - sebslight abhisekbasu1 zerone0x jamesgroat claude JustYannicc SocialNerd42069 Hyaxia dantelex daveonkels - Glucksberg google-labs-jules[bot] vignesh07 mteam88 Eng. Juan Combetto Mariano Belinky dbhurley TSavo julianengel benithors - bradleypriest timolins nachx639 pvoo sreekaransrinath gupsammy cristip73 stefangalescu nachoiacovino Vasanth Rao Naik Sabavat - iHildy cpojer lc0rp scald gumadeiras andranik-sahakyan davidguttman sleontenko rodrigouroz sircrumpet - peschee rafaelreis-r thewilloftheshadow ratulsarna lutr0 danielz1z emanuelst KristijanJovanovski CashWilliams rdev - osolmaz joshrad-dev kiranjd adityashaw2 sheeek artuskg onutc pauloportella tyler6204 neooriginal - manuelhettich minghinmatthewlam myfunc travisirby buddyh connorshea mcinteerj dependabot[bot] John-Rood timkrase - gerardward2007 obviyus tosh-hamburg azade-c roshanasingh4 bjesuiter cheeeee Josh Phillips pookNast Whoaa512 - YuriNachos chriseidhof dlauer robbyczgw-cla ysqander aj47 superman32432432 Takhoffman Yurii Chukhlib grp06 - antons austinm911 blacksmith-sh[bot] damoahdominic dan-dr HeimdallStrategy imfing jalehman jarvis-medmatic kkarimi - mahmoudashraf93 ngutman petter-b pkrmf RandyVentures Ryan Lisse dougvk erikpr1994 Ghost jonasjancarik - Keith the Silly Goose L36 Server Marc mitschabaude-bot mkbehr neist sibbl chrisrodz czekaj Friederike Seiler - gabriel-trigo iamadig Jonathan D. Rhyne (DJ-D) Kit koala73 manmal ogulcancelik pasogott petradonka rubyrunsstuff - siddhantjain suminhthanh svkozak VACInc wes-davis zats 24601 adam91holt ameno- Chris Taylor - Django Navarro evalexpr henrino3 humanwritten larlyssa odysseus0 oswalpalash pcty-nextgen-service-account Syhids Aaron Konyer - aaronveklabs andreabadesso cash-echo-bot Clawd ClawdFx erik-agens fcatuhe ivanrvpereira jayhickey jeffersonwarrior - jeffersonwarrior jverdi longmaba mickahouan mjrussell p6l-richard philipp-spiess robaxelsen Sash Catanzarite T5-AndyML - travisp VAC william arzt zknicker alejandro maza andrewting19 Andrii anpoirier Asleep123 bolismauro - conhecendoia Dimitrios Ploutarchos Drake Thomsen Evizero fal3 Felix Krause ganghyun kim gtsifrikas HazAT hrdwdmrbl + steipete bohdanpodvirnyi iHildy joaohlisboa mneves75 MatthieuBizien MaudeBot Glucksberg rahthakor vrknetha + radek-paclt Tobias Bischoff joshp123 czekaj mukhtharcm maxsumrall xadenryan rodrigouroz juanpablodlc hsrvc + magimetal meaningfool patelhiren NicholasSpisak sebslight jonisjongithub abhisekbasu1 zerone0x jamesgroat claude + JustYannicc tyler6204 SocialNerd42069 Hyaxia dantelex daveonkels google-labs-jules[bot] lc0rp vignesh07 mteam88 + Eng. Juan Combetto Mariano Belinky dbhurley TSavo julianengel bradleypriest benithors timolins nachx639 pvoo + sreekaransrinath gupsammy cristip73 stefangalescu nachoiacovino Vasanth Rao Naik Sabavat petter-b cpojer scald gumadeiras + andranik-sahakyan davidguttman sleontenko denysvitali sircrumpet peschee rafaelreis-r thewilloftheshadow ratulsarna lutr0 + danielz1z emanuelst KristijanJovanovski CashWilliams rdev osolmaz joshrad-dev kiranjd adityashaw2 sheeek + artuskg Takhoffman onutc pauloportella neooriginal manuelhettich minghinmatthewlam myfunc travisirby buddyh + connorshea kyleok mcinteerj dependabot[bot] John-Rood timkrase gerardward2007 obviyus roshanasingh4 tosh-hamburg + azade-c bjesuiter cheeeee Josh Phillips dlauer pookNast Whoaa512 YuriNachos chriseidhof robbyczgw-cla + ysqander aj47 superman32432432 Yurii Chukhlib grp06 ngutman antons austinm911 blacksmith-sh[bot] damoahdominic + dan-dr HeimdallStrategy imfing jalehman jarvis-medmatic kkarimi mahmoudashraf93 pkrmf RandyVentures Ryan Lisse + dougvk erikpr1994 Ghost jonasjancarik Keith the Silly Goose L36 Server Marc mitschabaude-bot mkbehr neist + sibbl chrisrodz Friederike Seiler gabriel-trigo iamadig Jonathan D. Rhyne (DJ-D) Kit koala73 manmal ogulcancelik + pasogott petradonka rubyrunsstuff siddhantjain suminhthanh svkozak VACInc wes-davis zats 24601 + adam91holt ameno- Chris Taylor Django Navarro evalexpr henrino3 humanwritten larlyssa odysseus0 oswalpalash + pcty-nextgen-service-account Syhids Aaron Konyer aaronveklabs andreabadesso Andrii cash-echo-bot Clawd ClawdFx erik-agens + Evizero fcatuhe itsjaydesu ivancasco ivanrvpereira jayhickey jeffersonwarrior jeffersonwarrior jverdi longmaba + mickahouan mjrussell odnxe p6l-richard philipp-spiess robaxelsen Sash Catanzarite T5-AndyML travisp VAC + william arzt zknicker abhaymundhara alejandro maza andrewting19 anpoirier arthyn Asleep123 bolismauro conhecendoia + dasilva333 Dimitrios Ploutarchos Drake Thomsen EnzeD fal3 Felix Krause ganghyun kim gtsifrikas HazAT hrdwdmrbl hugobarauna Jamie Openshaw Jarvis Jefferson Nunn Kevin Lin kitze levifig Lloyd loukotal martinpucik - Matt mini Miles mrdbstn MSch Mustafa Tag Eldeen ndraiman nexty5870 odnxe prathamdby ptn1411 - reeltimeapps RLTCmpe Rolf Fredheim Rony Kelner Samrat Jha shiv19 siraht snopoke testingabc321 The Admiral + Matt mini Miles mrdbstn MSch Mustafa Tag Eldeen ndraiman nexty5870 prathamdby ptn1411 reeltimeapps + RLTCmpe Rolf Fredheim Rony Kelner Samrat Jha sergical shiv19 siraht snopoke testingabc321 The Admiral thesash Ubuntu voidserf Vultr-Clawd Admin Wimmie wstock yazinsai Zach Knickerbocker Alphonse-arianee Azade carlulsoe ddyo Erik latitudeki5223 Manuel Maly Mourad Boustani odrobnik pcty-nextgen-ios-builder Quentin Randy Torres rhjoh ronak-guliani William Stock diff --git a/docs/providers/venice.md b/docs/providers/venice.md index d73eba6214..fa31b94334 100644 --- a/docs/providers/venice.md +++ b/docs/providers/venice.md @@ -1,3 +1,9 @@ +--- +summary: "Use Venice AI privacy-focused models in Clawdbot" +read_when: + - You want privacy-focused inference in Clawdbot + - You want Venice AI setup guidance +--- # Venice AI Provider Venice AI provides privacy-focused AI inference with support for uncensored models and access to major proprietary models through their anonymized proxy. All inference is private by default—no training on your data, no logging. @@ -20,6 +26,7 @@ Venice offers two privacy levels — understanding this is key to choosing your - **Streaming**: ✅ Supported on all models - **Function calling**: ✅ Supported on select models (check model capabilities) - **Vision**: ✅ Supported on models with vision capability +- **No hard rate limits**: Fair-use throttling may apply for extreme usage ## Setup @@ -54,8 +61,7 @@ This will: ```bash clawdbot onboard --non-interactive \ --auth-choice venice-api-key \ - --token "vapi_xxxxxxxxxxxx" \ - --token-provider venice + --venice-api-key "vapi_xxxxxxxxxxxx" ``` ### 3. Verify Setup @@ -84,6 +90,12 @@ List all available models: clawdbot models list | grep venice ``` +## Configure via `clawdbot configure` + +1. Run `clawdbot configure` +2. Select **Model/auth** +3. Choose **Venice AI** + ## Which Model Should I Use? | Use Case | Recommended Model | Why | @@ -202,6 +214,36 @@ The Venice model catalog updates dynamically. Run `clawdbot models list` to see Venice API is at `https://api.venice.ai/api/v1`. Ensure your network allows HTTPS connections. +## Config file example + +```json5 +{ + env: { VENICE_API_KEY: "vapi_..." }, + agents: { defaults: { model: { primary: "venice/llama-3.3-70b" } } }, + models: { + mode: "merge", + providers: { + venice: { + baseUrl: "https://api.venice.ai/api/v1", + apiKey: "${VENICE_API_KEY}", + api: "openai-completions", + models: [ + { + id: "llama-3.3-70b", + name: "Llama 3.3 70B", + reasoning: false, + input: ["text"], + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 131072, + maxTokens: 8192 + } + ] + } + } + } +} +``` + ## Links - [Venice AI](https://venice.ai) diff --git a/src/agents/models-config.providers.ts b/src/agents/models-config.providers.ts index 8304345956..996f09dd0b 100644 --- a/src/agents/models-config.providers.ts +++ b/src/agents/models-config.providers.ts @@ -12,12 +12,7 @@ import { SYNTHETIC_BASE_URL, SYNTHETIC_MODEL_CATALOG, } from "./synthetic-models.js"; -import { - buildVeniceModelDefinition, - discoverVeniceModels, - VENICE_BASE_URL, - VENICE_MODEL_CATALOG, -} from "./venice-models.js"; +import { discoverVeniceModels, VENICE_BASE_URL } from "./venice-models.js"; type ModelsConfig = NonNullable; export type ProviderConfig = NonNullable[string]; diff --git a/src/agents/models-config.skips-writing-models-json-no-env-token.test.ts b/src/agents/models-config.skips-writing-models-json-no-env-token.test.ts index 29ebe32659..716419bcaf 100644 --- a/src/agents/models-config.skips-writing-models-json-no-env-token.test.ts +++ b/src/agents/models-config.skips-writing-models-json-no-env-token.test.ts @@ -51,12 +51,14 @@ describe("models-config", () => { const previousMinimax = process.env.MINIMAX_API_KEY; const previousMoonshot = process.env.MOONSHOT_API_KEY; const previousSynthetic = process.env.SYNTHETIC_API_KEY; + const previousVenice = process.env.VENICE_API_KEY; delete process.env.COPILOT_GITHUB_TOKEN; delete process.env.GH_TOKEN; delete process.env.GITHUB_TOKEN; delete process.env.MINIMAX_API_KEY; delete process.env.MOONSHOT_API_KEY; delete process.env.SYNTHETIC_API_KEY; + delete process.env.VENICE_API_KEY; try { vi.resetModules(); @@ -85,6 +87,8 @@ describe("models-config", () => { else process.env.MOONSHOT_API_KEY = previousMoonshot; if (previousSynthetic === undefined) delete process.env.SYNTHETIC_API_KEY; else process.env.SYNTHETIC_API_KEY = previousSynthetic; + if (previousVenice === undefined) delete process.env.VENICE_API_KEY; + else process.env.VENICE_API_KEY = previousVenice; } }); }); @@ -172,4 +176,42 @@ describe("models-config", () => { } }); }); + + it("adds venice provider when VENICE_API_KEY is set", async () => { + await withTempHome(async () => { + vi.resetModules(); + const prevKey = process.env.VENICE_API_KEY; + const prevVitest = process.env.VITEST; + process.env.VENICE_API_KEY = "vapi-venice-test"; + process.env.VITEST = "1"; + try { + const { ensureClawdbotModelsJson } = await import("./models-config.js"); + const { resolveClawdbotAgentDir } = await import("./agent-paths.js"); + + await ensureClawdbotModelsJson({}); + + const modelPath = path.join(resolveClawdbotAgentDir(), "models.json"); + const raw = await fs.readFile(modelPath, "utf8"); + const parsed = JSON.parse(raw) as { + providers: Record< + string, + { + baseUrl?: string; + apiKey?: string; + models?: Array<{ id: string }>; + } + >; + }; + expect(parsed.providers.venice?.baseUrl).toBe("https://api.venice.ai/api/v1"); + expect(parsed.providers.venice?.apiKey).toBe("VENICE_API_KEY"); + const ids = parsed.providers.venice?.models?.map((model) => model.id); + expect(ids).toContain("llama-3.3-70b"); + } finally { + if (prevKey === undefined) delete process.env.VENICE_API_KEY; + else process.env.VENICE_API_KEY = prevKey; + if (prevVitest === undefined) delete process.env.VITEST; + else process.env.VITEST = prevVitest; + } + }); + }); }); diff --git a/src/agents/venice-models.ts b/src/agents/venice-models.ts index 25716cc51b..32bd2f93b9 100644 --- a/src/agents/venice-models.ts +++ b/src/agents/venice-models.ts @@ -340,7 +340,9 @@ export async function discoverVeniceModels(): Promise { }); if (!response.ok) { - console.warn(`[venice-models] Failed to discover models: HTTP ${response.status}, using static catalog`); + console.warn( + `[venice-models] Failed to discover models: HTTP ${response.status}, using static catalog`, + ); return VENICE_MODEL_CATALOG.map(buildVeniceModelDefinition); } @@ -351,7 +353,9 @@ export async function discoverVeniceModels(): Promise { } // Merge discovered models with catalog metadata - const catalogById = new Map(VENICE_MODEL_CATALOG.map((m) => [m.id, m])); + const catalogById = new Map( + VENICE_MODEL_CATALOG.map((m) => [m.id, m]), + ); const models: ModelDefinitionConfig[] = []; for (const apiModel of data.data) { diff --git a/src/cli/program.smoke.test.ts b/src/cli/program.smoke.test.ts index 3dc01fcb28..27cca4ef78 100644 --- a/src/cli/program.smoke.test.ts +++ b/src/cli/program.smoke.test.ts @@ -154,6 +154,12 @@ describe("cli program (smoke)", () => { key: "sk-synthetic-test", field: "syntheticApiKey", }, + { + authChoice: "venice-api-key", + flag: "--venice-api-key", + key: "vapi-venice-test", + field: "veniceApiKey", + }, { authChoice: "zai-api-key", flag: "--zai-api-key", diff --git a/src/cli/program/register.onboard.ts b/src/cli/program/register.onboard.ts index 281464b6f7..ee9d5ccd26 100644 --- a/src/cli/program/register.onboard.ts +++ b/src/cli/program/register.onboard.ts @@ -52,7 +52,7 @@ export function registerOnboardCommand(program: Command) { .option("--mode ", "Wizard mode: local|remote") .option( "--auth-choice ", - "Auth: setup-token|claude-cli|token|chutes|openai-codex|openai-api-key|openrouter-api-key|ai-gateway-api-key|moonshot-api-key|kimi-code-api-key|synthetic-api-key|codex-cli|gemini-api-key|zai-api-key|apiKey|minimax-api|minimax-api-lightning|opencode-zen|skip", + "Auth: setup-token|claude-cli|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|codex-cli|gemini-api-key|zai-api-key|apiKey|minimax-api|minimax-api-lightning|opencode-zen|skip", ) .option( "--token-provider ", @@ -74,6 +74,7 @@ export function registerOnboardCommand(program: Command) { .option("--zai-api-key ", "Z.AI API key") .option("--minimax-api-key ", "MiniMax API key") .option("--synthetic-api-key ", "Synthetic API key") + .option("--venice-api-key ", "Venice API key") .option("--opencode-zen-api-key ", "OpenCode Zen API key") .option("--gateway-port ", "Gateway port") .option("--gateway-bind ", "Gateway bind: loopback|tailnet|lan|auto|custom") @@ -123,6 +124,7 @@ export function registerOnboardCommand(program: Command) { zaiApiKey: opts.zaiApiKey as string | undefined, minimaxApiKey: opts.minimaxApiKey as string | undefined, syntheticApiKey: opts.syntheticApiKey as string | undefined, + veniceApiKey: opts.veniceApiKey as string | undefined, opencodeZenApiKey: opts.opencodeZenApiKey as string | undefined, gatewayPort: typeof gatewayPort === "number" && Number.isFinite(gatewayPort) diff --git a/src/commands/auth-choice-options.test.ts b/src/commands/auth-choice-options.test.ts index db529761fa..e49bdfe01f 100644 --- a/src/commands/auth-choice-options.test.ts +++ b/src/commands/auth-choice-options.test.ts @@ -127,6 +127,18 @@ describe("buildAuthChoiceOptions", () => { expect(options.some((opt) => opt.value === "synthetic-api-key")).toBe(true); }); + it("includes Venice auth choice", () => { + const store: AuthProfileStore = { version: 1, profiles: {} }; + const options = buildAuthChoiceOptions({ + store, + includeSkip: false, + includeClaudeCliIfMissing: true, + platform: "darwin", + }); + + expect(options.some((opt) => opt.value === "venice-api-key")).toBe(true); + }); + it("includes Chutes OAuth auth choice", () => { const store: AuthProfileStore = { version: 1, profiles: {} }; const options = buildAuthChoiceOptions({ diff --git a/src/commands/auth-choice-options.ts b/src/commands/auth-choice-options.ts index f13eef365e..2c46e05354 100644 --- a/src/commands/auth-choice-options.ts +++ b/src/commands/auth-choice-options.ts @@ -67,12 +67,6 @@ const AUTH_CHOICE_GROUP_DEFS: { hint: "Anthropic-compatible (multi-model)", choices: ["synthetic-api-key"], }, - { - value: "venice", - label: "Venice AI", - hint: "Privacy-focused (uncensored models)", - choices: ["venice-api-key"], - }, { value: "google", label: "Google", @@ -115,6 +109,12 @@ const AUTH_CHOICE_GROUP_DEFS: { hint: "API key", choices: ["opencode-zen"], }, + { + value: "venice", + label: "Venice AI", + hint: "Privacy-focused (uncensored models)", + choices: ["venice-api-key"], + }, ]; function formatOAuthHint(expires?: number, opts?: { allowStale?: boolean }): string { @@ -197,11 +197,6 @@ export function buildAuthChoiceOptions(params: { options.push({ value: "moonshot-api-key", label: "Moonshot AI API key" }); options.push({ value: "kimi-code-api-key", label: "Kimi Code API key" }); options.push({ value: "synthetic-api-key", label: "Synthetic API key" }); - options.push({ - value: "venice-api-key", - label: "Venice AI API key", - hint: "Privacy-focused inference (uncensored models)", - }); options.push({ value: "github-copilot", label: "GitHub Copilot (GitHub device login)", @@ -232,6 +227,11 @@ export function buildAuthChoiceOptions(params: { label: "OpenCode Zen (multi-model proxy)", hint: "Claude, GPT, Gemini via opencode.ai/zen", }); + options.push({ + value: "venice-api-key", + label: "Venice AI API key", + hint: "Privacy-focused inference (uncensored models)", + }); options.push({ value: "minimax-api", label: "MiniMax M2.1" }); options.push({ value: "minimax-api-lightning", diff --git a/src/commands/auth-choice.test.ts b/src/commands/auth-choice.test.ts index fbc434aa3a..a123dafbed 100644 --- a/src/commands/auth-choice.test.ts +++ b/src/commands/auth-choice.test.ts @@ -33,6 +33,7 @@ describe("applyAuthChoice", () => { const previousPiAgentDir = process.env.PI_CODING_AGENT_DIR; const previousOpenrouterKey = process.env.OPENROUTER_API_KEY; const previousAiGatewayKey = process.env.AI_GATEWAY_API_KEY; + const previousVeniceKey = process.env.VENICE_API_KEY; const previousSshTty = process.env.SSH_TTY; const previousChutesClientId = process.env.CHUTES_CLIENT_ID; let tempStateDir: string | null = null; @@ -69,6 +70,11 @@ describe("applyAuthChoice", () => { } else { process.env.AI_GATEWAY_API_KEY = previousAiGatewayKey; } + if (previousVeniceKey === undefined) { + delete process.env.VENICE_API_KEY; + } else { + process.env.VENICE_API_KEY = previousVeniceKey; + } if (previousSshTty === undefined) { delete process.env.SSH_TTY; } else { @@ -187,6 +193,59 @@ describe("applyAuthChoice", () => { expect(parsed.profiles?.["synthetic:default"]?.key).toBe("sk-synthetic-test"); }); + it("prompts and writes Venice API key when selecting venice-api-key", async () => { + tempStateDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-auth-")); + process.env.CLAWDBOT_STATE_DIR = tempStateDir; + process.env.CLAWDBOT_AGENT_DIR = path.join(tempStateDir, "agent"); + process.env.PI_CODING_AGENT_DIR = process.env.CLAWDBOT_AGENT_DIR; + + const text = vi.fn().mockResolvedValue("vapi-venice-test"); + const select: WizardPrompter["select"] = vi.fn( + async (params) => params.options[0]?.value as never, + ); + const multiselect: WizardPrompter["multiselect"] = vi.fn(async () => []); + const prompter: WizardPrompter = { + intro: vi.fn(noopAsync), + outro: vi.fn(noopAsync), + note: vi.fn(noopAsync), + select, + multiselect, + text, + confirm: vi.fn(async () => false), + progress: vi.fn(() => ({ update: noop, stop: noop })), + }; + const runtime: RuntimeEnv = { + log: vi.fn(), + error: vi.fn(), + exit: vi.fn((code: number) => { + throw new Error(`exit:${code}`); + }), + }; + + const result = await applyAuthChoice({ + authChoice: "venice-api-key", + config: {}, + prompter, + runtime, + setDefaultModel: true, + }); + + expect(text).toHaveBeenCalledWith( + expect.objectContaining({ message: "Enter Venice AI API key" }), + ); + expect(result.config.auth?.profiles?.["venice:default"]).toMatchObject({ + provider: "venice", + mode: "api_key", + }); + + const authProfilePath = authProfilePathFor(requireAgentDir()); + const raw = await fs.readFile(authProfilePath, "utf8"); + const parsed = JSON.parse(raw) as { + profiles?: Record; + }; + expect(parsed.profiles?.["venice:default"]?.key).toBe("vapi-venice-test"); + }); + it("sets default model when selecting github-copilot", async () => { tempStateDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-auth-")); process.env.CLAWDBOT_STATE_DIR = tempStateDir; diff --git a/src/commands/models/list.status-command.ts b/src/commands/models/list.status-command.ts index 8aa7015c8c..6f84fae33e 100644 --- a/src/commands/models/list.status-command.ts +++ b/src/commands/models/list.status-command.ts @@ -134,6 +134,7 @@ export async function modelsStatusCommand( "zai", "mistral", "synthetic", + "venice", ]; for (const provider of envProbeProviders) { if (resolveEnvApiKey(provider)) providersFromEnv.add(provider); diff --git a/src/commands/onboard-auth.test.ts b/src/commands/onboard-auth.test.ts index c87f4efeb0..ddb51774e6 100644 --- a/src/commands/onboard-auth.test.ts +++ b/src/commands/onboard-auth.test.ts @@ -15,9 +15,13 @@ import { applyOpenrouterProviderConfig, applySyntheticConfig, applySyntheticProviderConfig, + applyVeniceConfig, + applyVeniceProviderConfig, OPENROUTER_DEFAULT_MODEL_REF, SYNTHETIC_DEFAULT_MODEL_ID, SYNTHETIC_DEFAULT_MODEL_REF, + VENICE_DEFAULT_MODEL_ID, + VENICE_DEFAULT_MODEL_REF, setMinimaxApiKey, writeOAuthCredentials, } from "./onboard-auth.js"; @@ -343,6 +347,52 @@ describe("applySyntheticConfig", () => { }); }); +describe("applyVeniceConfig", () => { + it("adds venice provider with correct settings", () => { + const cfg = applyVeniceConfig({}); + expect(cfg.models?.providers?.venice).toMatchObject({ + baseUrl: "https://api.venice.ai/api/v1", + api: "openai-completions", + }); + }); + + it("sets correct primary model", () => { + const cfg = applyVeniceConfig({}); + expect(cfg.agents?.defaults?.model?.primary).toBe(VENICE_DEFAULT_MODEL_REF); + }); + + it("merges existing venice provider models", () => { + const cfg = applyVeniceProviderConfig({ + models: { + providers: { + venice: { + baseUrl: "https://old.example.com", + apiKey: "old-key", + api: "anthropic-messages", + models: [ + { + id: "old-model", + name: "Old", + reasoning: false, + input: ["text"], + cost: { input: 1, output: 2, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 1000, + maxTokens: 100, + }, + ], + }, + }, + }, + }); + expect(cfg.models?.providers?.venice?.baseUrl).toBe("https://api.venice.ai/api/v1"); + expect(cfg.models?.providers?.venice?.api).toBe("openai-completions"); + expect(cfg.models?.providers?.venice?.apiKey).toBe("old-key"); + const ids = cfg.models?.providers?.venice?.models.map((m) => m.id); + expect(ids).toContain("old-model"); + expect(ids).toContain(VENICE_DEFAULT_MODEL_ID); + }); +}); + describe("applyOpencodeZenProviderConfig", () => { it("adds allowlist entry for the default model", () => { const cfg = applyOpencodeZenProviderConfig({}); diff --git a/src/commands/onboard-non-interactive/local/auth-choice.ts b/src/commands/onboard-non-interactive/local/auth-choice.ts index 6762fb7d21..02e0a75b9f 100644 --- a/src/commands/onboard-non-interactive/local/auth-choice.ts +++ b/src/commands/onboard-non-interactive/local/auth-choice.ts @@ -20,6 +20,7 @@ import { applyOpencodeZenConfig, applyOpenrouterConfig, applySyntheticConfig, + applyVeniceConfig, applyVercelAiGatewayConfig, applyZaiConfig, setAnthropicApiKey, @@ -30,6 +31,7 @@ import { setOpencodeZenApiKey, setOpenrouterApiKey, setSyntheticApiKey, + setVeniceApiKey, setVercelAiGatewayApiKey, setZaiApiKey, } from "../../onboard-auth.js"; @@ -272,6 +274,25 @@ export async function applyNonInteractiveAuthChoice(params: { return applySyntheticConfig(nextConfig); } + if (authChoice === "venice-api-key") { + const resolved = await resolveNonInteractiveApiKey({ + provider: "venice", + cfg: baseConfig, + flagValue: opts.veniceApiKey, + flagName: "--venice-api-key", + envVar: "VENICE_API_KEY", + runtime, + }); + if (!resolved) return null; + if (resolved.source !== "profile") await setVeniceApiKey(resolved.key); + nextConfig = applyAuthProfileConfig(nextConfig, { + profileId: "venice:default", + provider: "venice", + mode: "api_key", + }); + return applyVeniceConfig(nextConfig); + } + if ( authChoice === "minimax-cloud" || authChoice === "minimax-api" ||