ReelBot / services /geminiService.ts
JeCabrera's picture
Upload 19 files
2accaeb verified
import { GoogleGenAI, Chat, HarmCategory, HarmBlockThreshold, GenerateContentResponse, Part, Content } from "@google/genai";
import { MODEL_NAME } from '../constants'; // Updated import, REEL_BOT_SYSTEM_INSTRUCTION removed as not directly used here
import type { GroundingChunk, UploadedFile } from '../types';
const API_KEY = process.env.API_KEY;
if (!API_KEY) {
console.error("API_KEY for Gemini is not set. Please set the API_KEY environment variable.");
}
const ai = new GoogleGenAI({ apiKey: API_KEY! });
const generationConfigValues = {
temperature: 0.7,
topK: 1,
topP: 1,
maxOutputTokens: 4096,
};
const safetySettingsList = [
{ category: HarmCategory.HARM_CATEGORY_HARASSMENT, threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE },
{ category: HarmCategory.HARM_CATEGORY_HATE_SPEECH, threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE },
{ category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT, threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE },
{ category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE },
];
async function createChatSessionWithHistory(systemInstructionText: string, history: Content[]): Promise<Chat> {
const filteredHistory = history.filter(content => content.role !== "system");
return ai.chats.create({
model: MODEL_NAME,
history: filteredHistory,
config: {
systemInstruction: systemInstructionText,
temperature: generationConfigValues.temperature,
topK: generationConfigValues.topK,
topP: generationConfigValues.topP,
maxOutputTokens: generationConfigValues.maxOutputTokens,
safetySettings: safetySettingsList,
tools: [{ googleSearch: {} }],
// thinkingConfig: { thinkingBudget: 0 }, // Kept commented: allow default thinking for complex prompt
}
});
}
async function* sendMessageStream(
chat: Chat,
messageText: string,
imageFile?: UploadedFile
): AsyncGenerator<{ text: string; groundingChunks?: GroundingChunk[] }, void, undefined> {
try {
const parts: Part[] = [];
const userProvidedText = messageText.trim();
if (imageFile) {
if (imageFile.dataUrl && imageFile.type.startsWith('image/')) {
const base64Data = imageFile.dataUrl.split(',')[1];
if (base64Data) {
if (!userProvidedText) {
parts.push({ text: "Describe this image" });
} else {
parts.push({ text: userProvidedText });
}
parts.push({
inlineData: {
mimeType: imageFile.type,
data: base64Data,
},
});
}
} else {
const docInfo = `(User uploaded a document: ${imageFile.name})`;
if (userProvidedText) {
parts.push({ text: `${userProvidedText}\n${docInfo}` });
} else {
parts.push({ text: docInfo });
}
}
} else {
if (userProvidedText) {
parts.push({ text: userProvidedText });
}
}
if (parts.length === 0) {
parts.push({ text: " " });
}
const result = await chat.sendMessageStream({ message: parts });
for await (const chunk of result) {
const text = chunk.text;
const groundingMetadata = chunk.candidates?.[0]?.groundingMetadata;
let groundingChunks: GroundingChunk[] | undefined = undefined;
if (groundingMetadata?.groundingChunks && groundingMetadata.groundingChunks.length > 0) {
groundingChunks = groundingMetadata.groundingChunks
.filter(gc => gc.web?.uri && gc.web?.title)
.map(gc => ({ web: { uri: gc.web!.uri, title: gc.web!.title } }));
}
yield { text, groundingChunks };
}
} catch (error) {
console.error("Error in sendMessageStream:", error);
if (error instanceof Error) {
yield { text: `\n\n[AI Error: ${error.message}]` };
} else {
yield { text: `\n\n[An unexpected AI error occurred]` };
}
throw error;
}
}
// generateChatName function removed
export const geminiService = {
createChatSessionWithHistory,
sendMessageStream,
// generateChatName removed from exports
};