Skip to content

Commit d75751b

Browse files
committed
Convo update
1 parent 767b63c commit d75751b

File tree

9 files changed

+6296
-267
lines changed

9 files changed

+6296
-267
lines changed
Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
import { eq, and } from 'drizzle-orm'
2+
import { type NextRequest, NextResponse } from 'next/server'
3+
import { z } from 'zod'
4+
import { getSession } from '@/lib/auth'
5+
import { createLogger } from '@/lib/logs/console-logger'
6+
import { db } from '@/db'
7+
import { copilotChats } from '@/db/schema'
8+
import { executeProviderRequest } from '@/providers'
9+
import { getRotatingApiKey } from '@/lib/utils'
10+
11+
const logger = createLogger('CopilotChatAPI')
12+
13+
const UpdateChatSchema = z.object({
14+
title: z.string().optional(),
15+
messages: z.array(z.any()).optional(),
16+
model: z.string().optional(),
17+
})
18+
19+
const AddMessageSchema = z.object({
20+
message: z.object({
21+
role: z.enum(['user', 'assistant']),
22+
content: z.string(),
23+
timestamp: z.string().optional(),
24+
citations: z.array(z.any()).optional(),
25+
}),
26+
})
27+
28+
/**
29+
* GET /api/copilot/chats/[id]
30+
* Get a specific copilot chat
31+
*/
32+
export async function GET(req: NextRequest, { params }: { params: { id: string } }) {
33+
try {
34+
const session = await getSession()
35+
if (!session?.user?.id) {
36+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
37+
}
38+
39+
const chatId = params.id
40+
41+
logger.info(`Getting chat ${chatId} for user ${session.user.id}`)
42+
43+
const [chat] = await db
44+
.select()
45+
.from(copilotChats)
46+
.where(
47+
and(
48+
eq(copilotChats.id, chatId),
49+
eq(copilotChats.userId, session.user.id)
50+
)
51+
)
52+
.limit(1)
53+
54+
if (!chat) {
55+
return NextResponse.json({ error: 'Chat not found' }, { status: 404 })
56+
}
57+
58+
return NextResponse.json({
59+
success: true,
60+
chat: {
61+
id: chat.id,
62+
title: chat.title,
63+
model: chat.model,
64+
messages: chat.messages,
65+
createdAt: chat.createdAt,
66+
updatedAt: chat.updatedAt,
67+
messageCount: Array.isArray(chat.messages) ? chat.messages.length : 0,
68+
},
69+
})
70+
} catch (error) {
71+
logger.error('Failed to get copilot chat:', error)
72+
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
73+
}
74+
}
75+
76+
/**
77+
* PUT /api/copilot/chats/[id]
78+
* Update a copilot chat (add messages, update title, etc.)
79+
*/
80+
export async function PUT(req: NextRequest, { params }: { params: { id: string } }) {
81+
try {
82+
const session = await getSession()
83+
if (!session?.user?.id) {
84+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
85+
}
86+
87+
const chatId = params.id
88+
const body = await req.json()
89+
const { title, messages, model } = UpdateChatSchema.parse(body)
90+
91+
logger.info(`Updating chat ${chatId} for user ${session.user.id}`)
92+
93+
// First verify the chat exists and belongs to the user
94+
const [existingChat] = await db
95+
.select()
96+
.from(copilotChats)
97+
.where(
98+
and(
99+
eq(copilotChats.id, chatId),
100+
eq(copilotChats.userId, session.user.id)
101+
)
102+
)
103+
.limit(1)
104+
105+
if (!existingChat) {
106+
return NextResponse.json({ error: 'Chat not found' }, { status: 404 })
107+
}
108+
109+
// Prepare update data
110+
const updateData: any = {
111+
updatedAt: new Date(),
112+
}
113+
114+
if (title !== undefined) updateData.title = title
115+
if (messages !== undefined) updateData.messages = messages
116+
if (model !== undefined) updateData.model = model
117+
118+
// Update the chat
119+
const [updatedChat] = await db
120+
.update(copilotChats)
121+
.set(updateData)
122+
.where(eq(copilotChats.id, chatId))
123+
.returning()
124+
125+
if (!updatedChat) {
126+
throw new Error('Failed to update chat')
127+
}
128+
129+
logger.info(`Updated chat ${chatId} for user ${session.user.id}`)
130+
131+
return NextResponse.json({
132+
success: true,
133+
chat: {
134+
id: updatedChat.id,
135+
title: updatedChat.title,
136+
model: updatedChat.model,
137+
messages: updatedChat.messages,
138+
createdAt: updatedChat.createdAt,
139+
updatedAt: updatedChat.updatedAt,
140+
messageCount: Array.isArray(updatedChat.messages) ? updatedChat.messages.length : 0,
141+
},
142+
})
143+
} catch (error) {
144+
if (error instanceof z.ZodError) {
145+
return NextResponse.json(
146+
{ error: 'Invalid request data', details: error.errors },
147+
{ status: 400 }
148+
)
149+
}
150+
151+
logger.error('Failed to update copilot chat:', error)
152+
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
153+
}
154+
}
155+
156+
/**
157+
* DELETE /api/copilot/chats/[id]
158+
* Delete a copilot chat
159+
*/
160+
export async function DELETE(req: NextRequest, { params }: { params: { id: string } }) {
161+
try {
162+
const session = await getSession()
163+
if (!session?.user?.id) {
164+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
165+
}
166+
167+
const chatId = params.id
168+
169+
logger.info(`Deleting chat ${chatId} for user ${session.user.id}`)
170+
171+
// First verify the chat exists and belongs to the user
172+
const [existingChat] = await db
173+
.select({ id: copilotChats.id })
174+
.from(copilotChats)
175+
.where(
176+
and(
177+
eq(copilotChats.id, chatId),
178+
eq(copilotChats.userId, session.user.id)
179+
)
180+
)
181+
.limit(1)
182+
183+
if (!existingChat) {
184+
return NextResponse.json({ error: 'Chat not found' }, { status: 404 })
185+
}
186+
187+
// Delete the chat
188+
await db
189+
.delete(copilotChats)
190+
.where(eq(copilotChats.id, chatId))
191+
192+
logger.info(`Deleted chat ${chatId} for user ${session.user.id}`)
193+
194+
return NextResponse.json({
195+
success: true,
196+
message: 'Chat deleted successfully',
197+
})
198+
} catch (error) {
199+
logger.error('Failed to delete copilot chat:', error)
200+
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
201+
}
202+
}
203+
204+
/**
205+
* Generate a chat title using LLM based on the first user message
206+
*/
207+
export async function generateChatTitle(userMessage: string): Promise<string> {
208+
try {
209+
const apiKey = getRotatingApiKey('anthropic')
210+
211+
const response = await executeProviderRequest('anthropic', {
212+
model: 'claude-3-haiku-20240307', // Use faster, cheaper model for title generation
213+
systemPrompt: 'You are a helpful assistant that generates concise, descriptive titles for chat conversations. Create a title that captures the main topic or question being discussed. Keep it under 50 characters and make it specific and clear.',
214+
context: `Generate a concise title for a conversation that starts with this user message: "${userMessage}"
215+
216+
Return only the title text, nothing else.`,
217+
temperature: 0.3,
218+
maxTokens: 50,
219+
apiKey,
220+
stream: false,
221+
})
222+
223+
// Handle different response types
224+
if (typeof response === 'object' && 'content' in response) {
225+
return response.content?.trim() || 'New Chat'
226+
}
227+
228+
return 'New Chat'
229+
} catch (error) {
230+
logger.error('Failed to generate chat title:', error)
231+
return 'New Chat' // Fallback title
232+
}
233+
}

0 commit comments

Comments
 (0)