- Implement GPT-5 model support with correct parameters (max_completion_tokens, temperature=1) - Add configurable reasoning effort levels (low/medium/high) with database migration - Create admin interface controls for reasoning effort management in agent creation/editing - Fix conversation loading regression to prevent double-click requirement - Update home page cards for uniform 280px height with proper text truncation - Add comprehensive GPT-5 parameter handling in OpenAI service integration 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
175 lines
No EOL
5.6 KiB
JavaScript
175 lines
No EOL
5.6 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,
|
|
reasoningEffort = 'medium',
|
|
} = assistantConfig;
|
|
|
|
console.log(`Making OpenAI request with model: ${model}`);
|
|
|
|
const requestParams = {
|
|
model,
|
|
messages,
|
|
temperature,
|
|
stream,
|
|
};
|
|
|
|
// GPT-5 uses max_completion_tokens instead of max_tokens and requires temperature = 1
|
|
if (model === 'gpt-5') {
|
|
console.log(`GPT-5 detected, using max_completion_tokens, temperature = 1, and reasoning_effort = ${reasoningEffort}`);
|
|
requestParams.max_completion_tokens = maxTokens;
|
|
requestParams.temperature = 1; // GPT-5 only supports temperature = 1
|
|
requestParams.reasoning_effort = reasoningEffort; // GPT-5 reasoning effort from agent config
|
|
} else {
|
|
requestParams.max_tokens = maxTokens;
|
|
}
|
|
|
|
const response = await openai.chat.completions.create(requestParams);
|
|
|
|
return response;
|
|
} catch (error) {
|
|
console.error('OpenAI Responses API Error:', {
|
|
model: assistantConfig.model,
|
|
error: error.message,
|
|
status: error.status,
|
|
code: error.code,
|
|
type: error.type
|
|
});
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async createStreamingResponse(messages, assistantConfig = {}) {
|
|
try {
|
|
const {
|
|
model = 'gpt-4o',
|
|
temperature = 0.7,
|
|
maxTokens = 4000,
|
|
reasoningEffort = 'medium',
|
|
} = assistantConfig;
|
|
|
|
console.log(`Making streaming OpenAI request with model: ${model}`);
|
|
|
|
const requestParams = {
|
|
model,
|
|
messages,
|
|
temperature,
|
|
stream: true,
|
|
};
|
|
|
|
// GPT-5 uses max_completion_tokens instead of max_tokens and requires temperature = 1
|
|
if (model === 'gpt-5') {
|
|
console.log(`GPT-5 detected for streaming, using max_completion_tokens, temperature = 1, and reasoning_effort = ${reasoningEffort}`);
|
|
requestParams.max_completion_tokens = maxTokens;
|
|
requestParams.temperature = 1; // GPT-5 only supports temperature = 1
|
|
requestParams.reasoning_effort = reasoningEffort; // GPT-5 reasoning effort from agent config
|
|
} else {
|
|
requestParams.max_tokens = maxTokens;
|
|
}
|
|
|
|
const stream = await openai.chat.completions.create(requestParams);
|
|
|
|
return stream;
|
|
} catch (error) {
|
|
console.error('OpenAI Streaming Responses API Error:', {
|
|
model: assistantConfig.model,
|
|
error: error.message,
|
|
status: error.status,
|
|
code: error.code,
|
|
type: error.type
|
|
});
|
|
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(); |