changes
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import ollama from '@/lib/ollama';
|
||||
|
||||
interface Message {
|
||||
role: 'user' | 'assistant';
|
||||
content: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a short, descriptive title for a chat based on the conversation
|
||||
*/
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const { model, messages } = await request.json();
|
||||
|
||||
if (!model || !messages || !Array.isArray(messages) || messages.length === 0) {
|
||||
return NextResponse.json({ error: 'Model and messages array are required' }, { status: 400 });
|
||||
}
|
||||
|
||||
// Format the conversation for context
|
||||
const conversationContext = messages
|
||||
.map((msg: Message) => `${msg.role === 'user' ? 'User' : 'Assistant'}: ${msg.content}`)
|
||||
.join('\n\n');
|
||||
|
||||
// Create a prompt asking for a short title with full context
|
||||
const systemPrompt = `You are a helpful assistant that generates very short, descriptive titles for conversations. Based on the conversation below, generate a concise title (3-6 words maximum) that captures the main topic or intent. Reply with ONLY the title text, nothing else - no quotes, no prefixes, no explanation.`;
|
||||
|
||||
const response = await ollama.chat({
|
||||
model,
|
||||
messages: [
|
||||
{ role: 'system', content: systemPrompt },
|
||||
{
|
||||
role: 'user',
|
||||
content: `Generate a short title for this conversation:\n\n${conversationContext}`,
|
||||
},
|
||||
],
|
||||
options: {
|
||||
temperature: 0.7,
|
||||
num_predict: 25, // Limit output length
|
||||
},
|
||||
});
|
||||
|
||||
// Clean up the response - remove quotes, extra whitespace, etc.
|
||||
let title = response.message.content
|
||||
.trim()
|
||||
.replace(/^["']|["']$/g, '') // Remove surrounding quotes
|
||||
.replace(/^Title:\s*/i, '') // Remove "Title:" prefix if present
|
||||
.replace(/\n.*/g, '') // Only keep first line
|
||||
.trim();
|
||||
|
||||
// Fallback if title is empty or too long
|
||||
if (!title || title.length > 50) {
|
||||
const firstUserMessage = messages.find((m: Message) => m.role === 'user')?.content || '';
|
||||
title = firstUserMessage.slice(0, 30) + (firstUserMessage.length > 30 ? '...' : '');
|
||||
}
|
||||
|
||||
return NextResponse.json({ title });
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Generate title error:', error);
|
||||
|
||||
// Return a fallback - don't fail the request
|
||||
return NextResponse.json({
|
||||
title: 'New Chat',
|
||||
error: error instanceof Error ? error.message : 'Failed to generate title',
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -414,6 +414,9 @@ export default function ChatLayout() {
|
||||
content: fullContent,
|
||||
};
|
||||
|
||||
// Track if this is a new chat (for title generation)
|
||||
const isNewChat = !activeChatId;
|
||||
|
||||
// Save to both localStorage and database
|
||||
try {
|
||||
// Save user message to database
|
||||
@@ -427,10 +430,9 @@ export default function ChatLayout() {
|
||||
});
|
||||
const userSaveData = await userSaveRes.json();
|
||||
const savedChatId = userSaveData.chatId;
|
||||
const chatTitle = userSaveData.title || userMessage.content.slice(0, 50);
|
||||
|
||||
// Update activeChatId if this was a new chat
|
||||
if (!activeChatId && savedChatId) {
|
||||
if (isNewChat && savedChatId) {
|
||||
setActiveChatId(savedChatId);
|
||||
}
|
||||
|
||||
@@ -444,6 +446,48 @@ export default function ChatLayout() {
|
||||
}),
|
||||
});
|
||||
|
||||
// Generate a title for new chats using the model
|
||||
let chatTitle = `${userMessage.content.slice(0, 30)}...`; // Fallback title
|
||||
|
||||
if (isNewChat && selectedModel) {
|
||||
try {
|
||||
// Build conversation context (excluding the initial greeting)
|
||||
const conversationForTitle = [...newMessages, responseMessage]
|
||||
.filter((m) => !(m.role === 'assistant' && m.content.includes('How can I help')))
|
||||
.map((m) => ({ role: m.role, content: m.content }));
|
||||
|
||||
const titleRes = await fetch('/api/chat/title', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
model: selectedModel,
|
||||
messages: conversationForTitle,
|
||||
}),
|
||||
});
|
||||
const titleData = await titleRes.json();
|
||||
|
||||
if (titleData.title) {
|
||||
chatTitle = titleData.title;
|
||||
|
||||
// Update title in database
|
||||
await fetch(`/api/chats/${savedChatId}`, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ title: chatTitle }),
|
||||
});
|
||||
|
||||
// Update title in local state
|
||||
setChats((prev) =>
|
||||
prev.map((c) => (c.id === savedChatId ? { ...c, title: chatTitle } : c))
|
||||
);
|
||||
}
|
||||
} catch (titleError) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Failed to generate title:', titleError);
|
||||
// Continue with fallback title
|
||||
}
|
||||
}
|
||||
|
||||
// Save to localStorage
|
||||
const finalMessages = [...newMessages, responseMessage];
|
||||
saveLocalChat({
|
||||
@@ -456,6 +500,7 @@ export default function ChatLayout() {
|
||||
// Refresh chat list
|
||||
fetchChats();
|
||||
} catch (saveError) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Failed to save messages:', saveError);
|
||||
}
|
||||
} catch (e) {
|
||||
|
||||
Reference in New Issue
Block a user