Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 47 additions & 35 deletions app/(chat)/api/chat/route.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import {
streamText,
convertToModelMessages,
stepCountIs,
} from 'ai';
import { streamText, convertToModelMessages, stepCountIs } from 'ai';
import { withSupermemory } from '@supermemory/tools/ai-sdk';
import { auth, type UserType } from '@/app/(auth)/auth';
import { type RequestHints, systemPrompt } from '@/lib/ai/prompts';
Expand Down Expand Up @@ -38,7 +34,8 @@ export async function POST(request: Request) {
}

try {
const { id, message, selectedChatModel, selectedVisibilityType } = requestBody;
const { id, message, selectedChatModel, selectedVisibilityType } =
requestBody;

const session = await auth();
if (!session?.user) {
Expand Down Expand Up @@ -74,9 +71,10 @@ export async function POST(request: Request) {
// Convert DB messages to AI SDK v5 format (using parts array)
const formattedPreviousMessages = previousMessages.map((dbMsg: any) => {
// Ensure parts array exists, or create one from content if needed
const parts = Array.isArray(dbMsg.parts) && dbMsg.parts.length > 0
? dbMsg.parts
: [{ type: 'text', text: dbMsg.content || '' }];
const parts =
Array.isArray(dbMsg.parts) && dbMsg.parts.length > 0
? dbMsg.parts
: [{ type: 'text', text: dbMsg.content || '' }];

return {
id: dbMsg.id,
Expand All @@ -95,10 +93,7 @@ export async function POST(request: Request) {
};

// Append current message to previous messages
const messages = [
...formattedPreviousMessages,
formattedCurrentMessage,
];
const messages = [...formattedPreviousMessages, formattedCurrentMessage];

const { longitude, latitude, city, country } = geolocation(request);
const requestHints: RequestHints = {
Expand All @@ -115,7 +110,8 @@ export async function POST(request: Request) {
id: message.id,
role: 'user',
parts: message.parts,
attachments: message.parts?.filter((part: any) => part.type === 'file') ?? [],
attachments:
message.parts?.filter((part: any) => part.type === 'file') ?? [],
createdAt: new Date(),
},
],
Expand All @@ -127,13 +123,19 @@ export async function POST(request: Request) {
// Get the API key for supermemory tools
const supermemoryApiKey = process.env.SUPERMEMORY_API_KEY;
if (!supermemoryApiKey) {
return new ChatSDKError('bad_request:api', 'SUPERMEMORY_API_KEY is not configured').toResponse();
return new ChatSDKError(
'bad_request:api',
'SUPERMEMORY_API_KEY is not configured',
).toResponse();
}

// Get the API key for Exa tools
const exaApiKey = process.env.EXA_API_KEY;
if (!exaApiKey) {
return new ChatSDKError('bad_request:api', 'EXA_API_KEY is not configured').toResponse();
return new ChatSDKError(
'bad_request:api',
'EXA_API_KEY is not configured',
).toResponse();
}

// Always use user ID as container tag
Expand All @@ -145,25 +147,30 @@ export async function POST(request: Request) {
const webSearchTool = createWebSearchTool(exaApiKey);

// Wrap the language model with supermemory
const baseModel = myProvider(session.user.id).languageModel(selectedChatModel);
const baseModel = myProvider(session.user.id).languageModel(
selectedChatModel,
);
const modelWithMemory = withSupermemory(baseModel, containerTag, {
conversationId: id,
mode: "full",
mode: 'full',
verbose: true,
addMemory: "always"
addMemory: 'always',
});

const toolsConfig = {
searchMemories: memoryTools.searchMemories,
webSearch: webSearchTool,
};

// Log what messages we're sending to AI SDK
const convertedMessages = convertToModelMessages(messages as any);
convertedMessages.forEach((msg, idx) => {
console.log(`[Chat API] Message ${idx}:`, {
role: msg.role,
content: typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content)
content:
typeof msg.content === 'string'
? msg.content
: JSON.stringify(msg.content),
});
});

Expand All @@ -178,20 +185,25 @@ export async function POST(request: Request) {
if (session.user?.id) {
try {
// Check if the response contains split delimiter
const splitMessages = text.split('<SPLIT>').map(t => t.trim()).filter(t => t.length > 0);

const splitMessages = text
.split('<SPLIT>')
.map((t) => t.trim())
.filter((t) => t.length > 0);

// If there are multiple messages, save them separately with small time delays
if (splitMessages.length > 1) {
const messagesToSave = splitMessages.map((messageText, index) => ({
id: generateUUID(),
chatId: id,
role: 'assistant' as const,
parts: [{ type: 'text' as const, text: messageText }],
attachments: [],
// Add small time increments to ensure correct ordering
createdAt: new Date(Date.now() + index * 100),
}));

const messagesToSave = splitMessages.map(
(messageText, index) => ({
id: generateUUID(),
chatId: id,
role: 'assistant' as const,
parts: [{ type: 'text' as const, text: messageText }],
attachments: [],
// Add small time increments to ensure correct ordering
createdAt: new Date(Date.now() + index * 100),
}),
);

await saveMessages({ messages: messagesToSave });
} else {
// Single message, save as before
Expand All @@ -217,7 +229,7 @@ export async function POST(request: Request) {
experimental_telemetry: {
isEnabled: isProductionEnvironment,
functionId: 'stream-text',
}
},
});

return result.toUIMessageStreamResponse();
Expand Down Expand Up @@ -254,4 +266,4 @@ export async function DELETE(request: Request) {

const deletedChat = await deleteChatById({ id });
return Response.json(deletedChat, { status: 200 });
}
}
16 changes: 16 additions & 0 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,22 @@
}
}

/* Staggered message animation - slide from below */
@keyframes slideInFromBottom {
0% {
opacity: 0;
transform: translateY(30px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}

.animate-message-in {
animation: slideInFromBottom 0.4s cubic-bezier(0.34, 0.69, 0.1, 1) both;
}

/* Global caret styling */
* {
caret-color: #0A7CFF;
Expand Down
Loading
Loading