game-analysis-types
Version:
Common TypeScript types and utilities for game analysis tools.
177 lines • 8.78 kB
JavaScript
export class GeminiModelStrategy {
config;
constructor(config) {
this.config = config;
}
getConfig() {
return this.config;
}
/**
* Generate text using Gemini API (fulfills AIModelStrategy interface)
* @param request - The AI request with prompt and options
* @returns The AI response
*/
async generate(request) {
const apiKey = this.config.apiKey;
// Use the specific model endpoint from config if provided, else default
const apiEndpoint = this.config.apiEndpoint || `https://generativelanguage.googleapis.com/v1beta/models/${this.config.model}:generateContent?key=${apiKey}`;
const headers = {
'Content-Type': 'application/json',
// Authorization header might not be needed if key is in URL
// 'Authorization': `Bearer ${apiKey}`
};
const body = {
contents: [{ parts: [{ text: request.prompt }] }],
generationConfig: {
// Ensure maxOutputTokens is an integer
maxOutputTokens: Math.floor(request.options?.maxTokens || this.config.maxTokens || 8192), // Added default
temperature: request.options?.temperature || this.config.temperature || 0.7 // Added default
}
};
try {
const response = await fetch(apiEndpoint, {
method: 'POST',
headers,
body: JSON.stringify(body)
});
const responseBody = await response.text(); // Read body first for better error reporting
if (!response.ok) {
console.error('Gemini API Response Error Body:', responseBody);
throw new Error(`Gemini API error: ${response.status} ${response.statusText} - ${responseBody}`);
}
const result = JSON.parse(responseBody);
// Gemini returns content in a somewhat different format; extract the text
const content = result.candidates?.[0]?.content?.parts?.[0]?.text || '';
// Gemini API (v1beta) may not provide exact token usage in the response
// We might need to estimate or use a library if precise token count is needed
const inputTokens = 0; // Placeholder
const outputTokens = 0; // Placeholder
const totalTokens = 0; // Placeholder
return {
content,
model: this.config.model, // Use the configured model name
tokenUsage: {
input: inputTokens,
output: outputTokens,
total: totalTokens
},
// Calculate cost based on estimated tokens or characters if API doesn't provide
cost: this.calculateCost(inputTokens, outputTokens), // Will be 0 with current placeholders
metadata: {
model_version: result.candidates?.[0]?.model || this.config.model,
finishing_reason: result.candidates?.[0]?.finishReason || 'stop',
// Add other relevant metadata if available
raw_response: result // Include raw response for potential debugging
}
};
}
catch (error) {
console.error('Gemini API generate error:', error);
throw new Error(`Failed to generate with Gemini: ${error instanceof Error ? error.message : String(error)}`);
}
}
async analyze(request) {
const apiKey = this.config.apiKey;
// Use the specific model endpoint from config if provided, else default
const apiEndpoint = this.config.apiEndpoint || `https://generativelanguage.googleapis.com/v1beta/models/${this.config.model}:generateContent?key=${apiKey}`;
const headers = {
'Content-Type': 'application/json',
// Authorization header might not be needed if key is in URL
// 'Authorization': `Bearer ${apiKey}`
};
const body = {
contents: [{ parts: [{ text: request.prompt }] }],
generationConfig: {
// Ensure maxOutputTokens is an integer
maxOutputTokens: Math.floor(request.options?.maxTokens || this.config.maxTokens || 8192), // Added default
temperature: request.options?.temperature || this.config.temperature || 0.7 // Added default
}
};
try {
const response = await fetch(apiEndpoint, {
method: 'POST',
headers,
body: JSON.stringify(body)
});
const responseBody = await response.text(); // Read body first for better error reporting
if (!response.ok) {
console.error('Gemini API Response Error Body:', responseBody);
throw new Error(`Gemini API error: ${response.status} ${response.statusText} - ${responseBody}`);
}
const result = JSON.parse(responseBody);
// Gemini returns content in a somewhat different format; extract the text
const content = result.candidates?.[0]?.content?.parts?.[0]?.text || '';
// Gemini API (v1beta) may not provide exact token usage in the response
// We might need to estimate or use a library if precise token count is needed
const inputTokens = 0; // Placeholder
const outputTokens = 0; // Placeholder
const totalTokens = 0; // Placeholder
return {
content,
model: this.config.model, // Use the configured model name
tokenUsage: {
input: inputTokens,
output: outputTokens,
total: totalTokens
},
// Calculate cost based on estimated tokens or characters if API doesn't provide
cost: this.calculateCost(inputTokens, outputTokens), // Will be 0 with current placeholders
metadata: {
model_version: result.candidates?.[0]?.model || this.config.model,
finishing_reason: result.candidates?.[0]?.finishReason || 'stop'
// Add other relevant metadata if available
}
};
}
catch (error) {
console.error('Gemini API error:', error);
throw new Error(`Failed to analyze with Gemini: ${error instanceof Error ? error.message : String(error)}`);
}
}
async isAvailable() {
// Basic check: is API key configured?
if (!this.config.apiKey) {
console.warn('Gemini API key not configured.');
return false;
}
// More robust check: Try listing models (requires API key)
// Note: Endpoint might vary based on Gemini API version
const listModelsEndpoint = `https://generativelanguage.googleapis.com/v1beta/models?key=${this.config.apiKey}`;
try {
const response = await fetch(listModelsEndpoint, { method: 'GET' });
if (!response.ok) {
const errorBody = await response.text();
console.warn(`Gemini availability check failed: ${response.status} ${response.statusText}`, errorBody);
return false;
}
// Optionally check if the specific configured model is in the list
// const data = await response.json();
// const modelExists = data.models?.some(m => m.name.includes(this.config.model));
// return modelExists;
return true; // If the list models call succeeds, assume basic availability
}
catch (error) {
console.error('Gemini availability check fetch error:', error);
return false;
}
}
/**
* Calculate the cost of the API call based on token usage
* @param inputTokens - Number of input tokens
* @param outputTokens - Number of output tokens
* @returns The cost of the API call in USD
*/
calculateCost(inputTokens, outputTokens) {
// Use costs from config, defaulting to 0 if not provided or if tokens are 0
if (inputTokens === 0 && outputTokens === 0)
return 0;
const inputCostPer1K = this.config.inputCostPer1K || 0;
const outputCostPer1K = this.config.outputCostPer1K || 0;
// Handle potential estimation if API doesn't provide counts
// For now, assumes counts are provided or are 0
const inputCost = (inputTokens / 1000) * inputCostPer1K;
const outputCost = (outputTokens / 1000) * outputCostPer1K;
return inputCost + outputCost;
}
}
//# sourceMappingURL=gemini.js.map