UNPKG

perf-lens

Version:

AI-powered frontend performance optimizer

120 lines (119 loc) 4.78 kB
import OpenAI from 'openai'; import Anthropic from '@anthropic-ai/sdk'; import { GoogleGenerativeAI } from '@google/generative-ai'; export class OpenAIModel { constructor(config) { this.config = config; this.client = new OpenAI({ apiKey: config.apiKey || process.env.PERF_LENS_OPENAI_API_KEY, }); } async generateSuggestions(prompt, options) { const isReasoningModel = /^o/.test(this.config.model); const stream = await this.client.chat.completions.create({ model: this.config.model, messages: [ { role: 'system', content: options?.systemPrompt || 'You are a performance optimization expert for frontend web applications. You MUST only reference files and line numbers that actually exist in the provided code. Never make assumptions about code you cannot see.', }, { role: 'user', content: prompt }, ], ...(isReasoningModel ? { reasoning_effort: 'high' } : { temperature: this.config.temperature || 0.2 }), max_completion_tokens: this.config.maxTokens, stream: true, }); let fullResponse = ''; for await (const chunk of stream) { if (chunk.choices[0]?.delta?.content) { const content = chunk.choices[0].delta.content; if (options?.onChunk) options.onChunk(content, fullResponse.length === 0); fullResponse += content; } } return fullResponse; } getConfig() { return this.config; } } export class AnthropicModel { constructor(config) { this.config = config; this.client = new Anthropic({ apiKey: config.apiKey || process.env.PERF_LENS_ANTHROPIC_API_KEY, }); } async generateSuggestions(prompt, options) { const stream = await this.client.messages.create({ model: this.config.model, system: options?.systemPrompt || 'You are a performance optimization expert for frontend web applications. You MUST only reference files and line numbers that actually exist in the provided code. Never make assumptions about code you cannot see.', messages: [{ role: 'user', content: prompt }], temperature: this.config.temperature || 0.2, max_tokens: this.config.maxTokens || 4096, // Anthropic's max tokens older models stream: true, }); let fullResponse = ''; for await (const chunk of stream) { if (chunk.type === 'content_block_delta' && 'text' in chunk.delta) { const text = chunk.delta.text; if (options?.onChunk) options.onChunk(text, fullResponse.length === 0); fullResponse += text; } } return fullResponse; } getConfig() { return this.config; } } export class GeminiModel { constructor(config) { this.config = config; this.client = new GoogleGenerativeAI(config.apiKey || process.env.PERF_LENS_GEMINI_API_KEY || ''); } async generateSuggestions(prompt, options) { const model = this.client.getGenerativeModel({ model: this.config.model, generationConfig: { temperature: this.config.temperature || 0.2, maxOutputTokens: this.config.maxTokens, }, systemInstruction: options?.systemPrompt || 'You are a performance optimization expert for frontend web applications. You MUST only reference files and line numbers that actually exist in the provided code. Never make assumptions about code you cannot see.', }); const result = await model.generateContentStream({ contents: [{ role: 'user', parts: [{ text: prompt }] }], }); let fullResponse = ''; for await (const chunk of result.stream) { if (chunk.text) { if (options?.onChunk) options.onChunk(chunk.text(), fullResponse.length === 0); fullResponse += chunk.text(); } } return fullResponse; } getConfig() { return this.config; } } export function createAIModel(config) { switch (config.provider) { case 'openai': return new OpenAIModel(config); case 'anthropic': return new AnthropicModel(config); case 'gemini': return new GeminiModel(config); default: throw new Error(`Unsupported AI provider: ${config.provider}`); } }