diff --git a/README.md b/README.md index 1c2aedc..d7d6576 100644 --- a/README.md +++ b/README.md @@ -157,7 +157,7 @@ Get CodeVisualizer up and running in your VS Code environment in just a few clic 3. **Configure AI Features (Optional)** - Open Settings: `Ctrl+,` (Windows/Linux) or `Cmd+,` (Mac) - Search for "CodeVisualizer" - - Enable AI labels and add your API key for supported providers + - Enable AI labels and add your API key for supported providers, including Atlas Cloud - Or use Ollama for completely local AI processing 4. **Start Visualizing** diff --git a/package.json b/package.json index 5d70741..b9eb8be 100644 --- a/package.json +++ b/package.json @@ -258,6 +258,7 @@ "type": "string", "enum": [ "openai", + "atlascloud", "gemini", "groq", "ollama", diff --git a/src/core/llm/LLMManager.ts b/src/core/llm/LLMManager.ts index 7983664..f668939 100644 --- a/src/core/llm/LLMManager.ts +++ b/src/core/llm/LLMManager.ts @@ -38,6 +38,7 @@ export class LLMManager { const providerPick = await vscode.window.showQuickPick( [ { label: "OpenAI", value: "openai" }, + { label: "Atlas Cloud", value: "atlascloud" }, { label: "Gemini", value: "gemini" }, { label: "Groq", value: "groq" }, { label: "Ollama (local)", value: "ollama" }, diff --git a/src/core/llm/LLMService.ts b/src/core/llm/LLMService.ts index 4ab5f15..2d76c3a 100644 --- a/src/core/llm/LLMService.ts +++ b/src/core/llm/LLMService.ts @@ -1,7 +1,7 @@ import * as crypto from "crypto"; import { logInfo, logWarn, logError } from "./LLMLogger"; -export type Provider = "openai" | "gemini" | "groq" | "ollama"; +export type Provider = "openai" | "atlascloud" | "gemini" | "groq" | "ollama"; export interface TranslateParams { mermaidSource: string; @@ -19,6 +19,8 @@ export class LLMService { switch (provider) { case "openai": return ["gpt-4o-mini", "gpt-4o", "o3-mini"]; + case "atlascloud": + return ["openai/gpt-4.1-mini"]; case "gemini": return ["gemini-1.5-flash", "gemini-1.5-pro"]; case "groq": @@ -266,6 +268,17 @@ async function callProvider( expectedCount, labels ); + case "atlascloud": + return await callOpenAI( + model, + apiKey, + systemPrompt, + userPrompt, + expectedCount, + labels, + "https://api.atlascloud.ai/v1/chat/completions", + "Atlas Cloud" + ); case "gemini": return await callGemini( model, @@ -439,6 +452,8 @@ async function callOpenAI( userPrompt: string, expectedCount: number, referenceLabels: string[], + endpoint = "https://api.openai.com/v1/chat/completions", + providerName = "OpenAI", ): Promise { try { const bodyPayload = { @@ -450,7 +465,7 @@ async function callOpenAI( { role: "user", content: userPrompt }, ], }; - const res = await fetch("https://api.openai.com/v1/chat/completions", { + const res = await fetch(endpoint, { method: "POST", headers: { "Content-Type": "application/json", @@ -461,7 +476,7 @@ async function callOpenAI( if (!res.ok) { const text = await safeReadBody(res); logWarn( - `OpenAI responded with ${res.status} ${res.statusText}. Body snippet: ${text.substring( + `${providerName} responded with ${res.status} ${res.statusText}. Body snippet: ${text.substring( 0, 500 )}` @@ -471,7 +486,7 @@ async function callOpenAI( const data: unknown = await res.json(); if (!isOpenAIChatCompletionResponse(data)) { logWarn( - `OpenAI response shape unexpected: ${JSON.stringify(data).substring( + `${providerName} response shape unexpected: ${JSON.stringify(data).substring( 0, 500 )}` @@ -483,13 +498,13 @@ async function callOpenAI( ? data.choices[0].message.content : undefined; if (!content) { - logWarn("OpenAI response did not contain message content"); + logWarn(`${providerName} response did not contain message content`); return null; } const parsed = parseLabelsJsonText(content, expectedCount, referenceLabels); if (!parsed) { logWarn( - `OpenAI output could not be parsed as JSON array. Content: ${content.substring( + `${providerName} output could not be parsed as JSON array. Content: ${content.substring( 0, 500 )}` @@ -498,7 +513,7 @@ async function callOpenAI( return parsed; } catch (err) { logError( - `OpenAI fetch error: ${err instanceof Error ? err.message : String(err)}`, + `${providerName} fetch error: ${err instanceof Error ? err.message : String(err)}`, ); return null; } @@ -966,4 +981,4 @@ function sanitizeLabel(label: string): string { s = s.slice(0, maxLen - 1).trimEnd() + "…"; } return s; -} \ No newline at end of file +}