perf-lens
Version:
AI-powered frontend performance optimizer
120 lines (119 loc) • 4.78 kB
JavaScript
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}`);
}
}