mirror of
https://github.com/voideditor/void
synced 2026-05-24 01:48:25 +00:00
Gemini added
This commit is contained in:
parent
0f0719c596
commit
0c8a185df7
4 changed files with 105 additions and 10 deletions
20
extensions/void/package-lock.json
generated
20
extensions/void/package-lock.json
generated
|
|
@ -7,6 +7,9 @@
|
|||
"": {
|
||||
"name": "void",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"@google/generative-ai": "^0.21.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@anthropic-ai/sdk": "^0.29.2",
|
||||
"@eslint/js": "^9.9.1",
|
||||
|
|
@ -737,6 +740,15 @@
|
|||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@google/generative-ai": {
|
||||
"version": "0.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.21.0.tgz",
|
||||
"integrity": "sha512-7XhUbtnlkSEZK15kN3t+tzIMxsbKm/dSkKBFalj+20NvPKe1kBY7mR2P7vuijEn+f06z5+A8bVGKO0v39cr6Wg==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanwhocodes/config-array": {
|
||||
"version": "0.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
|
||||
|
|
@ -6013,6 +6025,14 @@
|
|||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/monaco-editor": {
|
||||
"version": "0.52.0",
|
||||
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.52.0.tgz",
|
||||
"integrity": "sha512-OeWhNpABLCeTqubfqLMXGsqf6OmPU6pHM85kF3dhy6kq5hnhuVS1p3VrEW/XhWHc71P2tHyS5JFySD8mgs1crw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
|
|
|
|||
|
|
@ -151,5 +151,8 @@
|
|||
"typescript": "5.5.4",
|
||||
"typescript-eslint": "^8.3.0",
|
||||
"uuid": "^10.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@google/generative-ai": "^0.21.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import Anthropic from '@anthropic-ai/sdk';
|
||||
import OpenAI from 'openai';
|
||||
import { Ollama } from 'ollama/browser'
|
||||
import { GoogleGenerativeAI } from '@google/generative-ai';
|
||||
import { VoidConfig } from '../webviews/common/contextForConfig'
|
||||
|
||||
export type AbortRef = { current: (() => void) | null }
|
||||
|
|
@ -37,8 +38,6 @@ type SendLLMMessageFnTypeExternal = (params: {
|
|||
abortRef: AbortRef,
|
||||
}) => void
|
||||
|
||||
|
||||
|
||||
const parseMaxTokensStr = (maxTokensStr: string) => {
|
||||
// parse the string but only if the full string is a valid number, eg parseInt('100abc') should return NaN
|
||||
let int = isNaN(Number(maxTokensStr)) ? undefined : parseInt(maxTokensStr)
|
||||
|
|
@ -103,8 +102,72 @@ const sendAnthropicMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFi
|
|||
return { abort }
|
||||
};
|
||||
|
||||
// Gemini
|
||||
const sendGeminiMsg: SendLLMMessageFnTypeInternal = async ({ messages, onText, onFinalMessage, onError, voidConfig, abortRef }) => {
|
||||
let didAbort = false
|
||||
let fullText = ''
|
||||
|
||||
abortRef.current = () => {
|
||||
didAbort = true
|
||||
}
|
||||
|
||||
try {
|
||||
const genAI = new GoogleGenerativeAI(voidConfig.gemini.apikey);
|
||||
// Force the model to be exactly what's configured
|
||||
const modelName = voidConfig.gemini.model;
|
||||
const model = genAI.getGenerativeModel({ model: modelName });
|
||||
|
||||
// Combine system messages with the first user message
|
||||
let systemContent = messages
|
||||
.filter(msg => msg.role === 'system')
|
||||
.map(msg => msg.content)
|
||||
.join('\n');
|
||||
|
||||
// Filter out system messages and modify first user message if needed
|
||||
let geminiMessages = messages.filter(msg => msg.role !== 'system');
|
||||
if (systemContent && geminiMessages.length > 0 && geminiMessages[0].role === 'user') {
|
||||
geminiMessages[0] = {
|
||||
...geminiMessages[0],
|
||||
content: `${systemContent}\n\n${geminiMessages[0].content}`
|
||||
};
|
||||
}
|
||||
|
||||
// Convert remaining messages to Gemini format
|
||||
const history = geminiMessages.map(msg => ({
|
||||
role: msg.role === 'assistant' ? 'model' : msg.role,
|
||||
parts: [{ text: msg.content }]
|
||||
}));
|
||||
|
||||
const chat = model.startChat({
|
||||
history: history.slice(0, -1), // Exclude last message
|
||||
generationConfig: {
|
||||
maxOutputTokens: parseInt(voidConfig.default.maxTokens)
|
||||
// Removed model from generationConfig since it's not a valid property
|
||||
// Model is already set when creating the model instance above
|
||||
}
|
||||
});
|
||||
|
||||
const lastMessage = messages[messages.length - 1].content;
|
||||
const result = await chat.sendMessageStream(lastMessage);
|
||||
|
||||
for await (const chunk of result.stream) {
|
||||
if (didAbort) return;
|
||||
const newText = chunk.text();
|
||||
fullText += newText;
|
||||
onText(newText, fullText);
|
||||
}
|
||||
|
||||
onFinalMessage(fullText);
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof Error && error.message?.includes('API key')) {
|
||||
onError('Invalid API key.');
|
||||
} else if (error instanceof Error) {
|
||||
onError(error.message || 'Error connecting to Gemini');
|
||||
} else {
|
||||
onError('Error connecting to Gemini');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// OpenAI, OpenRouter, OpenAICompatible
|
||||
const sendOpenAIMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, voidConfig, abortRef }) => {
|
||||
|
|
@ -178,7 +241,6 @@ const sendOpenAIMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinal
|
|||
|
||||
};
|
||||
|
||||
|
||||
// Ollama
|
||||
export const sendOllamaMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, voidConfig, abortRef }) => {
|
||||
|
||||
|
|
@ -220,8 +282,6 @@ export const sendOllamaMsg: SendLLMMessageFnTypeInternal = ({ messages, onText,
|
|||
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Greptile
|
||||
// https://docs.greptile.com/api-reference/query
|
||||
// https://docs.greptile.com/quickstart#sample-response-streamed
|
||||
|
|
@ -236,7 +296,6 @@ const sendGreptileMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFin
|
|||
didAbort = true
|
||||
}
|
||||
|
||||
|
||||
fetch('https://api.greptile.com/v2/query', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
|
|
@ -291,8 +350,6 @@ const sendGreptileMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFin
|
|||
|
||||
}
|
||||
|
||||
|
||||
|
||||
export const sendLLMMessage: SendLLMMessageFnTypeExternal = ({ messages, onText, onFinalMessage, onError, voidConfig, abortRef }) => {
|
||||
if (!voidConfig) return;
|
||||
|
||||
|
|
@ -310,6 +367,8 @@ export const sendLLMMessage: SendLLMMessageFnTypeExternal = ({ messages, onText,
|
|||
return sendOllamaMsg({ messages, onText, onFinalMessage, onError, voidConfig, abortRef });
|
||||
case 'greptile':
|
||||
return sendGreptileMsg({ messages, onText, onFinalMessage, onError, voidConfig, abortRef });
|
||||
case 'gemini':
|
||||
return sendGeminiMsg({ messages, onText, onFinalMessage, onError, voidConfig, abortRef });
|
||||
default:
|
||||
onError(`Error: whichApi was ${voidConfig.default.whichApi}, which is not recognized!`)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@ export const configFields = [
|
|||
'ollama',
|
||||
'openRouter',
|
||||
'openAICompatible',
|
||||
'azure'
|
||||
'azure',
|
||||
'gemini'
|
||||
] as const
|
||||
|
||||
|
||||
|
|
@ -164,6 +165,19 @@ const voidConfigInfo: Record<
|
|||
// }
|
||||
// },
|
||||
},
|
||||
gemini: {
|
||||
apikey: configString('Google API key.', ''),
|
||||
model: configEnum(
|
||||
'Gemini model to use.',
|
||||
'gemini-1.5-flash',
|
||||
[
|
||||
"gemini-1.5-flash",
|
||||
"gemini-1.5-pro",
|
||||
"gemini-1.5-flash-8b",
|
||||
"gemini-1.0-pro"
|
||||
] as const
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -273,4 +287,3 @@ export function useVoidConfig(): ConfigValueType {
|
|||
}
|
||||
return context
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue