Save chat etc
'use server';
import { generateId } from 'ai';
import { existsSync, mkdirSync } from 'fs';
import { writeFile, readFile } from 'fs/promises';
import path from 'path';
import { Message } from 'ai';
export async function createChat(): Promise<string> {
try {
const id = generateId(); // generate a unique chat ID
const chatFilePath = getChatFile(id);
await writeFile(chatFilePath, '[]'); // create an empty chat file
return id;
} catch (error) {
throw new Error('Failed to create chat');
}
}
function getChatFile(id: string): string {
const chatDir = path.join(process.cwd(), '.chats');
if (!existsSync(chatDir)) {
mkdirSync(chatDir, { recursive: true });
}
return path.join(chatDir, `${id}.json`);
}
export async function loadChat(id: string): Promise<Message[]> {
try {
const chatFilePath = getChatFile(id);
const data = await readFile(chatFilePath, 'utf8');
const messages = JSON.parse(data);
return messages;
} catch (error) {
throw new Error('Failed to load chat');
}
}
export async function saveChat({
id,
messages,
}: {
id: string;
messages: Message[];
}): Promise<void> {
try {
const content = JSON.stringify(messages, null, 2);
const chatFilePath = getChatFile(id);
await writeFile(chatFilePath, content);
} catch (error) {
throw new Error('Failed to save chat');
}
}
The specific message is omitted in production builds to avoid leaking sensitive details. A digest property is included on this error instance which may provide additional details about the nature of the error.
{
"name": "egpyt-gpt-chat",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"browser": {
"fs": false,
"path": false,
"fs/promises": false,
"os": false
},
"dependencies": {
"@ai-sdk/deepseek": "^0.1.8",
"@ai-sdk/fal": "^0.0.2",
"@ai-sdk/fireworks": "^0.1.8",
"@ai-sdk/google": "^1.1.8",
"@ai-sdk/groq": "^1.1.7",
"@ai-sdk/openai": "^1.1.9",
"@codemirror/highlight": "^0.19.8",
"@codemirror/lang-python": "^6.1.7",
"@codemirror/theme-one-dark": "^6.1.2",
"@fal-ai/client": "^1.2.3",
"@heroui/react": "^2.6.14",
"@iconify/react": "^5.2.0",
"@mdx-js/loader": "^3.1.0",
"@mdx-js/react": "^3.1.0",
"@nanostores/react": "github:ai/react",
"@next/env": "^15.1.6",
"@next/mdx": "^15.1.6",
"@prose-ui/core": "^1.0.4",
"@prose-ui/next": "^1.0.4",
"@radix-ui/react-avatar": "^1.1.2",
"@radix-ui/react-collapsible": "^1.1.2",
"@radix-ui/react-dialog": "^1.1.5",
"@radix-ui/react-dropdown-menu": "^2.1.5",
"@radix-ui/react-separator": "^1.1.1",
"@radix-ui/react-slot": "^1.1.1",
"@radix-ui/react-switch": "^1.1.2",
"@radix-ui/react-tooltip": "^1.1.7",
"@react-aria/ssr": "^3.9.7",
"@tabler/icons-react": "^3.29.0",
"@tiptap/core": "^2.11.5",
"@tiptap/react": "^2.11.5",
"@tiptap/starter-kit": "^2.11.5",
"@types/mdx": "^2.0.13",
"@types/react-syntax-highlighter": "^15.5.13",
"ai": "^4.1.16",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"codemirror": "^6.0.1",
"date-fns": "^4.1.0",
"framer-motion": "^11.18.2",
"fs": "^0.0.1-security",
"geist": "^1.3.1",
"highlight.js": "^11.11.1",
"js-cookie": "^3.0.5",
"lucide-react": "^0.474.0",
"marked": "^15.0.7",
"next": "14.2.16",
"next-auth": "^4.24.11",
"next-themes": "^0.4.4",
"react": "^18",
"react-copy-to-clipboard": "^5.1.0",
"react-dom": "^18",
"react-markdown": "^9.0.3",
"react-remarkable": "^1.1.3",
"react-resizable": "^3.0.5",
"react-syntax-highlighter": "^15.6.1",
"rehype-highlight": "^7.0.2",
"remark-gfm": "^4.0.0",
"remarkable-react": "^1.4.3",
"remeda": "^2.20.0",
"shiki": "^2.3.2",
"sonner": "^1.7.4",
"tailwind-merge": "^3.0.1",
"tailwindcss-animate": "^1.0.7",
"tiptap-markdown": "^0.3.4",
"usehooks-ts": "^3.1.1"
},
"devDependencies": {
"@iconify-json/solar": "^1.2.2",
"@types/js-cookie": "^3.0.6",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-copy-to-clipboard": "^5.0.7",
"@types/react-dom": "^18",
"@types/react-resizable": "^3.0.8",
"@types/remarkable": "^2.0.8",
"eslint": "^8",
"eslint-config-next": "14.2.16",
"postcss": "^8",
"tailwindcss": "^3.4.1",
"typescript": "^5"
}
}