mirror of
https://github.com/voideditor/void
synced 2026-05-24 09:58:23 +00:00
OpenAICompatible improvements + misc refactor
This commit is contained in:
parent
d3beafec74
commit
e2f337f882
5 changed files with 55 additions and 82 deletions
|
|
@ -1 +1,8 @@
|
||||||
Please see the `CONTRIBUTING.md` for information on how to contribute :)!
|
Please see the `CONTRIBUTING.md` for information on how to contribute :)!
|
||||||
|
|
||||||
|
|
||||||
|
Here's an overview on how the extension works:
|
||||||
|
|
||||||
|
- The extension mounts in `extension.ts`.
|
||||||
|
|
||||||
|
- The Sidebar's HTML (everything in `sidebar/`) is built in React, and it's rendered by mounting a `<script>` tag - see `SidebarWebviewProvider.ts`.
|
||||||
|
|
|
||||||
|
|
@ -19,25 +19,25 @@
|
||||||
"void.whichApi": {
|
"void.whichApi": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "anthropic",
|
"default": "anthropic",
|
||||||
"description": "Choose an API provider",
|
"description": "Choose an API provider.",
|
||||||
"enum": [
|
"enum": [
|
||||||
"openai",
|
"openAI",
|
||||||
|
"openAICompatible",
|
||||||
"anthropic",
|
"anthropic",
|
||||||
"azure",
|
"azure",
|
||||||
"greptile",
|
|
||||||
"ollama",
|
"ollama",
|
||||||
"openaicompatible"
|
"greptile"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"void.anthropic.apiKey": {
|
"void.anthropic.apiKey": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "",
|
"default": "",
|
||||||
"description": "Anthropic API Key"
|
"description": "Anthropic API key."
|
||||||
},
|
},
|
||||||
"void.anthropic.model": {
|
"void.anthropic.model": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "claude-3-5-sonnet-20240620",
|
"default": "claude-3-5-sonnet-20240620",
|
||||||
"description": "Anthropic Model to use.",
|
"description": "Anthropic model to use.",
|
||||||
"enum": [
|
"enum": [
|
||||||
"claude-3-5-sonnet-20240620",
|
"claude-3-5-sonnet-20240620",
|
||||||
"claude-3-opus-20240229",
|
"claude-3-opus-20240229",
|
||||||
|
|
@ -47,7 +47,7 @@
|
||||||
},
|
},
|
||||||
"void.anthropic.maxTokens": {
|
"void.anthropic.maxTokens": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "1024",
|
"default": "8192",
|
||||||
"description": "Anthropic max number of tokens to output.",
|
"description": "Anthropic max number of tokens to output.",
|
||||||
"enum": [
|
"enum": [
|
||||||
"1024",
|
"1024",
|
||||||
|
|
@ -59,12 +59,12 @@
|
||||||
"void.openAI.apiKey": {
|
"void.openAI.apiKey": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "",
|
"default": "",
|
||||||
"description": "OpenAI API Key."
|
"description": "OpenAI API key."
|
||||||
},
|
},
|
||||||
"void.openAI.model": {
|
"void.openAI.model": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "gpt-4o",
|
"default": "gpt-4o",
|
||||||
"description": "OpenAI model.",
|
"description": "OpenAI model to use.",
|
||||||
"enum": [
|
"enum": [
|
||||||
"o1-preview",
|
"o1-preview",
|
||||||
"o1-mini",
|
"o1-mini",
|
||||||
|
|
@ -88,12 +88,12 @@
|
||||||
"void.greptile.apiKey": {
|
"void.greptile.apiKey": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "",
|
"default": "",
|
||||||
"description": "Greptile API Key."
|
"description": "Greptile API key."
|
||||||
},
|
},
|
||||||
"void.greptile.githubPAT": {
|
"void.greptile.githubPAT": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "",
|
"default": "",
|
||||||
"description": "Github PAT given to Greptile to access your repository."
|
"description": "Github PAT given to Greptile to access your repository."
|
||||||
},
|
},
|
||||||
"void.ollama.endpoint": {
|
"void.ollama.endpoint": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|
@ -193,20 +193,20 @@
|
||||||
"smollm:1.7b"
|
"smollm:1.7b"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"void.openaiCompatible.endpoint": {
|
"void.openAICompatible.endpoint": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "http://127.0.0.1:11434/v1",
|
"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"
|
"description": "The endpoint."
|
||||||
},
|
},
|
||||||
"void.openaiCompatible.model": {
|
"void.openAICompatible.model": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "gpt-4o",
|
||||||
|
"description": "The name of the model to use."
|
||||||
|
},
|
||||||
|
"void.openAICompatible.apiKey": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "",
|
"default": "",
|
||||||
"description": "Your provider's model name to use."
|
"description": "Your API key."
|
||||||
},
|
|
||||||
"void.openaiCompatible.apiKey": {
|
|
||||||
"type": "string",
|
|
||||||
"default": "",
|
|
||||||
"description": "Your provider's API Key."
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -41,12 +41,12 @@ export class SidebarWebviewProvider implements vscode.WebviewViewProvider {
|
||||||
|
|
||||||
private updateWebviewHTML(webview: vscode.Webview) {
|
private updateWebviewHTML(webview: vscode.Webview) {
|
||||||
const allowed_urls = ['https://api.anthropic.com', 'https://api.openai.com', 'https://api.greptile.com'];
|
const allowed_urls = ['https://api.anthropic.com', 'https://api.openai.com', 'https://api.greptile.com'];
|
||||||
const ollamaEndpoint: string | undefined = vscode.workspace.getConfiguration('void').get('ollama.endpoint');
|
const ollamaEndpoint: string | undefined = vscode.workspace.getConfiguration('void.ollama').get('endpoint');
|
||||||
if (ollamaEndpoint)
|
if (ollamaEndpoint)
|
||||||
allowed_urls.push(ollamaEndpoint);
|
allowed_urls.push(ollamaEndpoint);
|
||||||
const openaiCompatibleEndpoint: string | undefined = vscode.workspace.getConfiguration('void').get('openaiCompatible.endpoint');
|
const openAICompatibleEndpoint: string | undefined = vscode.workspace.getConfiguration('void.openAICompatible').get('endpoint');
|
||||||
if (openaiCompatibleEndpoint)
|
if (openAICompatibleEndpoint)
|
||||||
allowed_urls.push(openaiCompatibleEndpoint);
|
allowed_urls.push(openAICompatibleEndpoint);
|
||||||
|
|
||||||
const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'dist/sidebar/index.js'));
|
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'));
|
const stylesUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'dist/sidebar/styles.css'));
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ export type ApiConfig = {
|
||||||
model: string,
|
model: string,
|
||||||
maxTokens: string
|
maxTokens: string
|
||||||
},
|
},
|
||||||
openai: {
|
openAI: {
|
||||||
apikey: string,
|
apikey: string,
|
||||||
model: string,
|
model: string,
|
||||||
},
|
},
|
||||||
|
|
@ -28,7 +28,7 @@ export type ApiConfig = {
|
||||||
endpoint: string,
|
endpoint: string,
|
||||||
model: string
|
model: string
|
||||||
},
|
},
|
||||||
openaicompatible: {
|
openAICompatible: {
|
||||||
endpoint: string,
|
endpoint: string,
|
||||||
model: string,
|
model: string,
|
||||||
apikey: string
|
apikey: string
|
||||||
|
|
@ -109,7 +109,7 @@ const sendClaudeMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinal
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// OpenAI
|
// OpenAI and OpenAICompatible
|
||||||
const sendOpenAIMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, apiConfig }) => {
|
const sendOpenAIMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, apiConfig }) => {
|
||||||
|
|
||||||
let didAbort = false
|
let didAbort = false
|
||||||
|
|
@ -120,55 +120,22 @@ const sendOpenAIMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinal
|
||||||
didAbort = true;
|
didAbort = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const openai = new OpenAI({ apiKey: apiConfig.openai.apikey, dangerouslyAllowBrowser: true });
|
const openai = new OpenAI({ apiKey: apiConfig.openAI.apikey, dangerouslyAllowBrowser: true });
|
||||||
|
|
||||||
openai.chat.completions.create({
|
let options: OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming
|
||||||
model: apiConfig.openai.model,
|
if (apiConfig.whichApi === 'openAI') {
|
||||||
messages: messages,
|
options = { model: apiConfig.openAI.model, messages: messages, stream: true, }
|
||||||
stream: true,
|
}
|
||||||
})
|
else if (apiConfig.whichApi === 'openAICompatible') {
|
||||||
.then(async response => {
|
options = { model: apiConfig.openAICompatible.model, messages: messages, stream: true, }
|
||||||
abort = () => {
|
}
|
||||||
// response.controller.abort()
|
else {
|
||||||
didAbort = true;
|
console.error(`sendOpenAIMsg: invalid whichApi: ${apiConfig.whichApi}`)
|
||||||
}
|
throw new Error(`apiConfig.whichAPI was invalid: ${apiConfig.whichApi}`)
|
||||||
// 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 };
|
|
||||||
};
|
|
||||||
|
|
||||||
// OpenAI Compatible
|
openai.chat.completions
|
||||||
const sendOpenAICompatibleMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, apiConfig }) => {
|
.create(options)
|
||||||
|
|
||||||
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 => {
|
.then(async response => {
|
||||||
abort = () => {
|
abort = () => {
|
||||||
// response.controller.abort()
|
// response.controller.abort()
|
||||||
|
|
@ -317,14 +284,13 @@ export const sendLLMMessage: SendLLMMessageFnTypeExternal = ({ messages, onText,
|
||||||
switch (apiConfig.whichApi) {
|
switch (apiConfig.whichApi) {
|
||||||
case 'anthropic':
|
case 'anthropic':
|
||||||
return sendClaudeMsg({ messages, onText, onFinalMessage, apiConfig });
|
return sendClaudeMsg({ messages, onText, onFinalMessage, apiConfig });
|
||||||
case 'openai':
|
case 'openAI':
|
||||||
|
case 'openAICompatible':
|
||||||
return sendOpenAIMsg({ messages, onText, onFinalMessage, apiConfig });
|
return sendOpenAIMsg({ messages, onText, onFinalMessage, apiConfig });
|
||||||
case 'greptile':
|
case 'greptile':
|
||||||
return sendGreptileMsg({ messages, onText, onFinalMessage, apiConfig });
|
return sendGreptileMsg({ messages, onText, onFinalMessage, apiConfig });
|
||||||
case 'ollama':
|
case 'ollama':
|
||||||
return sendOllamaMsg({ messages, onText, onFinalMessage, apiConfig });
|
return sendOllamaMsg({ messages, onText, onFinalMessage, apiConfig });
|
||||||
case 'openaicompatible':
|
|
||||||
return sendOpenAICompatibleMsg({ messages, onText, onFinalMessage, apiConfig });
|
|
||||||
default:
|
default:
|
||||||
console.error(`Error: whichApi was ${apiConfig.whichApi}, which is not recognized!`);
|
console.error(`Error: whichApi was ${apiConfig.whichApi}, which is not recognized!`);
|
||||||
return { abort: () => { } }
|
return { abort: () => { } }
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ const getApiConfig = () => {
|
||||||
model: vscode.workspace.getConfiguration('void.anthropic').get('model') ?? '',
|
model: vscode.workspace.getConfiguration('void.anthropic').get('model') ?? '',
|
||||||
maxTokens: vscode.workspace.getConfiguration('void.anthropic').get('maxTokens') ?? '',
|
maxTokens: vscode.workspace.getConfiguration('void.anthropic').get('maxTokens') ?? '',
|
||||||
},
|
},
|
||||||
openai: {
|
openAI: {
|
||||||
apikey: vscode.workspace.getConfiguration('void.openAI').get('apiKey') ?? '',
|
apikey: vscode.workspace.getConfiguration('void.openAI').get('apiKey') ?? '',
|
||||||
model: vscode.workspace.getConfiguration('void.openAI').get('model') ?? '',
|
model: vscode.workspace.getConfiguration('void.openAI').get('model') ?? '',
|
||||||
},
|
},
|
||||||
|
|
@ -35,10 +35,10 @@ const getApiConfig = () => {
|
||||||
endpoint: vscode.workspace.getConfiguration('void.ollama').get('endpoint') ?? '',
|
endpoint: vscode.workspace.getConfiguration('void.ollama').get('endpoint') ?? '',
|
||||||
model: vscode.workspace.getConfiguration('void.ollama').get('model') ?? '',
|
model: vscode.workspace.getConfiguration('void.ollama').get('model') ?? '',
|
||||||
},
|
},
|
||||||
openaicompatible: {
|
openAICompatible: {
|
||||||
endpoint: vscode.workspace.getConfiguration('void.openaiCompatible').get('endpoint') ?? '',
|
endpoint: vscode.workspace.getConfiguration('void.openAICompatible').get('endpoint') ?? '',
|
||||||
apikey: vscode.workspace.getConfiguration('void.openaiCompatible').get('apiKey') ?? '',
|
apikey: vscode.workspace.getConfiguration('void.openAICompatible').get('apiKey') ?? '',
|
||||||
model: vscode.workspace.getConfiguration('void.openaiCompatible').get('model') ?? '',
|
model: vscode.workspace.getConfiguration('void.openAICompatible').get('model') ?? '',
|
||||||
},
|
},
|
||||||
whichApi: vscode.workspace.getConfiguration('void').get('whichApi') ?? ''
|
whichApi: vscode.workspace.getConfiguration('void').get('whichApi') ?? ''
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue