diff --git a/extensions/void/package.json b/extensions/void/package.json index 5fdf0bdc..e6663fe0 100644 --- a/extensions/void/package.json +++ b/extensions/void/package.json @@ -25,7 +25,8 @@ "anthropic", "azure", "greptile", - "ollama" + "ollama", + "openaicompatible" ] }, "void.anthropic.apiKey": { @@ -191,6 +192,21 @@ "smollm:360m", "smollm:1.7b" ] + }, + "void.openaiCompatible.endpoint": { + "type": "string", + "default": "http://127.0.0.1:11434/v1", + "description": "The openai compatible api provider's endpoint. default value is a example of the ollama openai-mode uri" + }, + "void.openaiCompatible.model": { + "type": "string", + "default": "", + "description": "Your provider's model name to use." + }, + "void.openaiCompatible.apiKey": { + "type": "string", + "default": "", + "description": "Your provider's API Key." } } }, diff --git a/extensions/void/src/SidebarWebviewProvider.ts b/extensions/void/src/SidebarWebviewProvider.ts index e7baa51c..1173fa1e 100644 --- a/extensions/void/src/SidebarWebviewProvider.ts +++ b/extensions/void/src/SidebarWebviewProvider.ts @@ -44,6 +44,9 @@ export class SidebarWebviewProvider implements vscode.WebviewViewProvider { const ollamaEndpoint: string | undefined = vscode.workspace.getConfiguration('void').get('ollama.endpoint'); if (ollamaEndpoint) allowed_urls.push(ollamaEndpoint); + const openaiCompatibleEndpoint: string | undefined = vscode.workspace.getConfiguration('void').get('openaiCompatible.endpoint'); + if (openaiCompatibleEndpoint) + allowed_urls.push(openaiCompatibleEndpoint); const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'dist/sidebar/index.js')); const stylesUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'dist/sidebar/styles.css')); diff --git a/extensions/void/src/common/sendLLMMessage.ts b/extensions/void/src/common/sendLLMMessage.ts index 8f651de2..d49f39d9 100644 --- a/extensions/void/src/common/sendLLMMessage.ts +++ b/extensions/void/src/common/sendLLMMessage.ts @@ -28,6 +28,11 @@ export type ApiConfig = { endpoint: string, model: string }, + openaicompatible: { + endpoint: string, + model: string, + apikey: string + } whichApi: string } @@ -146,6 +151,47 @@ const sendOpenAIMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinal return { abort }; }; +// OpenAI Compatible +const sendOpenAICompatibleMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, apiConfig }) => { + + let didAbort = false + let fullText = '' + + // if abort is called, onFinalMessage is NOT called, and no later onTexts are called either + let abort: () => void = () => { + didAbort = true; + }; + + const openai = new OpenAI({ apiKey: apiConfig.openaicompatible.apikey, baseURL: apiConfig.openaicompatible.endpoint, dangerouslyAllowBrowser: true }); + + openai.chat.completions.create({ + model: apiConfig.openaicompatible.model, + messages: messages, + stream: true, + }) + .then(async response => { + abort = () => { + // response.controller.abort() + didAbort = true; + } + // when receive text + try { + for await (const chunk of response) { + if (didAbort) return; + const newText = chunk.choices[0]?.delta?.content || ''; + fullText += newText; + onText(newText, fullText); + } + onFinalMessage(fullText); + } + // when error/fail + catch (error) { + console.error('Error in OpenAI stream:', error); + onFinalMessage(fullText); + } + }) + return { abort }; +}; // Ollama @@ -277,6 +323,8 @@ export const sendLLMMessage: SendLLMMessageFnTypeExternal = ({ messages, onText, return sendGreptileMsg({ messages, onText, onFinalMessage, apiConfig }); case 'ollama': return sendOllamaMsg({ messages, onText, onFinalMessage, apiConfig }); + case 'openaicompatible': + return sendOpenAICompatibleMsg({ messages, onText, onFinalMessage, apiConfig }); default: console.error(`Error: whichApi was ${apiConfig.whichApi}, which is not recognized!`); return { abort: () => { } } diff --git a/extensions/void/src/extension.ts b/extensions/void/src/extension.ts index 20630248..262953ec 100644 --- a/extensions/void/src/extension.ts +++ b/extensions/void/src/extension.ts @@ -35,6 +35,11 @@ const getApiConfig = () => { endpoint: vscode.workspace.getConfiguration('void.ollama').get('endpoint') ?? '', model: vscode.workspace.getConfiguration('void.ollama').get('model') ?? '', }, + openaicompatible: { + endpoint: vscode.workspace.getConfiguration('void.openaiCompatible').get('endpoint') ?? '', + apikey: vscode.workspace.getConfiguration('void.openaiCompatible').get('apiKey') ?? '', + model: vscode.workspace.getConfiguration('void.openaiCompatible').get('model') ?? '', + }, whichApi: vscode.workspace.getConfiguration('void').get('whichApi') ?? '' } return apiConfig