contaigents
Version:
Modular AI Content Ecosystem with Audio Generation
159 lines (158 loc) • 6.64 kB
JavaScript
import { BaseLLM } from "./BaseLLM.js";
export class GoogleProvider extends BaseLLM {
constructor() {
super(GoogleProvider.providerConfig);
}
async executePrompt(prompt, options = {}) {
if (!(await this.isConfigured())) {
throw new Error("Google provider is not configured");
}
const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/${this.config.model}:generateContent?key=${this.config.apiKey}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
contents: [{ parts: [{ text: prompt }] }],
generationConfig: {
temperature: options.temperature ?? 0.7,
maxOutputTokens: options.maxTokens,
},
}),
});
if (!response.ok) {
const error = await response.json();
throw new Error(`Google API error: ${error.error?.message || "Unknown error"}`);
}
const data = await response.json();
// Validate response structure with better error handling
if (!data.candidates || !Array.isArray(data.candidates) || data.candidates.length === 0) {
throw new Error(`Google API returned no candidates. Response: ${JSON.stringify(data)}`);
}
const candidate = data.candidates[0];
// Check if response was truncated due to token limit
if (candidate && candidate.finishReason === 'MAX_TOKENS') {
throw new Error(`Google API response was truncated due to MAX_TOKENS limit. Consider increasing maxTokens parameter.`);
}
if (!candidate || !candidate.content || !candidate.content.parts || !Array.isArray(candidate.content.parts) || candidate.content.parts.length === 0) {
throw new Error(`Google API candidate has invalid content structure. Candidate: ${JSON.stringify(candidate)}`);
}
const text = candidate.content.parts[0].text;
if (typeof text !== 'string') {
throw new Error(`Google API returned non-string text. Parts: ${JSON.stringify(candidate.content.parts)}`);
}
return {
content: text,
raw: data,
};
}
async executeConversation(messages, options = {}) {
if (!(await this.isConfigured())) {
throw new Error("Google provider is not configured");
}
// Extract system message for systemInstruction
const systemMessage = messages.find(msg => msg.role === 'system');
// Convert conversation messages to Google format (excluding system messages)
const contents = messages
.filter(msg => msg.role !== 'system')
.map(msg => {
const parts = [{ text: msg.content }];
// Add image attachments if present
if (msg.images && msg.images.length > 0) {
for (const image of msg.images) {
parts.push({
inline_data: {
mime_type: image.mimeType,
data: image.base64Data
}
});
}
}
return {
role: msg.role === 'assistant' ? 'model' : 'user',
parts
};
});
// Build request body
const requestBody = {
contents,
generationConfig: {
temperature: options.temperature ?? 0.7,
maxOutputTokens: options.maxTokens,
},
};
// Add system instruction if system message exists
if (systemMessage) {
requestBody.systemInstruction = {
parts: [{ text: systemMessage.content }]
};
}
const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/${this.config.model}:generateContent?key=${this.config.apiKey}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(requestBody),
});
if (!response.ok) {
const error = await response.json();
throw new Error(`Google API error: ${error.error?.message || "Unknown error"}`);
}
const data = await response.json();
// Validate response structure with better error handling
if (!data.candidates || !Array.isArray(data.candidates) || data.candidates.length === 0) {
throw new Error(`Google API returned no candidates. Response: ${JSON.stringify(data)}`);
}
const candidate = data.candidates[0];
// Check if response was truncated due to token limit
if (candidate && candidate.finishReason === 'MAX_TOKENS') {
throw new Error(`Google API response was truncated due to MAX_TOKENS limit. Consider increasing maxTokens parameter.`);
}
if (!candidate || !candidate.content || !candidate.content.parts || !Array.isArray(candidate.content.parts) || candidate.content.parts.length === 0) {
throw new Error(`Google API candidate has invalid content structure. Candidate: ${JSON.stringify(candidate)}`);
}
const text = candidate.content.parts[0].text;
if (typeof text !== 'string') {
throw new Error(`Google API returned non-string text. Parts: ${JSON.stringify(candidate.content.parts)}`);
}
return {
content: text,
raw: data,
};
}
supportsConversation() {
return true;
}
getCapabilities() {
return {
supportsConversation: true,
supportsSystemMessages: true, // Google supports system messages via systemInstruction
supportsToolCalls: true, // Enable tool calls for agentic behavior
customFormat: 'gemini',
maxContextLength: 1000000,
};
}
}
GoogleProvider.providerConfig = {
name: "Google",
configFields: [
{
name: "apiKey",
required: true,
},
{
name: "model",
required: true,
options: [
"gemini-2.5-flash",
"gemini-2.0-flash",
"gemini-2.0-flash-lite-preview-02-05",
"gemini-1.5-flash",
"gemini-1.5-flash-8b",
"gemini-1.5-pro",
"gemini-1.0-pro",
],
default: "gemini-2.5-flash",
},
],
};