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" }}