diff options
| author | Dhravya Shah <[email protected]> | 2025-09-15 19:02:24 -0700 |
|---|---|---|
| committer | Dhravya Shah <[email protected]> | 2025-09-15 19:02:24 -0700 |
| commit | 27ea75b4a2d46de9d49388bb6a750d7a1bdb152a (patch) | |
| tree | 8aae321a3e57b7676dc79ccac2a09a85cc7e5556 | |
| parent | Merge branch 'main' of https://github.com/supermemoryai/supermemory (diff) | |
| download | supermemory-27ea75b4a2d46de9d49388bb6a750d7a1bdb152a.tar.xz supermemory-27ea75b4a2d46de9d49388bb6a750d7a1bdb152a.zip | |
change /memories to /documents, remove unwanted dependency, biome format
| -rw-r--r-- | CLAUDE.md | 4 | ||||
| -rw-r--r-- | apps/browser-extension/utils/api.ts | 4 | ||||
| -rw-r--r-- | apps/web/components/views/add-memory/index.tsx | 82 | ||||
| -rw-r--r-- | apps/web/components/views/mcp/index.tsx | 118 | ||||
| -rw-r--r-- | bun.lock | 3 | ||||
| -rw-r--r-- | package.json | 4 | ||||
| -rw-r--r-- | packages/lib/api.ts | 14 | ||||
| -rw-r--r-- | packages/lib/queries.ts | 6 |
8 files changed, 116 insertions, 119 deletions
@@ -37,7 +37,7 @@ This is a **Turbo monorepo** containing multiple applications and shared package The API serves as the core backend with these key features: **Key API Routes** -- `/v3/memories` - CRUD operations for documents/memories +- `/v3/documents` - CRUD operations for documents/memories - `/v3/search` - Semantic search across indexed content - `/v3/connections` - External service integrations (Google Drive, Notion, OneDrive) - `/v3/settings` - Organization and user settings @@ -116,4 +116,4 @@ All content goes through the `IngestContentWorkflow` which handles: ### Deployment - Cloudflare Workers for scalable serverless deployment - Source map uploads to Sentry for production debugging -- Environment-specific configuration management
\ No newline at end of file +- Environment-specific configuration management diff --git a/apps/browser-extension/utils/api.ts b/apps/browser-extension/utils/api.ts index 7e4de310..2a4c838b 100644 --- a/apps/browser-extension/utils/api.ts +++ b/apps/browser-extension/utils/api.ts @@ -104,7 +104,7 @@ export async function setDefaultProject(project: Project): Promise<void> { */ export async function saveMemory(payload: MemoryPayload): Promise<unknown> { try { - const response = await makeAuthenticatedRequest<unknown>("/v3/memories", { + const response = await makeAuthenticatedRequest<unknown>("/v3/documents", { method: "POST", body: JSON.stringify(payload), }) @@ -139,7 +139,7 @@ export async function saveAllTweets( ): Promise<unknown> { try { const response = await makeAuthenticatedRequest<unknown>( - "/v3/memories/batch", + "/v3/documents/batch", { method: "POST", body: JSON.stringify({ diff --git a/apps/web/components/views/add-memory/index.tsx b/apps/web/components/views/add-memory/index.tsx index 74469f3b..8bd6a0f0 100644 --- a/apps/web/components/views/add-memory/index.tsx +++ b/apps/web/components/views/add-memory/index.tsx @@ -33,6 +33,7 @@ import { UploadIcon, } from "lucide-react" import { AnimatePresence, motion } from "motion/react" +import dynamic from "next/dynamic" import { useEffect, useState } from "react" import { toast } from "sonner" import { z } from "zod" @@ -43,7 +44,6 @@ import { ActionButtons } from "./action-buttons" import { MemoryUsageRing } from "./memory-usage-ring" import { ProjectSelection } from "./project-selection" import { TabButton } from "./tab-button" -import dynamic from "next/dynamic" const TextEditor = dynamic( () => import("./text-editor").then((mod) => ({ default: mod.TextEditor })), @@ -236,7 +236,7 @@ export function AddMemoryView({ const processingPromise = (async () => { // First, create the memory - const response = await $fetch("@post/memories", { + const response = await $fetch("@post/documents", { body: { content: content, containerTags: [project], @@ -262,7 +262,7 @@ export function AddMemoryView({ while (attempts < maxAttempts) { try { const memory = await $fetch<{ status: string; content: string }>( - "@get/memories/" + memoryId, + `@get/documents/${memoryId}`, ) if (memory.error) { @@ -438,7 +438,7 @@ export function AddMemoryView({ formData.append("containerTags", JSON.stringify([project])) const response = await fetch( - `${process.env.NEXT_PUBLIC_BACKEND_URL}/v3/memories/file`, + `${process.env.NEXT_PUBLIC_BACKEND_URL}/v3/documents/file`, { method: "POST", body: formData, @@ -455,7 +455,7 @@ export function AddMemoryView({ // If we have metadata, we can update the document after creation if (title || description) { - await $fetch(`@patch/memories/${data.id}`, { + await $fetch(`@patch/documents/${data.id}`, { body: { metadata: { ...(title && { title }), @@ -648,12 +648,12 @@ export function AddMemoryView({ }`} > <TextEditor - value={state.value} - onChange={handleChange} + className="text-white" + disabled={addContentMutation.isPending} onBlur={handleBlur} + onChange={handleChange} placeholder="Write your note here..." - disabled={addContentMutation.isPending} - className="text-white" + value={state.value} /> </div> {state.meta.errors.length > 0 && ( @@ -692,36 +692,36 @@ export function AddMemoryView({ <addContentForm.Field name="project"> {({ state, handleChange }) => ( <ProjectSelection - projects={projects} - selectedProject={state.value} - onProjectChange={handleChange} + disabled={addContentMutation.isPending} + id="note-project" + isLoading={isLoadingProjects} onCreateProject={() => setShowCreateProjectDialog(true) } - disabled={addContentMutation.isPending} - isLoading={isLoadingProjects} - id="note-project" + onProjectChange={handleChange} + projects={projects} + selectedProject={state.value} /> )} </addContentForm.Field> </motion.div> <MemoryUsageRing - memoriesUsed={memoriesUsed} memoriesLimit={memoriesLimit} + memoriesUsed={memoriesUsed} /> </div> <ActionButtons + isSubmitDisabled={!addContentForm.state.canSubmit} + isSubmitting={addContentMutation.isPending} onCancel={() => { setShowAddDialog(false) onClose?.() addContentForm.reset() }} - submitText="Add Note" submitIcon={Plus} - isSubmitting={addContentMutation.isPending} - isSubmitDisabled={!addContentForm.state.canSubmit} + submitText="Add Note" /> </div> </form> @@ -818,36 +818,36 @@ export function AddMemoryView({ <addContentForm.Field name="project"> {({ state, handleChange }) => ( <ProjectSelection - projects={projects} - selectedProject={state.value} - onProjectChange={handleChange} + disabled={addContentMutation.isPending} + id="link-project-2" + isLoading={isLoadingProjects} onCreateProject={() => setShowCreateProjectDialog(true) } - disabled={addContentMutation.isPending} - isLoading={isLoadingProjects} - id="link-project-2" + onProjectChange={handleChange} + projects={projects} + selectedProject={state.value} /> )} </addContentForm.Field> </motion.div> <MemoryUsageRing - memoriesUsed={memoriesUsed} memoriesLimit={memoriesLimit} + memoriesUsed={memoriesUsed} /> </div> <ActionButtons + isSubmitDisabled={!addContentForm.state.canSubmit} + isSubmitting={addContentMutation.isPending} onCancel={() => { setShowAddDialog(false) onClose?.() addContentForm.reset() }} - submitText="Add Link" submitIcon={Plus} - isSubmitting={addContentMutation.isPending} - isSubmitDisabled={!addContentForm.state.canSubmit} + submitText="Add Link" /> </div> </form> @@ -970,37 +970,37 @@ export function AddMemoryView({ <fileUploadForm.Field name="project"> {({ state, handleChange }) => ( <ProjectSelection - projects={projects} - selectedProject={state.value} - onProjectChange={handleChange} + disabled={fileUploadMutation.isPending} + id="file-project" + isLoading={isLoadingProjects} onCreateProject={() => setShowCreateProjectDialog(true) } - disabled={fileUploadMutation.isPending} - isLoading={isLoadingProjects} - id="file-project" + onProjectChange={handleChange} + projects={projects} + selectedProject={state.value} /> )} </fileUploadForm.Field> </motion.div> <MemoryUsageRing - memoriesUsed={memoriesUsed} memoriesLimit={memoriesLimit} + memoriesUsed={memoriesUsed} /> </div> <ActionButtons + isSubmitDisabled={selectedFiles.length === 0} + isSubmitting={fileUploadMutation.isPending} onCancel={() => { setShowAddDialog(false) onClose?.() fileUploadForm.reset() setSelectedFiles([]) }} - submitText="Upload File" submitIcon={UploadIcon} - isSubmitting={fileUploadMutation.isPending} - isSubmitDisabled={selectedFiles.length === 0} + submitText="Upload File" /> </div> </form> @@ -1058,9 +1058,9 @@ export function AddMemoryView({ </div> <DialogFooter className="flex-col sm:flex-row gap-3 sm:gap-0"> <motion.div + className="w-full sm:w-auto" whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }} - className="w-full sm:w-auto" > <Button className="bg-white/5 hover:bg-white/10 border-white/10 text-white w-full sm:w-auto" @@ -1075,9 +1075,9 @@ export function AddMemoryView({ </Button> </motion.div> <motion.div + className="w-full sm:w-auto" whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }} - className="w-full sm:w-auto" > <Button className="bg-white/10 hover:bg-white/20 text-white border-white/20 w-full sm:w-auto" diff --git a/apps/web/components/views/mcp/index.tsx b/apps/web/components/views/mcp/index.tsx index d9ffbd1a..e79c2716 100644 --- a/apps/web/components/views/mcp/index.tsx +++ b/apps/web/components/views/mcp/index.tsx @@ -1,9 +1,9 @@ -import { $fetch } from "@lib/api"; -import { authClient } from "@lib/auth"; -import { useAuth } from "@lib/auth-context"; -import { useForm } from "@tanstack/react-form"; -import { useMutation } from "@tanstack/react-query"; -import { Button } from "@ui/components/button"; +import { $fetch } from "@lib/api" +import { authClient } from "@lib/auth" +import { useAuth } from "@lib/auth-context" +import { useForm } from "@tanstack/react-form" +import { useMutation } from "@tanstack/react-query" +import { Button } from "@ui/components/button" import { Dialog, DialogContent, @@ -11,18 +11,18 @@ import { DialogHeader, DialogTitle, DialogTrigger, -} from "@ui/components/dialog"; -import { Input } from "@ui/components/input"; -import { CopyableCell } from "@ui/copyable-cell"; -import { Loader2 } from "lucide-react"; -import { AnimatePresence, motion } from "motion/react"; -import Image from "next/image"; -import { generateSlug } from "random-word-slugs"; -import { useEffect, useState } from "react"; -import { toast } from "sonner"; -import { z } from "zod/v4"; -import { analytics } from "@/lib/analytics"; -import { InstallationDialogContent } from "./installation-dialog-content"; +} from "@ui/components/dialog" +import { Input } from "@ui/components/input" +import { CopyableCell } from "@ui/copyable-cell" +import { Loader2 } from "lucide-react" +import { AnimatePresence, motion } from "motion/react" +import Image from "next/image" +import { generateSlug } from "random-word-slugs" +import { useEffect, useState } from "react" +import { toast } from "sonner" +import { z } from "zod/v4" +import { analytics } from "@/lib/analytics" +import { InstallationDialogContent } from "./installation-dialog-content" // Validation schemas const mcpMigrationSchema = z.object({ @@ -33,56 +33,56 @@ const mcpMigrationSchema = z.object({ /^https:\/\/mcp\.supermemory\.ai\/[^/]+\/sse$/, "Link must be in format: https://mcp.supermemory.ai/userId/sse", ), -}); +}) export function MCPView() { - const [isMigrateDialogOpen, setIsMigrateDialogOpen] = useState(false); - const projectId = localStorage.getItem("selectedProject") ?? "default"; - const { org } = useAuth(); - const [apiKey, setApiKey] = useState<string>(); - const [isInstallDialogOpen, setIsInstallDialogOpen] = useState(false); + const [isMigrateDialogOpen, setIsMigrateDialogOpen] = useState(false) + const projectId = localStorage.getItem("selectedProject") ?? "default" + const { org } = useAuth() + const [apiKey, setApiKey] = useState<string>() + const [isInstallDialogOpen, setIsInstallDialogOpen] = useState(false) useEffect(() => { - analytics.mcpViewOpened(); - }, []); + analytics.mcpViewOpened() + }, []) const apiKeyMutation = useMutation({ mutationFn: async () => { - if (apiKey) return apiKey; + if (apiKey) return apiKey const res = await authClient.apiKey.create({ metadata: { organizationId: org?.id, }, name: generateSlug(), prefix: `sm_${org?.id}_`, - }); - return res.key; + }) + return res.key }, onSuccess: (data) => { - setApiKey(data); - setIsInstallDialogOpen(true); + setApiKey(data) + setIsInstallDialogOpen(true) }, - }); + }) // Form for MCP migration const mcpMigrationForm = useForm({ defaultValues: { url: "" }, onSubmit: async ({ value, formApi }) => { - const userId = extractUserIdFromMCPUrl(value.url); + const userId = extractUserIdFromMCPUrl(value.url) if (userId) { - migrateMCPMutation.mutate({ userId, projectId }); - formApi.reset(); + migrateMCPMutation.mutate({ userId, projectId }) + formApi.reset() } }, validators: { onChange: mcpMigrationSchema, }, - }); + }) const extractUserIdFromMCPUrl = (url: string): string | null => { - const regex = /^https:\/\/mcp\.supermemory\.ai\/([^/]+)\/sse$/; - const match = url.trim().match(regex); - return match?.[1] || null; - }; + const regex = /^https:\/\/mcp\.supermemory\.ai\/([^/]+)\/sse$/ + const match = url.trim().match(regex) + return match?.[1] || null + } // Migrate MCP mutation const migrateMCPMutation = useMutation({ @@ -90,33 +90,33 @@ export function MCPView() { userId, projectId, }: { - userId: string; - projectId: string; + userId: string + projectId: string }) => { - const response = await $fetch("@post/memories/migrate-mcp", { + const response = await $fetch("@post/documents/migrate-mcp", { body: { userId, projectId }, - }); + }) if (response.error) { throw new Error( response.error?.message || "Failed to migrate documents", - ); + ) } - return response.data; + return response.data }, onSuccess: (data) => { toast.success("Migration completed!", { description: `Successfully migrated ${data?.migratedCount} documents`, - }); - setIsMigrateDialogOpen(false); + }) + setIsMigrateDialogOpen(false) }, onError: (error) => { toast.error("Migration failed", { description: error instanceof Error ? error.message : "Unknown error", - }); + }) }, - }); + }) return ( <div className="space-y-6"> @@ -155,9 +155,9 @@ export function MCPView() { <Button disabled={apiKeyMutation.isPending} onClick={(e) => { - e.preventDefault(); - e.stopPropagation(); - apiKeyMutation.mutate(); + e.preventDefault() + e.stopPropagation() + apiKeyMutation.mutate() }} > Install Now @@ -213,9 +213,9 @@ export function MCPView() { </DialogHeader> <form onSubmit={(e) => { - e.preventDefault(); - e.stopPropagation(); - mcpMigrationForm.handleSubmit(); + e.preventDefault() + e.stopPropagation() + mcpMigrationForm.handleSubmit() }} > <div className="grid gap-4"> @@ -268,8 +268,8 @@ export function MCPView() { <Button className="bg-white/5 hover:bg-white/10 border-white/10 text-white" onClick={() => { - setIsMigrateDialogOpen(false); - mcpMigrationForm.reset(); + setIsMigrateDialogOpen(false) + mcpMigrationForm.reset() }} type="button" variant="outline" @@ -307,5 +307,5 @@ export function MCPView() { )} </AnimatePresence> </div> - ); + ) } @@ -26,7 +26,6 @@ "drizzle-zod": "~0.7.1", "file-type": "^21.0.0", "hono-openapi": "^0.4.8", - "llm-bridge": "1.0.8", "nanoid": "^5.1.5", "neverthrow": "^8.2.0", "pg": "^8.16.3", @@ -2703,8 +2702,6 @@ "listr2": ["[email protected]", "", { "dependencies": { "cli-truncate": "^4.0.0", "colorette": "^2.0.20", "eventemitter3": "^5.0.1", "log-update": "^6.1.0", "rfdc": "^1.4.1", "wrap-ansi": "^9.0.0" } }, "sha512-LWzX2KsqcB1wqQ4AHgYb4RsDXauQiqhjLk+6hjbaeHG4zpjjVAB6wC/gz6X0l+Du1cN3pUB5ZlrvTbhGSNnUQQ=="], - "llm-bridge": ["[email protected]", "", {}, "sha512-LSFld4m5WDAHNMkkMFn4nRbt3ks9Wx0H2CMaeFOVWTZFRKJB0AQgfD87AwkfnKdPwXAVaGK5rV5gp13WQSYVIg=="], - "load-json-file": ["[email protected]", "", { "dependencies": { "graceful-fs": "^4.1.2", "parse-json": "^4.0.0", "pify": "^3.0.0", "strip-bom": "^3.0.0" } }, "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw=="], "loader-runner": ["[email protected]", "", {}, "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg=="], diff --git a/package.json b/package.json index a7cc83e7..bb88f692 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "drizzle-zod": "~0.7.1", "file-type": "^21.0.0", "hono-openapi": "^0.4.8", - "llm-bridge": "1.0.8", + "nanoid": "^5.1.5", "neverthrow": "^8.2.0", "pg": "^8.16.3", @@ -65,4 +65,4 @@ "import": "./esm/index.mjs", "require": "./dist/index.js" } -}
\ No newline at end of file +} diff --git a/packages/lib/api.ts b/packages/lib/api.ts index e6ca8555..ad343050 100644 --- a/packages/lib/api.ts +++ b/packages/lib/api.ts @@ -127,11 +127,11 @@ export const apiSchema = createSchema({ output: SettingsResponseSchema, }, // Memory operations - "@post/memories": { + "@post/documents": { input: MemoryAddSchema, output: MemoryResponseSchema, }, - "@post/memories/list": { + "@post/documents/list": { body: z .object({ limit: z.number().optional(), @@ -142,11 +142,11 @@ export const apiSchema = createSchema({ .optional(), output: ListMemoriesResponseSchema, }, - "@post/memories/documents": { + "@post/documents/documents": { input: DocumentsWithMemoriesQuerySchema, output: DocumentsWithMemoriesResponseSchema, }, - "@post/memories/documents/by-ids": { + "@post/documents/documents/by-ids": { input: z.object({ ids: z.array(z.string()), by: z.enum(["id", "customId"]).optional(), @@ -154,13 +154,13 @@ export const apiSchema = createSchema({ }), output: DocumentsWithMemoriesResponseSchema, }, - "@post/memories/migrate-mcp": { + "@post/documents/migrate-mcp": { input: MigrateMCPRequestSchema, output: MigrateMCPResponseSchema, }, // Delete a memory - "@delete/memories/:id": { + "@delete/documents/:id": { output: z.any(), // 204 No-Content params: z.object({ id: z.string() }), }, @@ -200,7 +200,7 @@ export const apiSchema = createSchema({ output: z.object({ message: z.string(), }), - } + }, }) export const $fetch = createFetch({ diff --git a/packages/lib/queries.ts b/packages/lib/queries.ts index 0902f378..3e9e1ab9 100644 --- a/packages/lib/queries.ts +++ b/packages/lib/queries.ts @@ -1,9 +1,9 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query" import type { useCustomer } from "autumn-js/react" -import { $fetch } from "./api" import { toast } from "sonner" -import type { DocumentsWithMemoriesResponseSchema } from "../validation/api" import type { z } from "zod" +import type { DocumentsWithMemoriesResponseSchema } from "../validation/api" +import { $fetch } from "./api" type DocumentsResponse = z.infer<typeof DocumentsWithMemoriesResponseSchema> type DocumentWithMemories = DocumentsResponse["documents"][0] @@ -98,7 +98,7 @@ export const useDeleteDocument = (selectedProject: string) => { return useMutation({ mutationFn: async (documentId: string) => { // context for LLM: delete/memories/:documentId is documents delete endpoint not memories delete endpoint - const response = await $fetch(`@delete/memories/${documentId}`) + const response = await $fetch(`@delete/documents/${documentId}`) if (response.error) { throw new Error(response.error?.message || "Failed to delete document") } |