Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/atlascloud-provider.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@voltagent/core": patch
"create-voltagent-app": patch
---

Add Atlas Cloud as an OpenAI-compatible model provider.
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";

let createOpenAICompatibleCalls: unknown[][] = [];

vi.mock("@voltagent/internal", async () => {
const actual = await vi.importActual<typeof import("@voltagent/internal")>("@voltagent/internal");
return actual;
});

vi.mock("@ai-sdk/openai-compatible", () => ({
createOpenAICompatible: (...args: unknown[]) => {
createOpenAICompatibleCalls.push(args);
const mockModel = {
modelId: "mock-model",
specificationVersion: "v1",
provider: "atlascloud",
};
return {
languageModel: (modelId: string) => ({ ...mockModel, modelId }),
chatModel: (modelId: string) => ({ ...mockModel, modelId }),
};
},
}));

describe("Atlas Cloud provider registry", () => {
const originalEnv = { ...process.env };

beforeEach(() => {
createOpenAICompatibleCalls = [];
(globalThis as Record<string, unknown>).___voltagent_model_provider_registry = undefined;
process.env = { ...originalEnv };
});

afterEach(() => {
process.env = originalEnv;
(globalThis as Record<string, unknown>).___voltagent_model_provider_registry = undefined;
});

it("lists Atlas Cloud as a registered provider", async () => {
const { ModelProviderRegistry } = await import("./model-provider-registry");
const registry = ModelProviderRegistry.getInstance();

expect(registry.listProviders()).toContain("atlascloud");
});

it("loads Atlas Cloud through the OpenAI-compatible adapter", async () => {
process.env.ATLASCLOUD_API_KEY = "test-atlas-key";

const { ModelProviderRegistry } = await import("./model-provider-registry");
const registry = ModelProviderRegistry.getInstance();
const model = await registry.resolveLanguageModel("atlascloud/qwen/qwen3.5-flash");

expect(model).toMatchObject({
modelId: "qwen/qwen3.5-flash",
provider: "atlascloud",
});

expect(createOpenAICompatibleCalls).toHaveLength(1);
const config = createOpenAICompatibleCalls[0]?.[0] as Record<string, unknown>;
expect(config).toMatchObject({
name: "atlascloud",
baseURL: "https://api.atlascloud.ai/v1",
apiKey: "test-atlas-key",
supportsStructuredOutputs: true,
});
});

it("supports the conventional ATLASCLOUD_BASE_URL override", async () => {
process.env.ATLASCLOUD_API_KEY = "test-atlas-key";
process.env.ATLASCLOUD_BASE_URL = "https://atlas-proxy.example/v1";

const { ModelProviderRegistry } = await import("./model-provider-registry");
const registry = ModelProviderRegistry.getInstance();
await registry.resolveLanguageModel("atlascloud/deepseek-ai/deepseek-v4-pro");

const config = createOpenAICompatibleCalls[0]?.[0] as Record<string, unknown>;
expect(config.baseURL).toBe("https://atlas-proxy.example/v1");
});

it("throws a helpful error when ATLASCLOUD_API_KEY is not set", async () => {
const { ATLASCLOUD_API_KEY: _atlascloudApiKey, ...envWithoutAtlasCloud } = process.env;
process.env = envWithoutAtlasCloud;

const { ModelProviderRegistry } = await import("./model-provider-registry");
const registry = ModelProviderRegistry.getInstance();

await expect(registry.resolveLanguageModel("atlascloud/qwen/qwen3.5-flash")).rejects.toThrow(
/ATLASCLOUD_API_KEY/,
);
});
});
8 changes: 8 additions & 0 deletions packages/core/src/registries/model-provider-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,14 @@ const EXTRA_PROVIDER_REGISTRY: ModelProviderRegistryEntry[] = [
npm: "ollama-ai-provider-v2",
doc: "https://ollama.com",
},
{
id: "atlascloud",
name: "Atlas Cloud",
npm: "@ai-sdk/openai-compatible",
api: "https://api.atlascloud.ai/v1",
env: ["ATLASCLOUD_API_KEY"],
doc: "https://docs.atlascloud.ai",
},
{
id: "minimax",
name: "MiniMax",
Expand Down
17 changes: 16 additions & 1 deletion packages/create-voltagent-app/src/cli.integration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,13 @@ const scenarios: Scenario[] = [
aiProvider: "mistral",
ide: "none",
},
{
name: "atlascloud-hono-pnpm-none",
server: "hono",
packageManager: "pnpm",
aiProvider: "atlascloud",
ide: "none",
},
{
name: "ollama-elysia-bun-none",
server: "elysia",
Expand Down Expand Up @@ -317,7 +324,15 @@ describe.sequential("create-voltagent-app CLI option matrix", () => {
expect(serverCoverage).toEqual(new Set<ServerProvider>(["hono", "elysia"]));
expect(packageManagerCoverage).toEqual(new Set<PackageManager>(["pnpm", "bun", "yarn", "npm"]));
expect(providerCoverage).toEqual(
new Set<AIProvider>(["openai", "anthropic", "google", "groq", "mistral", "ollama"]),
new Set<AIProvider>([
"openai",
"anthropic",
"google",
"groq",
"mistral",
"atlascloud",
"ollama",
]),
);
expect(ideCoverage).toEqual(
new Set<NonNullable<ProjectOptions["ide"]>>(["none", "cursor", "windsurf", "vscode"]),
Expand Down
4 changes: 4 additions & 0 deletions packages/create-voltagent-app/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@ export const runCLI = async (): Promise<void> => {
{ name: `Google (${AI_PROVIDER_CONFIG.google.modelName})`, value: "google" },
{ name: `Groq (${AI_PROVIDER_CONFIG.groq.modelName})`, value: "groq" },
{ name: `Mistral (${AI_PROVIDER_CONFIG.mistral.modelName})`, value: "mistral" },
{
name: `Atlas Cloud (${AI_PROVIDER_CONFIG.atlascloud.modelName})`,
value: "atlascloud",
},
{ name: `Ollama (${AI_PROVIDER_CONFIG.ollama.modelName} - Local)`, value: "ollama" },
],
default: "openai",
Expand Down
16 changes: 15 additions & 1 deletion packages/create-voltagent-app/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
export type AIProvider = "openai" | "anthropic" | "google" | "groq" | "mistral" | "ollama";
export type AIProvider =
| "openai"
| "anthropic"
| "google"
| "groq"
| "mistral"
| "atlascloud"
| "ollama";
export type ServerProvider = "hono" | "elysia";
export type PackageManager = "npm" | "bun" | "yarn" | "pnpm";

Expand Down Expand Up @@ -57,6 +64,13 @@ export const AI_PROVIDER_CONFIG = {
modelName: "Mistral Large 2",
apiKeyUrl: "https://console.mistral.ai/api-keys",
},
atlascloud: {
name: "Atlas Cloud",
envVar: "ATLASCLOUD_API_KEY",
model: "atlascloud/qwen/qwen3.5-flash",
modelName: "Qwen3.5 Flash",
apiKeyUrl: "https://docs.atlascloud.ai",
},
ollama: {
name: "Ollama (Local)",
envVar: null,
Expand Down