change /memories to /documents, remove unwanted dependency, biome format

This commit is contained in:
Dhravya Shah 2025-09-15 19:02:24 -07:00
parent 8de2099e14
commit 27ea75b4a2
8 changed files with 117 additions and 120 deletions

View file

@ -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
- Environment-specific configuration management

View file

@ -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({

View file

@ -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}
onBlur={handleBlur}
placeholder="Write your note here..."
disabled={addContentMutation.isPending}
className="text-white"
disabled={addContentMutation.isPending}
onBlur={handleBlur}
onChange={handleChange}
placeholder="Write your note here..."
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"

View file

@ -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>
);
)
}

View file

@ -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": ["listr2@8.3.3", "", { "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": ["llm-bridge@1.0.8", "", {}, "sha512-LSFld4m5WDAHNMkkMFn4nRbt3ks9Wx0H2CMaeFOVWTZFRKJB0AQgfD87AwkfnKdPwXAVaGK5rV5gp13WQSYVIg=="],
"load-json-file": ["load-json-file@4.0.0", "", { "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": ["loader-runner@4.3.0", "", {}, "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg=="],

View file

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

View file

@ -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({

View file

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