From 5612380dc2c10ac95bd4c01544401e4ea3ac8291 Mon Sep 17 00:00:00 2001 From: Shoubhit Dash Date: Wed, 22 Oct 2025 22:43:09 +0530 Subject: fix prompt mutation in vercel middleware --- packages/tools/src/vercel/middleware.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'packages') diff --git a/packages/tools/src/vercel/middleware.ts b/packages/tools/src/vercel/middleware.ts index 0abebb5a..75d7a12d 100644 --- a/packages/tools/src/vercel/middleware.ts +++ b/packages/tools/src/vercel/middleware.ts @@ -9,7 +9,7 @@ import { convertProfileToMarkdown, type ProfileStructure } from "./util" const getLastUserMessage = (params: LanguageModelV2CallOptions) => { const lastUserMessage = params.prompt - .reverse() + .slice().reverse() .find((prompt: LanguageModelV2Message) => prompt.role === "user") const memories = lastUserMessage?.content .filter((content) => content.type === "text") @@ -70,7 +70,7 @@ const addSystemPrompt = async ( const queryText = mode !== "profile" ? params.prompt - .reverse() + .slice().reverse() .find((prompt) => prompt.role === "user") ?.content?.filter((content) => content.type === "text") ?.map((content) => (content.type === "text" ? content.text : "")) @@ -136,15 +136,21 @@ const getConversationContent = (params: LanguageModelV2CallOptions) => { return params.prompt .map((msg) => { const role = msg.role === "user" ? "User" : "Assistant" + + if (typeof msg.content === "string") { + return `${role}: ${msg.content}` + } + const content = msg.content .filter((c) => c.type === "text") - .map((c) => c.text) + .map((c) => (c.type === "text" ? c.text : "")) .join(" ") return `${role}: ${content}` }) .join("\n\n") } + const addMemoryTool = async ( client: Supermemory, containerTag: string, -- cgit v1.2.3 From 4365fbd2a2231f68d074d325e7447fb0e0baaefc Mon Sep 17 00:00:00 2001 From: Shoubhit Dash Date: Wed, 22 Oct 2025 22:58:32 +0530 Subject: add test --- packages/tools/test/vercel.test.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'packages') diff --git a/packages/tools/test/vercel.test.ts b/packages/tools/test/vercel.test.ts index 4db02394..32197977 100644 --- a/packages/tools/test/vercel.test.ts +++ b/packages/tools/test/vercel.test.ts @@ -521,5 +521,29 @@ describe("withSupermemory / wrapVercelLanguageModel", () => { containerTags: [customTag], }) }) + + it("should not mutate the original params.prompt array", async () => { + mockSupermemory.search.execute.mockResolvedValue( + createMockSearchResponse([]), + ) + + const middleware = createSupermemoryMiddleware( + mockSupermemory, + TEST_CONFIG.containerTag, + ) + + const originalPrompt = [ + { role: "user" as const, content: [{ type: "text" as const, text: "First" }] }, + { role: "user" as const, content: [{ type: "text" as const, text: "Last" }] } + ] + const params: LanguageModelV2CallOptions = { prompt: [...originalPrompt] } + + await callTransformParams(middleware, params) + + // Verify order is unchanged + expect(params.prompt[0]?.content[0]).toBe("First") + expect(params.prompt[1]?.content[0]).toBe("Last") + }) + }) }) -- cgit v1.2.3 From 8436229cbb1aa0b0d57ccd0e52816dd11b1f8899 Mon Sep 17 00:00:00 2001 From: Shoubhit Dash Date: Wed, 22 Oct 2025 23:00:08 +0530 Subject: add comment --- packages/tools/src/vercel/middleware.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'packages') diff --git a/packages/tools/src/vercel/middleware.ts b/packages/tools/src/vercel/middleware.ts index 75d7a12d..f6a346b8 100644 --- a/packages/tools/src/vercel/middleware.ts +++ b/packages/tools/src/vercel/middleware.ts @@ -70,7 +70,8 @@ const addSystemPrompt = async ( const queryText = mode !== "profile" ? params.prompt - .slice().reverse() + // Create a shallow copy before reversing to avoid mutating the original array + .slice().reverse() .find((prompt) => prompt.role === "user") ?.content?.filter((content) => content.type === "text") ?.map((content) => (content.type === "text" ? content.text : "")) -- cgit v1.2.3 From 5fb33743b38bfd4f04e972c2fe4ff7e011333de2 Mon Sep 17 00:00:00 2001 From: Mahesh Sanikommmu Date: Wed, 22 Oct 2025 12:31:54 -0700 Subject: add props interface export --- packages/tools/package.json | 2 +- packages/tools/src/vercel/index.ts | 16 +++++++++------- packages/tools/src/vercel/middleware.ts | 1 - 3 files changed, 10 insertions(+), 9 deletions(-) (limited to 'packages') diff --git a/packages/tools/package.json b/packages/tools/package.json index 9d820ddc..2a4f0a0a 100644 --- a/packages/tools/package.json +++ b/packages/tools/package.json @@ -1,7 +1,7 @@ { "name": "@supermemory/tools", "type": "module", - "version": "1.2.15", + "version": "1.2.16", "description": "Memory tools for AI SDK and OpenAI function calling with supermemory", "scripts": { "build": "tsdown", diff --git a/packages/tools/src/vercel/index.ts b/packages/tools/src/vercel/index.ts index 12717ac6..e216a363 100644 --- a/packages/tools/src/vercel/index.ts +++ b/packages/tools/src/vercel/index.ts @@ -2,6 +2,13 @@ import type { LanguageModelV2 } from "@ai-sdk/provider" import { wrapLanguageModel } from "ai" import { createSupermemoryMiddleware } from "./middleware" +interface WrapVercelLanguageModelOptions { + conversationId?: string; + verbose?: boolean; + mode?: "profile" | "query" | "full"; + addMemory?: "always" | "never"; +} + /** * Wraps a language model with supermemory middleware to automatically inject relevant memories * into the system prompt based on the user's message content. @@ -43,12 +50,7 @@ import { createSupermemoryMiddleware } from "./middleware" const wrapVercelLanguageModel = ( model: LanguageModelV2, containerTag: string, - options?: { - conversationId?: string; - verbose?: boolean; - mode?: "profile" | "query" | "full"; - addMemory?: "always" | "never"; - }, + options?: WrapVercelLanguageModelOptions, ): LanguageModelV2 => { const SUPERMEMORY_API_KEY = process.env.SUPERMEMORY_API_KEY @@ -69,4 +71,4 @@ const wrapVercelLanguageModel = ( return wrappedModel } -export { wrapVercelLanguageModel as withSupermemory } +export { wrapVercelLanguageModel as withSupermemory, type WrapVercelLanguageModelOptions as WithSupermemoryOptions } diff --git a/packages/tools/src/vercel/middleware.ts b/packages/tools/src/vercel/middleware.ts index f6a346b8..37f48994 100644 --- a/packages/tools/src/vercel/middleware.ts +++ b/packages/tools/src/vercel/middleware.ts @@ -70,7 +70,6 @@ const addSystemPrompt = async ( const queryText = mode !== "profile" ? params.prompt - // Create a shallow copy before reversing to avoid mutating the original array .slice().reverse() .find((prompt) => prompt.role === "user") ?.content?.filter((content) => content.type === "text") -- cgit v1.2.3 From 4f2d8394129f9b51a3f415ce9aa6923a277be56e Mon Sep 17 00:00:00 2001 From: Mahesh Sanikommmu Date: Wed, 22 Oct 2025 14:00:59 -0700 Subject: chat app withSupermemory test --- packages/tools/src/vercel/middleware.ts | 2 +- packages/tools/test/chatapp/.gitignore | 41 +++++++ packages/tools/test/chatapp/README.md | 77 +++++++++++++ packages/tools/test/chatapp/app/api/chat/route.ts | 23 ++++ packages/tools/test/chatapp/app/favicon.ico | Bin 0 -> 25931 bytes packages/tools/test/chatapp/app/globals.css | 26 +++++ packages/tools/test/chatapp/app/layout.tsx | 34 ++++++ packages/tools/test/chatapp/app/page.tsx | 129 ++++++++++++++++++++++ packages/tools/test/chatapp/eslint.config.mjs | 18 +++ packages/tools/test/chatapp/next.config.ts | 7 ++ packages/tools/test/chatapp/package.json | 30 +++++ packages/tools/test/chatapp/postcss.config.mjs | 7 ++ packages/tools/test/chatapp/public/file.svg | 1 + packages/tools/test/chatapp/public/globe.svg | 1 + packages/tools/test/chatapp/public/next.svg | 1 + packages/tools/test/chatapp/public/vercel.svg | 1 + packages/tools/test/chatapp/public/window.svg | 1 + packages/tools/test/chatapp/tsconfig.json | 34 ++++++ 18 files changed, 432 insertions(+), 1 deletion(-) create mode 100644 packages/tools/test/chatapp/.gitignore create mode 100644 packages/tools/test/chatapp/README.md create mode 100644 packages/tools/test/chatapp/app/api/chat/route.ts create mode 100644 packages/tools/test/chatapp/app/favicon.ico create mode 100644 packages/tools/test/chatapp/app/globals.css create mode 100644 packages/tools/test/chatapp/app/layout.tsx create mode 100644 packages/tools/test/chatapp/app/page.tsx create mode 100644 packages/tools/test/chatapp/eslint.config.mjs create mode 100644 packages/tools/test/chatapp/next.config.ts create mode 100644 packages/tools/test/chatapp/package.json create mode 100644 packages/tools/test/chatapp/postcss.config.mjs create mode 100644 packages/tools/test/chatapp/public/file.svg create mode 100644 packages/tools/test/chatapp/public/globe.svg create mode 100644 packages/tools/test/chatapp/public/next.svg create mode 100644 packages/tools/test/chatapp/public/vercel.svg create mode 100644 packages/tools/test/chatapp/public/window.svg create mode 100644 packages/tools/test/chatapp/tsconfig.json (limited to 'packages') diff --git a/packages/tools/src/vercel/middleware.ts b/packages/tools/src/vercel/middleware.ts index 37f48994..586105a8 100644 --- a/packages/tools/src/vercel/middleware.ts +++ b/packages/tools/src/vercel/middleware.ts @@ -106,7 +106,7 @@ const addSystemPrompt = async ( const memories = `${profileData}\n${searchResultsMemories}`.trim() if (memories) { logger.debug("Memory content preview", { - content: memories.substring(0, 200), + content: memories, fullLength: memories.length, }) } diff --git a/packages/tools/test/chatapp/.gitignore b/packages/tools/test/chatapp/.gitignore new file mode 100644 index 00000000..5ef6a520 --- /dev/null +++ b/packages/tools/test/chatapp/.gitignore @@ -0,0 +1,41 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/packages/tools/test/chatapp/README.md b/packages/tools/test/chatapp/README.md new file mode 100644 index 00000000..f72d8b45 --- /dev/null +++ b/packages/tools/test/chatapp/README.md @@ -0,0 +1,77 @@ +# Chat App - Test Application + +This is a basic Next.js chat application created for testing the Supermemory packages and tools. It demonstrates how to integrate Supermemory with a simple chat interface. + +## Features + +- **Real-time Chat Interface**: Clean, modern chat UI with message bubbles +- **Supermemory Integration**: Uses Supermemory tools for enhanced AI conversations +- **Responsive Design**: Works on desktop and mobile devices +- **Dark Mode Support**: Automatic theme switching based on system preferences +- **Loading States**: Visual feedback during AI processing +- **Error Handling**: Graceful error handling for API failures + +## Tech Stack + +- **Next.js 16**: React framework with App Router +- **React 19**: Latest React with concurrent features +- **TypeScript**: Full type safety +- **Tailwind CSS**: Utility-first styling +- **AI SDK**: OpenAI integration for chat functionality +- **Supermemory**: Memory management and retrieval + +## Getting Started + +1. **Install Dependencies**: + ```bash + npm install + ``` + +2. **Set up Environment Variables**: + Create a `.env.local` file with your API keys: + ```env + OPENAI_API_KEY=your_openai_api_key_here + SUPERMEMORY_API_KEY=your_supermemory_api_key_here + ``` + +3. **Run the Development Server**: + ```bash + npm run dev + ``` + +4. **Open in Browser**: + Navigate to [http://localhost:3000](http://localhost:3000) + +## Usage + +- Type a message in the input field and press Enter or click Send +- The AI will respond using Supermemory for context-aware conversations +- Messages are displayed in a chat bubble format +- Loading indicator shows when the AI is processing + +## Testing Supermemory Features + +This app is specifically designed to test: +- Memory storage and retrieval +- Context-aware conversations +- Package integration +- API functionality +- UI/UX patterns + +## Development + +This is a test application and should not be used in production. It's designed for: +- Testing Supermemory package functionality +- Demonstrating integration patterns +- UI/UX experimentation +- Development and debugging + +## File Structure + +``` +app/ +├── api/chat/route.ts # API endpoint for chat functionality +├── globals.css # Global styles and Tailwind imports +├── layout.tsx # Root layout with fonts and metadata +└── page.tsx # Main chat interface component +``` diff --git a/packages/tools/test/chatapp/app/api/chat/route.ts b/packages/tools/test/chatapp/app/api/chat/route.ts new file mode 100644 index 00000000..7fddcbec --- /dev/null +++ b/packages/tools/test/chatapp/app/api/chat/route.ts @@ -0,0 +1,23 @@ +import { generateText, type ModelMessage } from "ai" +import { openai } from "@ai-sdk/openai" +import { withSupermemory } from "../../../../../src/vercel" + +const model = withSupermemory(openai("gpt-4"), "user-123", { + mode: "full", + addMemory: "always", + conversationId: "chat-session", + verbose: true, +}) + + +export async function POST(req: Request) { + const { messages }: { messages: ModelMessage[] } = await req.json() + + const { response } = await generateText({ + model, + system: "You are a helpful assistant.", + messages, + }) + + return Response.json({ messages: response.messages }) +} \ No newline at end of file diff --git a/packages/tools/test/chatapp/app/favicon.ico b/packages/tools/test/chatapp/app/favicon.ico new file mode 100644 index 00000000..718d6fea Binary files /dev/null and b/packages/tools/test/chatapp/app/favicon.ico differ diff --git a/packages/tools/test/chatapp/app/globals.css b/packages/tools/test/chatapp/app/globals.css new file mode 100644 index 00000000..a2dc41ec --- /dev/null +++ b/packages/tools/test/chatapp/app/globals.css @@ -0,0 +1,26 @@ +@import "tailwindcss"; + +:root { + --background: #ffffff; + --foreground: #171717; +} + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --font-sans: var(--font-geist-sans); + --font-mono: var(--font-geist-mono); +} + +@media (prefers-color-scheme: dark) { + :root { + --background: #0a0a0a; + --foreground: #ededed; + } +} + +body { + background: var(--background); + color: var(--foreground); + font-family: Arial, Helvetica, sans-serif; +} diff --git a/packages/tools/test/chatapp/app/layout.tsx b/packages/tools/test/chatapp/app/layout.tsx new file mode 100644 index 00000000..f7fa87eb --- /dev/null +++ b/packages/tools/test/chatapp/app/layout.tsx @@ -0,0 +1,34 @@ +import type { Metadata } from "next"; +import { Geist, Geist_Mono } from "next/font/google"; +import "./globals.css"; + +const geistSans = Geist({ + variable: "--font-geist-sans", + subsets: ["latin"], +}); + +const geistMono = Geist_Mono({ + variable: "--font-geist-mono", + subsets: ["latin"], +}); + +export const metadata: Metadata = { + title: "Create Next App", + description: "Generated by create next app", +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + {children} + + + ); +} diff --git a/packages/tools/test/chatapp/app/page.tsx b/packages/tools/test/chatapp/app/page.tsx new file mode 100644 index 00000000..6eea7cfc --- /dev/null +++ b/packages/tools/test/chatapp/app/page.tsx @@ -0,0 +1,129 @@ +"use client" + +import type { ModelMessage } from "ai" +import { useState } from "react" + +export default function Page() { + const [input, setInput] = useState("") + const [messages, setMessages] = useState([]) + const [isLoading, setIsLoading] = useState(false) + + const handleSendMessage = async () => { + if (!input.trim() || isLoading) return + + const userMessage = { role: "user" as const, content: input } + setMessages((currentMessages) => [...currentMessages, userMessage]) + setInput("") + setIsLoading(true) + + try { + const response = await fetch("/api/chat", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + messages: [...messages, userMessage], + }), + }) + + const { messages: newMessages } = await response.json() + setMessages((currentMessages) => [...currentMessages, ...newMessages]) + } catch (error) { + console.error("Error sending message:", error) + } finally { + setIsLoading(false) + } + } + + return ( +
+
+ {/* Header */} +
+

Chat App

+

+ Chat with AI using Supermemory +

+
+ + {/* Messages Container */} +
+ {messages.length === 0 && ( +
+ Start a conversation by typing a message below +
+ )} + {messages.map((message, index) => ( +
+
+
+ {message.role} +
+
+ {typeof message.content === "string" + ? message.content + : message.content + .filter((part) => part.type === "text") + .map((part, partIndex) => ( +
+ {part.text} +
+ ))} +
+
+
+ ))} + {isLoading && ( +
+
+
+
+ + AI is thinking... + +
+
+
+ )} +
+ + {/* Input Container */} +
+ setInput(event.target.value)} + onKeyDown={(event) => { + if (event.key === "Enter" && !event.shiftKey) { + event.preventDefault() + handleSendMessage() + } + }} + placeholder="Type your message here..." + disabled={isLoading} + className="flex-1 px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent bg-background text-foreground placeholder:text-muted-foreground disabled:opacity-50 disabled:cursor-not-allowed" + /> + +
+
+
+ ) +} diff --git a/packages/tools/test/chatapp/eslint.config.mjs b/packages/tools/test/chatapp/eslint.config.mjs new file mode 100644 index 00000000..05e726d1 --- /dev/null +++ b/packages/tools/test/chatapp/eslint.config.mjs @@ -0,0 +1,18 @@ +import { defineConfig, globalIgnores } from "eslint/config"; +import nextVitals from "eslint-config-next/core-web-vitals"; +import nextTs from "eslint-config-next/typescript"; + +const eslintConfig = defineConfig([ + ...nextVitals, + ...nextTs, + // Override default ignores of eslint-config-next. + globalIgnores([ + // Default ignores of eslint-config-next: + ".next/**", + "out/**", + "build/**", + "next-env.d.ts", + ]), +]); + +export default eslintConfig; diff --git a/packages/tools/test/chatapp/next.config.ts b/packages/tools/test/chatapp/next.config.ts new file mode 100644 index 00000000..e9ffa308 --- /dev/null +++ b/packages/tools/test/chatapp/next.config.ts @@ -0,0 +1,7 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + /* config options here */ +}; + +export default nextConfig; diff --git a/packages/tools/test/chatapp/package.json b/packages/tools/test/chatapp/package.json new file mode 100644 index 00000000..746ca050 --- /dev/null +++ b/packages/tools/test/chatapp/package.json @@ -0,0 +1,30 @@ +{ + "name": "chatapp", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "eslint" + }, + "dependencies": { + "react": "19.2.0", + "react-dom": "19.2.0", + "next": "16.0.0", + "ai": "^4.0.0", + "@ai-sdk/openai": "^1.0.0", + "supermemory": "^1.0.0", + "@supermemory/tools": "workspace:*" + }, + "devDependencies": { + "typescript": "^5", + "@types/node": "^20", + "@types/react": "^19", + "@types/react-dom": "^19", + "@tailwindcss/postcss": "^4", + "tailwindcss": "^4", + "eslint": "^9", + "eslint-config-next": "16.0.0" + } +} diff --git a/packages/tools/test/chatapp/postcss.config.mjs b/packages/tools/test/chatapp/postcss.config.mjs new file mode 100644 index 00000000..61e36849 --- /dev/null +++ b/packages/tools/test/chatapp/postcss.config.mjs @@ -0,0 +1,7 @@ +const config = { + plugins: { + "@tailwindcss/postcss": {}, + }, +}; + +export default config; diff --git a/packages/tools/test/chatapp/public/file.svg b/packages/tools/test/chatapp/public/file.svg new file mode 100644 index 00000000..004145cd --- /dev/null +++ b/packages/tools/test/chatapp/public/file.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/tools/test/chatapp/public/globe.svg b/packages/tools/test/chatapp/public/globe.svg new file mode 100644 index 00000000..567f17b0 --- /dev/null +++ b/packages/tools/test/chatapp/public/globe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/tools/test/chatapp/public/next.svg b/packages/tools/test/chatapp/public/next.svg new file mode 100644 index 00000000..5174b28c --- /dev/null +++ b/packages/tools/test/chatapp/public/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/tools/test/chatapp/public/vercel.svg b/packages/tools/test/chatapp/public/vercel.svg new file mode 100644 index 00000000..77053960 --- /dev/null +++ b/packages/tools/test/chatapp/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/tools/test/chatapp/public/window.svg b/packages/tools/test/chatapp/public/window.svg new file mode 100644 index 00000000..b2b2a44f --- /dev/null +++ b/packages/tools/test/chatapp/public/window.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/tools/test/chatapp/tsconfig.json b/packages/tools/test/chatapp/tsconfig.json new file mode 100644 index 00000000..3a13f90a --- /dev/null +++ b/packages/tools/test/chatapp/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"] + } + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts", + "**/*.mts" + ], + "exclude": ["node_modules"] +} -- cgit v1.2.3