feat: enhance AI content generation with logging and fallbacks
All checks were successful
Deploy to Production / deploy (push) Successful in 1m48s

- Added comprehensive logging for OpenAI API calls and responses in both AltTextGenerator and MetadataGenerator
- Updated OpenAI model from gpt-5-2025-08-07 to gpt-4o and standardized completion parameters
- Implemented fallback mechanisms for handling empty or invalid AI responses
- Added buildFallback methods to generate reasonable defaults from input text
- Enhanced MetadataGenerator with smart title/tag derivation and URL slugification
This commit is contained in:
Ender 2025-10-27 00:04:23 +01:00
parent 36de987b5e
commit a9b009685c
2 changed files with 88 additions and 12 deletions

View File

@ -36,8 +36,12 @@ export class AltTextGenerator {
? ALT_TEXT_WITH_CAPTION_PROMPT
: ALT_TEXT_ONLY_PROMPT;
const completion = await this.openai.chat.completions.create({
model: 'gpt-5-2025-08-07',
console.log('[AltTextGenerator] Context length:', context.length);
console.log('[AltTextGenerator] Include caption:', includeCaption);
console.log('[AltTextGenerator] Calling OpenAI with model: gpt-4o');
const completionParams: any = {
model: 'gpt-4o',
messages: [
{
role: 'system',
@ -48,13 +52,24 @@ export class AltTextGenerator {
content: context,
},
],
max_completion_tokens: includeCaption ? 200 : 100,
});
temperature: 0.7,
max_tokens: includeCaption ? 200 : 100,
};
if (includeCaption) {
completionParams.response_format = { type: 'json_object' };
}
const completion = await this.openai.chat.completions.create(completionParams);
console.log('[AltTextGenerator] Response:', completion.choices[0]?.message?.content);
const response = completion.choices[0]?.message?.content?.trim() || '';
if (!response) {
throw new Error('No content generated');
console.log('[AltTextGenerator] Empty response, using fallback');
const fallback = this.buildFallback(params.placeholderDescription, includeCaption);
return fallback;
}
if (includeCaption) {
@ -69,6 +84,7 @@ export class AltTextGenerator {
};
} catch (parseErr) {
console.error('[AltTextGenerator] JSON parse error:', parseErr);
console.error('[AltTextGenerator] Raw response was:', response);
// Fallback: treat as alt text only
return { altText: response, caption: '' };
}
@ -78,4 +94,10 @@ export class AltTextGenerator {
return { altText: response, caption: '' };
}
}
private buildFallback(description: string, includeCaption: boolean): GenerateAltTextResponse {
const altText = description.replace(/_/g, ' ').replace(/-/g, ' ').trim();
const caption = includeCaption ? `Image showing ${altText}` : '';
return { altText, caption };
}
}

View File

@ -16,8 +16,12 @@ export class MetadataGenerator {
const textContent = stripHtmlTags(params.contentHtml);
const preview = textContent.slice(0, 2000);
console.log('[MetadataGenerator] Input preview length:', preview.length);
console.log('[MetadataGenerator] Input preview:', preview.slice(0, 200) + '...');
console.log('[MetadataGenerator] Calling OpenAI with model: gpt-4o');
const completion = await this.openai.chat.completions.create({
model: 'gpt-5-2025-08-07',
model: 'gpt-4o',
messages: [
{
role: 'system',
@ -28,13 +32,27 @@ export class MetadataGenerator {
content: `Generate metadata for this article:\n\n${preview}`,
},
],
max_completion_tokens: 300,
temperature: 0.7,
max_tokens: 300,
response_format: { type: 'json_object' as any },
});
console.log('[MetadataGenerator] OpenAI completion object:', JSON.stringify(completion, null, 2));
console.log('[MetadataGenerator] Choices:', completion.choices);
console.log('[MetadataGenerator] First choice:', completion.choices[0]);
console.log('[MetadataGenerator] Message:', completion.choices[0]?.message);
console.log('[MetadataGenerator] Content:', completion.choices[0]?.message?.content);
const response = completion.choices[0]?.message?.content || '';
console.log('[MetadataGenerator] Response length:', response.length);
console.log('[MetadataGenerator] Response is empty:', !response);
if (!response) {
throw new Error('No metadata generated');
console.log('[MetadataGenerator] Empty response from OpenAI, using fallback');
const fallback = this.buildFallbackMetadata(textContent);
console.log('[MetadataGenerator] Fallback metadata:', fallback);
return fallback;
}
console.log('[MetadataGenerator] Raw response:', response);
@ -45,13 +63,49 @@ export class MetadataGenerator {
console.log('[MetadataGenerator] Generated:', metadata);
return {
title: metadata.title || '',
tags: metadata.tags || '',
canonicalUrl: metadata.canonicalUrl || '',
title: metadata.title || this.buildFallbackMetadata(textContent).title,
tags: metadata.tags || this.buildFallbackMetadata(textContent).tags,
canonicalUrl: metadata.canonicalUrl || this.buildFallbackMetadata(textContent).canonicalUrl,
};
} catch (parseErr) {
console.error('[MetadataGenerator] JSON parse error:', parseErr);
throw new Error('Failed to parse metadata response');
const fallback = this.buildFallbackMetadata(textContent);
return fallback;
}
}
private buildFallbackMetadata(text: string): GenerateMetadataResponse {
const title = this.deriveTitle(text);
const tags = this.deriveTags(text).join(', ');
const canonicalUrl = this.slugify(title);
return { title, tags, canonicalUrl };
}
private deriveTitle(text: string): string {
if (!text) return '';
const sentence = text.split(/[.!?]/)[0] || text;
const words = sentence.trim().split(/\s+/).slice(0, 12).join(' ');
return words.length > 60 ? words.slice(0, 60).trim() : words;
}
private deriveTags(text: string): string[] {
const stop = new Set(['the','and','or','for','with','your','from','this','that','are','you','our','into','into','about','into','on','in','to','of','a','an','it','is','as','by','at','be','can','will','should','how','what','why']);
const counts: Record<string, number> = {};
text.toLowerCase().replace(/[^a-z0-9\s-]/g, ' ').split(/\s+/).forEach(w => {
if (!w || stop.has(w) || w.length < 3) return;
counts[w] = (counts[w] || 0) + 1;
});
const top = Object.entries(counts).sort((a,b)=>b[1]-a[1]).slice(0,5).map(([w])=>w);
return top;
}
private slugify(title: string): string {
return (title || '')
.toLowerCase()
.replace(/[^a-z0-9\s-]/g, '')
.trim()
.replace(/\s+/g, '-')
.replace(/-+/g, '-')
.slice(0, 80);
}
}