ideas-generator/server/utils/openai.js
DJP 88d18619bb Complete migration from OpenAI Assistants API to Chat Completions API with Vue.js frontend
Major Features Implemented:
- Full Vue.js 3 admin interface with Vite build system
- OpenAI Chat Completions API integration (replaced deprecated Assistants API)
- PostgreSQL database with Sequelize ORM
- Complete conversation management system
- User authentication system (admin@oliver.agency, user@oliver.agency)
- AI-powered conversation title generation
- Server-Sent Events for streaming responses
- Conversation soft delete functionality
- Rate limiting middleware with development bypass

Backend Infrastructure:
- Node.js/Express server with comprehensive error handling
- Database models: User, Assistant, Conversation, Message
- Chat API endpoints with full conversation history context
- Conversation CRUD operations with soft delete
- Migration and seeding scripts
- Environment-based configuration

Frontend Features:
- Responsive Vue.js interface with router
- Real-time chat with streaming responses
- Conversation sidebar with delete functionality
- Agent selection dropdown
- Persistent user sessions with hash-based user IDs
- Conversation history loading and continuity
- Login system with user role management
- Prominent logout functionality

Technical Improvements:
- Fixed conversation continuity by loading full message history
- Implemented conversation title generation using GPT-4o-mini
- Added conversation persistence mechanisms (periodic refresh, window focus)
- Enhanced error handling and rate limiting
- Proper environment variable management
- Clean project structure with separated concerns

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-03 13:08:26 -04:00

135 lines
No EOL
3.9 KiB
JavaScript

const OpenAI = require('openai');
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
organization: process.env.OPENAI_ORG_ID,
});
class OpenAIService {
constructor() {
if (!process.env.OPENAI_API_KEY || process.env.OPENAI_API_KEY.includes('your-actual')) {
console.warn('⚠️ OpenAI API key not configured properly');
}
}
async createResponse(messages, assistantConfig = {}) {
try {
const {
model = 'gpt-4o',
temperature = 0.7,
maxTokens = 4000,
stream = false,
} = assistantConfig;
const response = await openai.chat.completions.create({
model,
messages,
temperature,
max_tokens: maxTokens,
stream,
});
return response;
} catch (error) {
console.error('OpenAI Responses API Error:', error);
throw error;
}
}
async createStreamingResponse(messages, assistantConfig = {}) {
try {
const {
model = 'gpt-4o',
temperature = 0.7,
maxTokens = 4000,
} = assistantConfig;
const stream = await openai.chat.completions.create({
model,
messages,
temperature,
max_tokens: maxTokens,
stream: true,
});
return stream;
} catch (error) {
console.error('OpenAI Streaming Responses API Error:', error);
throw error;
}
}
formatMessagesForAPI(messages) {
return messages.map(msg => ({
role: msg.role,
content: msg.content,
}));
}
buildSystemMessage(systemPrompt) {
return {
role: 'system',
content: systemPrompt,
};
}
async generateConversationTitle(userMessage, assistantResponse, agentName) {
try {
if (!process.env.ENABLE_TITLE_GENERATION || process.env.ENABLE_TITLE_GENERATION !== 'true') {
console.log('Title generation disabled, using fallback');
return `Chat with ${agentName}`;
}
const titlePrompt = `Based on this conversation between a user and an AI assistant called "${agentName}", generate a short, descriptive title (3-6 words) that captures what the user is asking about or discussing. Be specific and concise.
User message: "${userMessage}"
Assistant response: "${assistantResponse}"
Generate only the title, nothing else. Examples:
- "Marketing Campaign Ideas"
- "JavaScript Function Help"
- "Travel Planning Advice"
- "Budget Analysis Discussion"`;
const response = await openai.chat.completions.create({
model: 'gpt-4o-mini', // Use cheaper model for title generation
messages: [
{ role: 'user', content: titlePrompt }
],
temperature: 0.3, // Lower temperature for more consistent titles
max_tokens: 20, // Short titles only
});
const title = response.choices[0]?.message?.content?.trim();
if (!title) {
console.warn('No title generated, using fallback');
return `Chat with ${agentName}`;
}
// Clean up the title - remove quotes if present
const cleanTitle = title.replace(/^["']|["']$/g, '');
console.log(`Generated title: "${cleanTitle}"`);
return cleanTitle;
} catch (error) {
console.error('Error generating conversation title:', error);
return `Chat with ${agentName}`; // Fallback title
}
}
async testConnection() {
try {
const response = await this.createResponse([
{ role: 'user', content: 'Hello, this is a test message.' }
], { maxTokens: 50 });
console.log('✅ OpenAI Responses API connection test successful');
return { success: true, response };
} catch (error) {
console.error('❌ OpenAI Responses API connection test failed:', error.message);
return { success: false, error: error.message };
}
}
}
module.exports = new OpenAIService();