OpenAICompatible improvements + misc refactor

This commit is contained in:
Andrew 2024-10-11 15:02:43 -07:00
parent d3beafec74
commit e2f337f882
5 changed files with 55 additions and 82 deletions

View file

@ -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`.

View file

@ -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."
} }
} }
}, },

View file

@ -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'));

View file

@ -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: () => { } }

View file

@ -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') ?? ''
} }