Initial
This commit is contained in:
commit
c552956516
23 files changed
+5193
No files matched your search
@@ -0,0 +1,173 @@
|
||||
import OpenAI from 'openai';
|
||||
import type { Config } from '../config/index.js';
|
||||
import type { Logger } from '../utils/logger.js';
|
||||
|
||||
export type DetectedGender = 'male' | 'female' | 'other' | 'unknown';
|
||||
|
||||
export interface ImageAnalysisResult {
|
||||
description: string;
|
||||
detectedGender: DetectedGender;
|
||||
}
|
||||
|
||||
export class OpenRouterClient {
|
||||
private client: OpenAI;
|
||||
|
||||
constructor(
|
||||
private config: Config,
|
||||
private logger: Logger
|
||||
) {
|
||||
this.client = new OpenAI({
|
||||
baseURL: 'https://openrouter.ai/api/v1',
|
||||
apiKey: config.openrouter.apiKey,
|
||||
defaultHeaders: {
|
||||
'HTTP-Referer': 'https://github.com/reddit-bot',
|
||||
'X-Title': 'Reddit Bot',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async detectGenderOnly(imageBase64: string, mimeType: string): Promise<DetectedGender> {
|
||||
try {
|
||||
this.logger.debug('Quick gender detection...');
|
||||
|
||||
const response = await this.client.chat.completions.create({
|
||||
model: this.config.openrouter.model,
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: 'What is the gender of the person in this image? Reply with ONLY one word: male, female, other, or unknown',
|
||||
},
|
||||
{
|
||||
type: 'image_url',
|
||||
image_url: {
|
||||
url: `data:${mimeType};base64,${imageBase64}`,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
max_tokens: 10,
|
||||
});
|
||||
|
||||
const result = response.choices[0]?.message?.content?.toLowerCase().trim() || 'unknown';
|
||||
|
||||
if (result.includes('female')) return 'female';
|
||||
if (result.includes('male') && !result.includes('female')) return 'male';
|
||||
if (result.includes('other')) return 'other';
|
||||
return 'unknown';
|
||||
} catch (error) {
|
||||
this.logger.error('Gender detection failed:', error);
|
||||
return 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
async analyzeImage(imageBase64: string, mimeType: string): Promise<ImageAnalysisResult> {
|
||||
try {
|
||||
this.logger.debug('Analyzing image with AI...');
|
||||
|
||||
const genderPrompt = `First, identify the gender of the person in this image. Respond with EXACTLY one of these on the first line: GENDER:male, GENDER:female, GENDER:other, or GENDER:unknown
|
||||
|
||||
Then provide your analysis:
|
||||
|
||||
${this.config.openrouter.imageAnalysisPrompt}`;
|
||||
|
||||
const response = await this.client.chat.completions.create({
|
||||
model: this.config.openrouter.model,
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: genderPrompt,
|
||||
},
|
||||
{
|
||||
type: 'image_url',
|
||||
image_url: {
|
||||
url: `data:${mimeType};base64,${imageBase64}`,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
max_tokens: 600,
|
||||
});
|
||||
|
||||
const fullResponse = response.choices[0]?.message?.content || '';
|
||||
|
||||
// Parse gender from response
|
||||
let detectedGender: DetectedGender = 'unknown';
|
||||
const genderMatch = fullResponse.match(/GENDER:(male|female|other|unknown)/i);
|
||||
if (genderMatch) {
|
||||
detectedGender = genderMatch[1].toLowerCase() as DetectedGender;
|
||||
}
|
||||
|
||||
// Remove gender line from description
|
||||
const description = fullResponse.replace(/GENDER:(male|female|other|unknown)\n?/i, '').trim();
|
||||
|
||||
this.logger.debug(`Image analysis complete - Gender: ${detectedGender}`);
|
||||
this.logger.debug(`Description: ${description}`);
|
||||
|
||||
return { description, detectedGender };
|
||||
} catch (error) {
|
||||
this.logger.error('Image analysis failed:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async generateMessage(
|
||||
imageAnalysis: string,
|
||||
postTitle: string,
|
||||
authorName: string,
|
||||
successfulExamples: string[] = []
|
||||
): Promise<string> {
|
||||
try {
|
||||
this.logger.debug('Generating message...');
|
||||
|
||||
let prompt = this.config.openrouter.messageGenerationPrompt
|
||||
.replace('{analysis}', imageAnalysis)
|
||||
.replace('{title}', postTitle)
|
||||
.replace('{author}', authorName);
|
||||
|
||||
// Add successful examples if available
|
||||
if (successfulExamples.length > 0) {
|
||||
const examplesText = successfulExamples
|
||||
.map((ex, i) => `Example ${i + 1}:\n${ex}`)
|
||||
.join('\n\n');
|
||||
|
||||
prompt = `Here are some examples of messages that got positive responses:\n\n${examplesText}\n\n---\n\nNow generate a new message in a similar style:\n\n${prompt}`;
|
||||
|
||||
this.logger.debug(`Including ${successfulExamples.length} successful examples in prompt`);
|
||||
}
|
||||
|
||||
const response = await this.client.chat.completions.create({
|
||||
model: this.config.openrouter.model,
|
||||
messages: [{ role: 'user', content: prompt }],
|
||||
max_tokens: 300,
|
||||
temperature: 0.8, // Add some creativity
|
||||
});
|
||||
|
||||
const message = response.choices[0]?.message?.content || '';
|
||||
this.logger.debug(`Message generated: ${message}`);
|
||||
return message.trim();
|
||||
} catch (error) {
|
||||
this.logger.error('Message generation failed:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async analyzeAndGenerateMessage(
|
||||
imageBase64: string,
|
||||
mimeType: string,
|
||||
postTitle: string,
|
||||
authorName: string,
|
||||
successfulExamples: string[] = []
|
||||
): Promise<{ analysis: ImageAnalysisResult; message: string }> {
|
||||
const analysis = await this.analyzeImage(imageBase64, mimeType);
|
||||
const message = await this.generateMessage(analysis.description, postTitle, authorName, successfulExamples);
|
||||
return { analysis, message };
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user