Added Ollama integration

This commit is contained in:
w1gs 2024-09-18 02:02:43 -04:00
parent a299cd58c7
commit f4ceb53738
4 changed files with 83 additions and 59 deletions

View file

@ -40,10 +40,15 @@
"default": "",
"description": "Greptile - Github PAT (gives Greptile access to your repo)"
},
"void.ollamaSettings": {
"void.ollamaSettings.endpoint": {
"type": "string",
"default": "",
"description": "Ollama settings (coming soon...)"
"description": "Ollama Endpoint - Local API server can be started with `OLLAMA_ORIGINS=* ollama serve`"
},
"void.ollamaSettings.model": {
"type": "string",
"default": "",
"description": "Ollama model to use"
}
}
},
@ -126,7 +131,6 @@
"eslint-plugin-react-hooks": "^4.6.2",
"globals": "^15.9.0",
"marked": "^14.1.0",
"ollama": "^0.5.8",
"postcss": "^8.4.41",
"react": "^18.3.1",
"react-dom": "^18.3.1",

View file

@ -54,8 +54,9 @@ export class SidebarWebviewProvider implements vscode.WebviewViewProvider {
const nonce = getNonce(); // only scripts with the nonce are allowed to run, this is a recommended security measure
const allowed_urls = ['https://api.anthropic.com', 'https://api.openai.com', 'https://api.greptile.com']
// Allow Ollama endpoint
const ollamaEndpoint = vscode.workspace.getConfiguration('void').get('ollamaSettings.endpoint') || 'http://localhost:11434'
const allowed_urls = ['https://api.anthropic.com', 'https://api.openai.com', 'https://api.greptile.com', ollamaEndpoint ]
webview.html = `<!DOCTYPE html>
<html lang="en">
<head>

View file

@ -1,8 +1,6 @@
import Anthropic from '@anthropic-ai/sdk';
import OpenAI from 'openai';
// import ollama from 'ollama'
export type ApiConfig = {
anthropic: {
apikey: string,
@ -20,7 +18,8 @@ export type ApiConfig = {
}
},
ollama: {
// TODO
endpoint: string,
model: string
},
whichApi: string
}
@ -220,66 +219,85 @@ const sendGreptileMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFin
export const sendLLMMessage: SendLLMMessageFnTypeExternal = ({ messages, onText, onFinalMessage, apiConfig }) => {
if (!apiConfig) return { abort: () => { } }
const whichApi = apiConfig.whichApi
const whichApi = apiConfig.whichApi;
if (whichApi === 'anthropic') {
return sendClaudeMsg({ messages, onText, onFinalMessage, apiConfig })
switch (whichApi) {
case 'anthropic':
return sendClaudeMsg({ messages, onText, onFinalMessage, apiConfig });
case 'openai':
return sendOpenAIMsg({ messages, onText, onFinalMessage, apiConfig });
case 'greptile':
return sendGreptileMsg({ messages, onText, onFinalMessage, apiConfig });
case 'ollama':
return sendOllamaMsg({ messages, onText, onFinalMessage, apiConfig });
default:
console.error(`Error: whichApi was ${whichApi}, which is not recognized!`);
return sendClaudeMsg({ messages, onText, onFinalMessage, apiConfig }); // TODO
}
else if (whichApi === 'openai') {
return sendOpenAIMsg({ messages, onText, onFinalMessage, apiConfig })
}
else if (whichApi === 'greptile') {
return sendGreptileMsg({ messages, onText, onFinalMessage, apiConfig })
}
else if (whichApi === 'ollama') {
return sendClaudeMsg({ messages, onText, onFinalMessage, apiConfig }) // TODO
}
else {
console.error(`Error: whichApi was ${whichApi}, which is not recognized!`)
return sendClaudeMsg({ messages, onText, onFinalMessage, apiConfig }) // TODO
}
}
// Ollama
// const sendOllamaMsg: sendMsgFnType = ({ messages, onText, onFinalMessage }) => {
export const sendOllamaMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, apiConfig }) => {
let didAbort = false;
let fullText = "";
// let did_abort = false
// let fullText = ''
// if abort is called, onFinalMessage is NOT called, and no later onTexts are called either
const abort = () => {
didAbort = true;
};
// // if abort is called, onFinalMessage is NOT called, and no later onTexts are called either
// let abort: () => void = () => {
// did_abort = true
// }
const handleError = (error: any) => {
console.error('Error:', error);
onFinalMessage(fullText);
};
// ollama.chat({ model: 'llama3.1', messages: messages, stream: true })
// .then(async response => {
if (apiConfig.ollama.endpoint.endsWith('/')) {
apiConfig.ollama.endpoint = apiConfig.ollama.endpoint.slice(0, -1);
}
// abort = () => {
// // response.abort() // this isn't needed now, to keep consistency with claude will leave it commented for now
// did_abort = true;
// }
fetch(`${apiConfig.ollama.endpoint}/api/chat`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
model: apiConfig.ollama.model,
messages: messages,
stream: true,
}),
})
.then(response => {
if (didAbort) return;
const reader = response.body?.getReader();
if (!reader) {
onFinalMessage(fullText);
return;
}
return reader;
})
.then(reader => {
if (!reader) return;
// // when receive text
// try {
// for await (const part of response) {
// if (did_abort) return
// let newText = part.message.content
// fullText += newText
// onText(newText, fullText)
// }
// }
// // when error/fail
// catch (e) {
// onFinalMessage(fullText)
// return
// }
const readStream = async () => {
try {
let done, value;
while ({ done, value } = await reader.read(), !done) {
if (didAbort) return;
const stringedResponse = new TextDecoder().decode(value);
const newText = JSON.parse(stringedResponse).message.content;
fullText += newText;
onText(newText, fullText);
}
onFinalMessage(fullText);
} catch (error) {
handleError(error);
}
};
// // when we get the final message on this stream
// onFinalMessage(fullText)
// })
// return { abort };
// };
readStream();
})
.catch(handleError);
return { abort };
};

View file

@ -25,7 +25,8 @@ const getApiConfig = () => {
}
},
ollama: {
// apikey: vscode.workspace.getConfiguration('void').get('ollamaSettings') ?? '',
endpoint: vscode.workspace.getConfiguration('void').get('ollamaSettings.endpoint') ?? '',
model: vscode.workspace.getConfiguration('void').get('ollamaSettings.model') ?? '',
},
whichApi: vscode.workspace.getConfiguration('void').get('whichApi') ?? ''
}