diff --git a/extensions/void/src/common/LangaugeServerTest/createJsProgramGraph.ts b/extensions/void/LangaugeServerTest/createJsProgramGraph.ts similarity index 100% rename from extensions/void/src/common/LangaugeServerTest/createJsProgramGraph.ts rename to extensions/void/LangaugeServerTest/createJsProgramGraph.ts diff --git a/extensions/void/src/common/LangaugeServerTest/findFunctions.ts b/extensions/void/LangaugeServerTest/findFunctions.ts similarity index 100% rename from extensions/void/src/common/LangaugeServerTest/findFunctions.ts rename to extensions/void/LangaugeServerTest/findFunctions.ts diff --git a/extensions/void/src/common/llm/index.ts b/extensions/void/src/common/llm/index.ts deleted file mode 100644 index 577c24c3..00000000 --- a/extensions/void/src/common/llm/index.ts +++ /dev/null @@ -1,49 +0,0 @@ -// Import message sending functions for different LLM providers -import { sendAnthropicMsg } from './providers/anthropic' -import { sendGeminiMsg } from './providers/gemini' -import { sendOpenAIMsg } from './providers/openai' -import { sendOllamaMsg } from './providers/ollama' -import { sendGreptileMsg } from './providers/greptile' -import { LLMMessage, OnText, OnFinalMessage, AbortRef } from './types' -import { VoidConfig } from '../../webviews/common/contextForConfig' - -// Main function to send messages to LLM providers -export const sendLLMMessage = ({ - messages, - onText, - onFinalMessage, - onError, - voidConfig, - abortRef -}: { - messages: LLMMessage[], // Array of messages to send - onText: OnText, // Callback for receiving text chunks - onFinalMessage: (fullText: string) => void, // Callback for final message - onError: (error: string) => void, // Error handling callback - voidConfig: VoidConfig | null, // Configuration object - abortRef: AbortRef, // Reference for aborting requests -}) => { - // Return early if no config is provided - if (!voidConfig) return - - // Trim whitespace from all message contents - messages = messages.map(m => ({ ...m, content: m.content.trim() })) - - // Route message to appropriate provider based on configuration - switch (voidConfig.default.whichApi) { - case 'anthropic': - return sendAnthropicMsg({ messages, onText, onFinalMessage, onError, voidConfig, abortRef }) - case 'openAI': - case 'openRouter': - case 'openAICompatible': - return sendOpenAIMsg({ messages, onText, onFinalMessage, onError, voidConfig, abortRef }) - case 'gemini': - return sendGeminiMsg({ messages, onText, onFinalMessage, onError, voidConfig, abortRef }) - case 'ollama': - return sendOllamaMsg({ messages, onText, onFinalMessage, onError, voidConfig, abortRef }) - case 'greptile': - return sendGreptileMsg({ messages, onText, onFinalMessage, onError, voidConfig, abortRef }) - default: - onError(`Error: whichApi was ${voidConfig.default.whichApi}, which is not recognized!`) - } -} diff --git a/extensions/void/src/common/llm/providers/anthropic.ts b/extensions/void/src/common/llm/providers/anthropic.ts deleted file mode 100644 index 9a058c4f..00000000 --- a/extensions/void/src/common/llm/providers/anthropic.ts +++ /dev/null @@ -1,66 +0,0 @@ -import Anthropic from '@anthropic-ai/sdk' -import { SendLLMMessageParams, LLMMessageAnthropic } from '../types' -import { parseMaxTokensStr } from '../utils' - -export const sendAnthropicMsg = ({ - messages, - onText, - onFinalMessage, - onError, - voidConfig -}: SendLLMMessageParams) => { - const anthropic = new Anthropic({ - apiKey: voidConfig.anthropic.apikey, - dangerouslyAllowBrowser: true - }) - - // Combine system messages into a single string - const systemMessage = messages - .filter(msg => msg.role === 'system') - .map(msg => msg.content) - .join('\n') - - // Remove system messages and cast to Anthropic message type - const anthropicMessages = messages - .filter(msg => msg.role !== 'system') as LLMMessageAnthropic[] - - let did_abort = false - - const stream = anthropic.messages.stream({ - system: systemMessage, - messages: anthropicMessages, - model: voidConfig.anthropic.model, - max_tokens: parseMaxTokensStr(voidConfig.default.maxTokens)!, - }) - - // Handle streaming response - stream.on('text', (newText, fullText) => { - if (did_abort) return - onText(newText, fullText) - }) - - // Handle final message - stream.on('finalMessage', (response) => { - if (did_abort) return - const content = response.content - .map(c => c.type === 'text' ? c.text : '') - .join('\n') - onFinalMessage(content) - }) - - // Handle errors - stream.on('error', (error) => { - if (error instanceof Anthropic.APIError && error.status === 401) { - onError('Invalid API key.') - } else { - onError(error.message) - } - }) - - return { - abort: () => { - did_abort = true - stream.controller.abort() - } - } -} diff --git a/extensions/void/src/common/llm/providers/gemini.ts b/extensions/void/src/common/llm/providers/gemini.ts deleted file mode 100644 index fec2ab65..00000000 --- a/extensions/void/src/common/llm/providers/gemini.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { GoogleGenerativeAI, GoogleGenerativeAIFetchError } from '@google/generative-ai' -import { SendLLMMessageParams } from '../types' -import { parseMaxTokensStr } from '../utils' - -export const sendGeminiMsg = async ({ - messages, - onText, - onFinalMessage, - onError, - voidConfig, - abortRef -}: SendLLMMessageParams) => { - let didAbort = false - let fullText = '' - - abortRef.current = () => { - didAbort = true - } - - const genAI = new GoogleGenerativeAI(voidConfig.gemini.apikey) - const model = genAI.getGenerativeModel({ model: voidConfig.gemini.model }) - - // Get system messages and combine them - const systemMessage = messages - .filter(msg => msg.role === 'system') - .map(msg => msg.content) - .join('\n') - - // Convert messages to Gemini format - const geminiMessages = messages - .filter(msg => msg.role !== 'system') - .map(msg => ({ - parts: [{ text: msg.content }], - role: msg.role === 'assistant' ? 'model' : 'user' - })) - - try { - const response = await model.generateContentStream({ - contents: geminiMessages, - systemInstruction: systemMessage, - }) - - abortRef.current = () => { - didAbort = true - } - - for await (const chunk of response.stream) { - if (didAbort) return - const newText = chunk.text() - fullText += newText - onText(newText, fullText) - } - - onFinalMessage(fullText) - } catch (error) { - if (error instanceof GoogleGenerativeAIFetchError) { - if (error.status === 400) { - onError('Invalid API key.') - } else { - onError(`${error.name}:\n${error.message}`) - } - } else if (error instanceof Error) { - onError(error.toString()) - } else { - onError('Unknown error occurred') - } - } -} diff --git a/extensions/void/src/common/llm/providers/greptile.ts b/extensions/void/src/common/llm/providers/greptile.ts deleted file mode 100644 index 1f66d136..00000000 --- a/extensions/void/src/common/llm/providers/greptile.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { SendLLMMessageParams } from '../types' - -// Response type for Greptile API -type GreptileResponse = { - type: 'message' | 'sources' | 'status' - message: string | { - filepath: string - linestart: number | null - lineend: number | null - } | '' -} - -// Sends a message to Greptile API and handles the streaming response -export const sendGreptileMsg = ({ - messages, - onText, - onFinalMessage, - onError, - voidConfig, - abortRef -}: SendLLMMessageParams) => { - let didAbort = false - let fullText = '' - - // Set up abort handler - abortRef.current = () => { - didAbort = true - } - - // Make API request to Greptile - fetch('https://api.greptile.com/v2/query', { - method: 'POST', - headers: { - "Authorization": `Bearer ${voidConfig.greptile.apikey}`, - "X-Github-Token": `${voidConfig.greptile.githubPAT}`, - "Content-Type": `application/json`, - }, - body: JSON.stringify({ - messages, - stream: true, - repositories: [voidConfig.greptile.repoinfo], - }), - }) - .then(async response => { - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`) - } - // Parse the streaming response into JSON array - const text = await response.text() - return JSON.parse(`[${text.trim().split('\n').join(',')}]`) as GreptileResponse[] - }) - .then(async responseArr => { - if (didAbort) return - - // Process each response chunk - for (const response of responseArr) { - if (didAbort) break - - switch (response.type) { - case 'message': - // Handle message chunks - fullText += response.message as string - onText(response.message as string, fullText) - break - - case 'sources': { - // Handle source reference chunks - const sourceInfo = response.message as { - filepath: string - linestart: number | null - lineend: number | null - } - const sourceText = `\nSource: ${sourceInfo.filepath}${sourceInfo.linestart - ? ` (lines ${sourceInfo.linestart}-${sourceInfo.lineend})` - : '' - }\n` - fullText += sourceText - onText(sourceText, fullText) - break - } - - case 'status': - // Handle completion status - if (!response.message) { - onFinalMessage(fullText) - } - break - } - } - }) - .catch(error => { - // Handle any errors that occur during the request - const errorMessage = error instanceof Error - ? error.message - : 'An unknown error occurred' - onError(errorMessage) - }) -} diff --git a/extensions/void/src/common/llm/providers/ollama.ts b/extensions/void/src/common/llm/providers/ollama.ts deleted file mode 100644 index b9b548ce..00000000 --- a/extensions/void/src/common/llm/providers/ollama.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { Ollama } from 'ollama/browser' -import { SendLLMMessageParams } from '../types' -import { parseMaxTokensStr } from '../utils' - -/** - * Check if an Ollama model is installed - */ -async function checkModelExists(ollama: Ollama, modelName: string): Promise<{ - exists: boolean, - installedModels: string[] -}> { - const models = await ollama.list() - const installedModels = models.models.map(m => m.name.replace(/:latest$/, '')) - const exists = installedModels.some(m => m.startsWith(modelName)) - return { exists, installedModels } -} - -/** - * Build error message for when model is not found - */ -function buildModelNotFoundError(modelName: string, installedModels: string[]): string { - return [ - `The model "${modelName}" is not available locally.`, - `Please run 'ollama pull ${modelName}' to download it first`, - `or try selecting one from the installed models:`, - installedModels.join(', ') - ].join(' ') -} - -/** - * Implementation of Ollama chat functionality - */ -export const sendOllamaMsg = async ({ - messages, - onText, - onFinalMessage, - onError, - voidConfig, - abortRef -}: SendLLMMessageParams) => { - let didAbort = false - let fullText = "" - - // Set up abort handler - abortRef.current = () => { - didAbort = true - } - - try { - // Initialize Ollama client - const ollama = new Ollama({ - host: voidConfig.ollama.endpoint - }) - - // Check if model exists - const { exists, installedModels } = await checkModelExists( - ollama, - voidConfig.ollama.model - ) - - if (!exists) { - const errorMessage = buildModelNotFoundError( - voidConfig.ollama.model, - installedModels - ) - onText(errorMessage, errorMessage) - onFinalMessage(errorMessage) - return - } - - // Start streaming chat response - const stream = await ollama.chat({ - model: voidConfig.ollama.model, - messages, - stream: true, - options: { - num_predict: parseMaxTokensStr(voidConfig.default.maxTokens) - } - }) - - // Update abort handler - abortRef.current = () => { - didAbort = true - } - - // Handle streaming response - for await (const chunk of stream) { - if (didAbort) return - - const newText = chunk.message.content - fullText += newText - onText(newText, fullText) - } - - // Send final message - onFinalMessage(fullText) - - } catch (error) { - // Handle connection errors - if (error instanceof Error && error.message.includes('Failed to fetch')) { - const errorMessage = [ - 'Ollama service is not running.', - 'Please start the Ollama service and try again.' - ].join(' ') - onText(errorMessage, errorMessage) - onFinalMessage(errorMessage) - } - // Handle other errors - else if (error) { - onError(error.toString()) - } - } -} diff --git a/extensions/void/src/common/llm/providers/openai.ts b/extensions/void/src/common/llm/providers/openai.ts deleted file mode 100644 index 4bbdbb72..00000000 --- a/extensions/void/src/common/llm/providers/openai.ts +++ /dev/null @@ -1,103 +0,0 @@ -import OpenAI from 'openai' -import { SendLLMMessageParams } from '../types' -import { parseMaxTokensStr } from '../utils' - -export const sendOpenAIMsg = ({ - messages, - onText, - onFinalMessage, - onError, - voidConfig, - abortRef -}: SendLLMMessageParams) => { - let didAbort = false - let fullText = '' - - abortRef.current = () => { - didAbort = true - } - - let openai: OpenAI - let options: OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming - - const maxTokens = parseMaxTokensStr(voidConfig.default.maxTokens) - - // Configure OpenAI client based on API type - switch (voidConfig.default.whichApi) { - case 'openAI': - openai = new OpenAI({ - apiKey: voidConfig.openAI.apikey, - dangerouslyAllowBrowser: true - }) - options = { - model: voidConfig.openAI.model, - messages, - stream: true, - max_tokens: maxTokens - } - break - - case 'openRouter': - openai = new OpenAI({ - baseURL: "https://openrouter.ai/api/v1", - apiKey: voidConfig.openRouter.apikey, - dangerouslyAllowBrowser: true, - defaultHeaders: { - "HTTP-Referer": 'https://voideditor.com', - "X-Title": 'Void Editor', - } - }) - options = { - model: voidConfig.openRouter.model, - messages, - stream: true, - max_tokens: maxTokens - } - break - - case 'openAICompatible': - openai = new OpenAI({ - baseURL: voidConfig.openAICompatible.endpoint, - apiKey: voidConfig.openAICompatible.apikey, - dangerouslyAllowBrowser: true - }) - options = { - model: voidConfig.openAICompatible.model, - messages, - stream: true, - max_tokens: maxTokens - } - break - - default: - throw new Error(`Invalid whichApi: ${voidConfig.default.whichApi}`) - } - - openai.chat.completions - .create(options) - .then(async response => { - abortRef.current = () => { - didAbort = true - } - - for await (const chunk of response) { - if (didAbort) return - const newText = chunk.choices[0]?.delta?.content || '' - fullText += newText - onText(newText, fullText) - } - - onFinalMessage(fullText) - }) - .catch(error => { - if (error instanceof OpenAI.APIError) { - if (error.status === 401) { - onError('Invalid API key.') - } else { - onError(`${error.name}:\n${error.message}`) - } - } else { - onError(error) - } - }) -} diff --git a/extensions/void/src/common/llm/types.ts b/extensions/void/src/common/llm/types.ts deleted file mode 100644 index 8b9901b9..00000000 --- a/extensions/void/src/common/llm/types.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { VoidConfig } from '../../webviews/common/contextForConfig' - -export type AbortRef = { current: (() => void) | null } - -export type OnText = (newText: string, fullText: string) => void - -export type OnFinalMessage = (input: string) => void - -export type LLMMessageAnthropic = { - role: 'user' | 'assistant', - content: string, -} - -export type LLMMessage = { - role: 'system' | 'user' | 'assistant', - content: string, -} - -export type SendLLMMessageParams = { - messages: LLMMessage[], - onText: OnText, - onFinalMessage: OnFinalMessage, - onError: (error: string) => void, - voidConfig: VoidConfig, - abortRef: AbortRef, -} diff --git a/extensions/void/src/common/llm/utils.ts b/extensions/void/src/common/llm/utils.ts deleted file mode 100644 index 66d0f4df..00000000 --- a/extensions/void/src/common/llm/utils.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const parseMaxTokensStr = (maxTokensStr: string) => { - let int = isNaN(Number(maxTokensStr)) ? undefined : parseInt(maxTokensStr) - if (Number.isNaN(int)) - return undefined - return int -}