From ab5ec52f28dbce6e9a00c5369e3e012b74187d6f Mon Sep 17 00:00:00 2001 From: Brokenc0de Date: Fri, 26 Jun 2026 11:30:49 +0700 Subject: [PATCH] feat(providers): add Venice AI provider OpenAI-compatible apikey provider (chat/embedding/image) with dynamic model discovery via modelsFetcher + passthroughModels. No executor needed. Co-authored-by: Cursor --- open-sse/providers/registry/index.js | 52 +++++++++++++------------ open-sse/providers/registry/venice.js | 56 +++++++++++++++++++++++++++ tests/unit/venice-provider.test.js | 41 ++++++++++++++++++++ 3 files changed, 124 insertions(+), 25 deletions(-) create mode 100644 open-sse/providers/registry/venice.js create mode 100644 tests/unit/venice-provider.test.js diff --git a/open-sse/providers/registry/index.js b/open-sse/providers/registry/index.js index c9dca60d..d0a2fe57 100644 --- a/open-sse/providers/registry/index.js +++ b/open-sse/providers/registry/index.js @@ -1,6 +1,6 @@ // Auto-generated: static imports of all registry entries -import p0 from "./alicode.js"; -import p1 from "./alicode-intl.js"; +import p0 from "./alicode-intl.js"; +import p1 from "./alicode.js"; import p2 from "./anthropic.js"; import p3 from "./antigravity.js"; import p4 from "./assemblyai.js"; @@ -31,12 +31,12 @@ import p28 from "./exa.js"; import p29 from "./fal-ai.js"; import p30 from "./firecrawl.js"; import p31 from "./fireworks.js"; -import p32 from "./gemini.js"; -import p33 from "./gemini-cli.js"; +import p32 from "./gemini-cli.js"; +import p33 from "./gemini.js"; import p34 from "./github.js"; import p35 from "./gitlab.js"; -import p36 from "./glm.js"; -import p37 from "./glm-cn.js"; +import p36 from "./glm-cn.js"; +import p37 from "./glm.js"; import p38 from "./google-pse.js"; import p39 from "./google-tts.js"; import p40 from "./grok-web.js"; @@ -48,27 +48,27 @@ import p45 from "./inworld.js"; import p46 from "./jina-ai.js"; import p47 from "./jina-reader.js"; import p48 from "./kilocode.js"; -import p49 from "./kimi.js"; -import p50 from "./kimi-coding.js"; +import p49 from "./kimi-coding.js"; +import p50 from "./kimi.js"; import p51 from "./kiro.js"; import p52 from "./linkup.js"; import p53 from "./local-device.js"; import p54 from "./mimo-free.js"; -import p55 from "./minimax.js"; -import p56 from "./minimax-cn.js"; +import p55 from "./minimax-cn.js"; +import p56 from "./minimax.js"; import p57 from "./mistral.js"; import p58 from "./mmf.js"; import p59 from "./nanobanana.js"; import p60 from "./nebius.js"; import p61 from "./nvidia.js"; -import p62 from "./ollama.js"; -import p63 from "./ollama-local.js"; +import p62 from "./ollama-local.js"; +import p63 from "./ollama.js"; import p64 from "./openai.js"; -import p65 from "./opencode.js"; -import p66 from "./opencode-go.js"; +import p65 from "./opencode-go.js"; +import p66 from "./opencode.js"; import p67 from "./openrouter.js"; -import p68 from "./perplexity.js"; -import p69 from "./perplexity-web.js"; +import p68 from "./perplexity-web.js"; +import p69 from "./perplexity.js"; import p70 from "./playht.js"; import p71 from "./qoder.js"; import p72 from "./qwen.js"; @@ -84,15 +84,16 @@ import p81 from "./tavily.js"; import p82 from "./together.js"; import p83 from "./topaz.js"; import p84 from "./tortoise.js"; -import p85 from "./vercel-ai-gateway.js"; -import p86 from "./vertex.js"; +import p85 from "./venice.js"; +import p86 from "./vercel-ai-gateway.js"; import p87 from "./vertex-partner.js"; -import p88 from "./volcengine-ark.js"; -import p89 from "./voyage-ai.js"; -import p90 from "./xai.js"; -import p91 from "./xiaomi-mimo.js"; -import p92 from "./xiaomi-tokenplan.js"; -import p93 from "./youcom.js"; +import p88 from "./vertex.js"; +import p89 from "./volcengine-ark.js"; +import p90 from "./voyage-ai.js"; +import p91 from "./xai.js"; +import p92 from "./xiaomi-mimo.js"; +import p93 from "./xiaomi-tokenplan.js"; +import p94 from "./youcom.js"; export default [ p0, @@ -188,5 +189,6 @@ export default [ p90, p91, p92, - p93 + p93, + p94 ]; diff --git a/open-sse/providers/registry/venice.js b/open-sse/providers/registry/venice.js new file mode 100644 index 00000000..cd686461 --- /dev/null +++ b/open-sse/providers/registry/venice.js @@ -0,0 +1,56 @@ +export default { + id: "venice", + priority: 115, + alias: "venice", + aliases: [ + "vn", + ], + uiAlias: "venice", + display: { + name: "Venice AI", + icon: "shield", + color: "#DC2626", + textIcon: "VE", + website: "https://venice.ai", + notice: { + text: "OpenAI-compatible. Private inference + uncensored models (Venice Uncensored, GLM, Qwen, DeepSeek, Llama).", + apiKeyUrl: "https://venice.ai/settings/api", + }, + }, + category: "apikey", + transport: { + baseUrl: "https://api.venice.ai/api/v1/chat/completions", + validateUrl: "https://api.venice.ai/api/v1/models", + thinkingFormat: "openai", + }, + // Curated seed; the full live catalogue (90+ text models) is fetched via + // modelsFetcher and any other id is accepted via passthroughModels. + models: [ + { id: "venice-uncensored-1-2", name: "Venice Uncensored 1.2" }, + { id: "zai-org-glm-5", name: "GLM-5" }, + { id: "qwen3-235b-a22b-instruct-2507", name: "Qwen3 235B A22B Instruct" }, + { id: "qwen3-coder-480b-a35b-instruct-turbo", name: "Qwen3 Coder 480B A35B Turbo" }, + { id: "qwen3-vl-235b-a22b", name: "Qwen3 VL 235B A22B" }, + { id: "deepseek-v4-pro", name: "DeepSeek V4 Pro" }, + { id: "llama-3.3-70b", name: "Llama 3.3 70B" }, + { id: "hermes-3-llama-3.1-405b", name: "Hermes 3 Llama 3.1 405B" }, + { id: "mistral-small-3-2-24b-instruct", name: "Mistral Small 3.2 24B" }, + { id: "text-embedding-3-large", name: "Text Embedding 3 Large", kind: "embedding" }, + { id: "text-embedding-bge-m3", name: "BGE-M3 Embedding", kind: "embedding" }, + { id: "text-embedding-qwen3-8b", name: "Qwen3 8B Embedding", kind: "embedding" }, + { id: "venice-sd35", name: "Venice SD3.5", params: ["n", "size"], kind: "image" }, + { id: "flux-2-pro", name: "FLUX.2 Pro", params: ["n", "size"], kind: "image" }, + { id: "gpt-image-2", name: "GPT Image 2 (via Venice)", params: ["n", "size", "quality"], kind: "image" }, + ], + serviceKinds: ["llm", "embedding", "image"], + embeddingConfig: { + baseUrl: "https://api.venice.ai/api/v1/embeddings", + authType: "apikey", + authHeader: "bearer", + }, + imageConfig: { + baseUrl: "https://api.venice.ai/api/v1/images/generations", + }, + modelsFetcher: { url: "https://api.venice.ai/api/v1/models", type: "openai" }, + passthroughModels: true, +}; diff --git a/tests/unit/venice-provider.test.js b/tests/unit/venice-provider.test.js new file mode 100644 index 00000000..d31a0ae9 --- /dev/null +++ b/tests/unit/venice-provider.test.js @@ -0,0 +1,41 @@ +import { describe, expect, it } from "vitest"; + +import REGISTRY from "../../open-sse/providers/registry/index.js"; +import { PROVIDERS, PROVIDER_MODELS } from "../../open-sse/providers/index.js"; + +describe("Venice AI provider", () => { + const venice = REGISTRY.find((e) => e.id === "venice"); + + it("is registered as an OpenAI-compatible apikey provider", () => { + expect(venice).toBeDefined(); + expect(venice.category).toBe("apikey"); + expect(venice.transport.baseUrl).toBe("https://api.venice.ai/api/v1/chat/completions"); + expect(venice.alias).toBe("venice"); + expect(venice.aliases).toContain("vn"); + }); + + it("enables dynamic model discovery and passthrough", () => { + expect(venice.passthroughModels).toBe(true); + expect(venice.modelsFetcher).toMatchObject({ + url: "https://api.venice.ai/api/v1/models", + type: "openai", + }); + }); + + it("builds into the runtime PROVIDERS map with the openai format default", () => { + expect(PROVIDERS.venice).toBeDefined(); + expect(PROVIDERS.venice.format).toBe("openai"); + expect(PROVIDERS.venice.baseUrl).toBe("https://api.venice.ai/api/v1/chat/completions"); + }); + + it("exposes its seed models (incl. the signature uncensored model)", () => { + const ids = (PROVIDER_MODELS.venice || []).map((m) => m.id); + expect(ids.length).toBeGreaterThan(0); + expect(ids).toContain("venice-uncensored-1-2"); + }); + + it("keeps every registry id unique after adding venice", () => { + const ids = REGISTRY.map((e) => e.id); + expect(new Set(ids).size).toBe(ids.length); + }); +});