aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/web/components/views/add-memory/index.tsx625
-rw-r--r--packages/ai-sdk/src/index.ts2
-rw-r--r--packages/validation/schemas.ts92
3 files changed, 378 insertions, 341 deletions
diff --git a/apps/web/components/views/add-memory/index.tsx b/apps/web/components/views/add-memory/index.tsx
index 8bd6a0f0..5116e952 100644
--- a/apps/web/components/views/add-memory/index.tsx
+++ b/apps/web/components/views/add-memory/index.tsx
@@ -1,9 +1,9 @@
-import { $fetch } from "@lib/api"
+import { $fetch } from "@lib/api";
import {
fetchConsumerProProduct,
fetchMemoriesFeature,
-} from "@repo/lib/queries"
-import { Button } from "@repo/ui/components/button"
+} from "@repo/lib/queries";
+import { Button } from "@repo/ui/components/button";
import {
Dialog,
DialogContent,
@@ -11,18 +11,18 @@ import {
DialogFooter,
DialogHeader,
DialogTitle,
-} from "@repo/ui/components/dialog"
-import { Input } from "@repo/ui/components/input"
-import { Label } from "@repo/ui/components/label"
-import { Textarea } from "@repo/ui/components/textarea"
-import { useForm } from "@tanstack/react-form"
-import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
+} from "@repo/ui/components/dialog";
+import { Input } from "@repo/ui/components/input";
+import { Label } from "@repo/ui/components/label";
+import { Textarea } from "@repo/ui/components/textarea";
+import { useForm } from "@tanstack/react-form";
+import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import {
Dropzone,
DropzoneContent,
DropzoneEmptyState,
-} from "@ui/components/shadcn-io/dropzone"
-import { useCustomer } from "autumn-js/react"
+} from "@ui/components/shadcn-io/dropzone";
+import { useCustomer } from "autumn-js/react";
import {
Brain,
FileIcon,
@@ -31,19 +31,19 @@ import {
PlugIcon,
Plus,
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"
-import { analytics } from "@/lib/analytics"
-import { useProject } from "@/stores"
-import { ConnectionsTabContent } from "../connections-tab-content"
-import { ActionButtons } from "./action-buttons"
-import { MemoryUsageRing } from "./memory-usage-ring"
-import { ProjectSelection } from "./project-selection"
-import { TabButton } from "./tab-button"
+} 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";
+import { analytics } from "@/lib/analytics";
+import { useProject } from "@/stores";
+import { ConnectionsTabContent } from "../connections-tab-content";
+import { ActionButtons } from "./action-buttons";
+import { MemoryUsageRing } from "./memory-usage-ring";
+import { ProjectSelection } from "./project-selection";
+import { TabButton } from "./tab-button";
const TextEditor = dynamic(
() => import("./text-editor").then((mod) => ({ default: mod.TextEditor })),
@@ -64,102 +64,91 @@ const TextEditor = dynamic(
),
ssr: false,
},
-)
-
-// // Processing status component
-// function ProcessingStatus({ status }: { status: string }) {
-// const statusConfig = {
-// queued: { color: "text-yellow-400", label: "Queued", icon: "⏳" },
-// extracting: { color: "text-blue-400", label: "Extracting", icon: "📤" },
-// chunking: { color: "text-indigo-400", label: "Chunking", icon: "✂️" },
-// embedding: { color: "text-purple-400", label: "Embedding", icon: "🧠" },
-// indexing: { color: "text-pink-400", label: "Indexing", icon: "📝" },
-// unknown: { color: "text-gray-400", label: "Processing", icon: "⚙️" },
-// }
-
-// const config =
-// statusConfig[status as keyof typeof statusConfig] || statusConfig.unknown
-
-// return (
-// <div className={`flex items-center gap-1 text-xs ${config.color}`}>
-// <span>{config.icon}</span>
-// <span>{config.label}</span>
-// </div>
-// )
-// }
+);
+
+// Simple function to extract plain text title from HTML content
+const getPlainTextTitle = (htmlContent: string) => {
+ const temp = document.createElement("div");
+ temp.innerHTML = htmlContent;
+ const plainText = temp.textContent || temp.innerText || htmlContent;
+ const firstLine = plainText.split("\n")[0].trim();
+ return firstLine.length > 0
+ ? firstLine.substring(0, 100)
+ : plainText.substring(0, 100);
+};
export function AddMemoryView({
onClose,
initialTab = "note",
}: {
- onClose?: () => void
- initialTab?: "note" | "link" | "file" | "connect"
+ onClose?: () => void;
+ initialTab?: "note" | "link" | "file" | "connect";
}) {
- const queryClient = useQueryClient()
- const { selectedProject, setSelectedProject } = useProject()
- const [showAddDialog, setShowAddDialog] = useState(true)
- const [selectedFiles, setSelectedFiles] = useState<File[]>([])
+ const queryClient = useQueryClient();
+ const { selectedProject, setSelectedProject } = useProject();
+ const [showAddDialog, setShowAddDialog] = useState(true);
+ const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
const [activeTab, setActiveTab] = useState<
"note" | "link" | "file" | "connect"
- >(initialTab)
- const autumn = useCustomer()
- const [showCreateProjectDialog, setShowCreateProjectDialog] = useState(false)
- const [newProjectName, setNewProjectName] = useState("")
+ >(initialTab);
+ const autumn = useCustomer();
+ const [showCreateProjectDialog, setShowCreateProjectDialog] = useState(false);
+ const [newProjectName, setNewProjectName] = useState("");
// Check memory limits
- const { data: memoriesCheck } = fetchMemoriesFeature(autumn)
+ const { data: memoriesCheck } = fetchMemoriesFeature(autumn);
- const memoriesUsed = memoriesCheck?.usage ?? 0
- const memoriesLimit = memoriesCheck?.included_usage ?? 0
+ const memoriesUsed = memoriesCheck?.usage ?? 0;
+ const memoriesLimit = memoriesCheck?.included_usage ?? 0;
// Fetch projects for the dropdown
const { data: projects = [], isLoading: isLoadingProjects } = useQuery({
queryKey: ["projects"],
queryFn: async () => {
- const response = await $fetch("@get/projects")
+ const response = await $fetch("@get/projects");
if (response.error) {
- throw new Error(response.error?.message || "Failed to load projects")
+ throw new Error(response.error?.message || "Failed to load projects");
}
- return response.data?.projects || []
+ return response.data?.projects || [];
},
staleTime: 30 * 1000,
- })
+ });
// Create project mutation
const createProjectMutation = useMutation({
mutationFn: async (name: string) => {
const response = await $fetch("@post/projects", {
body: { name },
- })
+ });
if (response.error) {
- throw new Error(response.error?.message || "Failed to create project")
+ throw new Error(response.error?.message || "Failed to create project");
}
- return response.data
+ return response.data;
},
onSuccess: (data) => {
- analytics.projectCreated()
- toast.success("Project created successfully!")
- setShowCreateProjectDialog(false)
- setNewProjectName("")
- queryClient.invalidateQueries({ queryKey: ["projects"] })
+ analytics.projectCreated();
+ toast.success("Project created successfully!");
+ setShowCreateProjectDialog(false);
+ setNewProjectName("");
+ queryClient.invalidateQueries({ queryKey: ["projects"] });
// Set the newly created project as selected
if (data?.containerTag) {
- setSelectedProject(data.containerTag)
+ setSelectedProject(data.containerTag);
// Update form values
- addContentForm.setFieldValue("project", data.containerTag)
- fileUploadForm.setFieldValue("project", data.containerTag)
+ addContentForm.setFieldValue("project", data.containerTag);
+ fileUploadForm.setFieldValue("project", data.containerTag);
}
},
onError: (error) => {
toast.error("Failed to create project", {
description: error instanceof Error ? error.message : "Unknown error",
- })
+ });
},
- })
+ });
const addContentForm = useForm({
defaultValues: {
@@ -171,8 +160,8 @@ export function AddMemoryView({
content: value.content,
project: value.project,
contentType: activeTab as "note" | "link",
- })
- formApi.reset()
+ });
+ formApi.reset();
},
validators: {
onChange: z.object({
@@ -180,19 +169,19 @@ export function AddMemoryView({
project: z.string(),
}),
},
- })
+ });
// Re-validate content field when tab changes between note/link
// biome-ignore lint/correctness/useExhaustiveDependencies: It is what it is
useEffect(() => {
// Trigger validation of the content field when switching between note/link
if (activeTab === "note" || activeTab === "link") {
- const currentValue = addContentForm.getFieldValue("content")
+ const currentValue = addContentForm.getFieldValue("content");
if (currentValue) {
- addContentForm.validateField("content", "change")
+ addContentForm.validateField("content", "change");
}
}
- }, [activeTab])
+ }, [activeTab]);
// Form for file upload metadata
const fileUploadForm = useForm({
@@ -203,8 +192,8 @@ export function AddMemoryView({
},
onSubmit: async ({ value, formApi }) => {
if (selectedFiles.length === 0) {
- toast.error("Please select a file to upload")
- return
+ toast.error("Please select a file to upload");
+ return;
}
for (const file of selectedFiles) {
@@ -213,13 +202,13 @@ export function AddMemoryView({
title: value.title || undefined,
description: value.description || undefined,
project: value.project,
- })
+ });
}
- formApi.reset()
- setSelectedFiles([])
+ formApi.reset();
+ setSelectedFiles([]);
},
- })
+ });
const addContentMutation = useMutation({
mutationFn: async ({
@@ -227,191 +216,212 @@ export function AddMemoryView({
project,
contentType,
}: {
- content: string
- project: string
- contentType: "note" | "link"
+ content: string;
+ project: string;
+ contentType: "note" | "link";
}) => {
- // close the modal
- onClose?.()
-
- const processingPromise = (async () => {
- // First, create the memory
- const response = await $fetch("@post/documents", {
- body: {
- content: content,
- containerTags: [project],
- metadata: {
- sm_source: "consumer", // Use "consumer" source to bypass limits
- },
+ console.log("📤 Creating memory...");
+
+ const response = await $fetch("@post/documents", {
+ body: {
+ content: content,
+ containerTags: [project],
+ metadata: {
+ sm_source: "consumer",
},
- })
-
- if (response.error) {
- throw new Error(
- response.error?.message || `Failed to add ${contentType}`,
- )
- }
-
- const memoryId = response.data.id
-
- // Polling function to check status
- const pollForCompletion = async (): Promise<any> => {
- let attempts = 0
- const maxAttempts = 60 // Maximum 5 minutes (60 attempts * 5 seconds)
-
- while (attempts < maxAttempts) {
- try {
- const memory = await $fetch<{ status: string; content: string }>(
- `@get/documents/${memoryId}`,
- )
-
- if (memory.error) {
- throw new Error(
- memory.error?.message || "Failed to fetch memory status",
- )
- }
-
- // Check if processing is complete
- // Adjust this condition based on your API response structure
- if (
- memory.data?.status === "done" ||
- // Sometimes the memory might be ready when it has content and no processing status
- memory.data?.content
- ) {
- return memory.data
- }
-
- // If still processing, wait and try again
- await new Promise((resolve) => setTimeout(resolve, 5000)) // Wait 5 seconds
- attempts++
- } catch (error) {
- console.error("Error polling memory status:", error)
- // Don't throw immediately, retry a few times
- if (attempts >= 3) {
- throw new Error("Failed to check processing status")
- }
- await new Promise((resolve) => setTimeout(resolve, 5000))
- attempts++
- }
- }
-
- // If we've exceeded max attempts, throw an error
- throw new Error(
- "Memory processing timed out. Please check back later.",
- )
- }
-
- // Wait for completion
- const completedMemory = await pollForCompletion()
- return completedMemory
- })()
+ },
+ });
- toast.promise(processingPromise, {
- loading: "Processing...",
- success: `${contentType === "link" ? "Link" : "Note"} created successfully!`,
- error: (err) =>
- `Failed to add ${contentType}: ${err instanceof Error ? err.message : "Unknown error"}`,
- })
+ if (response.error) {
+ throw new Error(
+ response.error?.message || `Failed to add ${contentType}`,
+ );
+ }
- return processingPromise
+ console.log("✅ Memory created:", response.data);
+ return response.data;
},
onMutate: async ({ content, project, contentType }) => {
- console.log("🚀 onMutate starting...")
+ console.log("🚀 OPTIMISTIC UPDATE: Starting for", contentType);
- // Cancel any outgoing refetches
+ // Cancel queries to prevent conflicts
await queryClient.cancelQueries({
queryKey: ["documents-with-memories", project],
- })
- console.log("✅ Cancelled queries")
+ });
+ console.log("📞 QUERIES CANCELLED for project:", project);
- // Snapshot the previous value
+ // Get previous data for rollback
const previousMemories = queryClient.getQueryData([
"documents-with-memories",
project,
- ])
- console.log("📸 Previous memories:", previousMemories)
+ ]);
- // Create optimistic memory
+ // Create optimistic memory with proper title
+ const tempId = `temp-${Date.now()}`;
const optimisticMemory = {
- id: `temp-${Date.now()}`,
+ id: tempId,
content: contentType === "link" ? "" : content,
url: contentType === "link" ? content : null,
title:
- contentType === "link" ? "Processing..." : content.substring(0, 100),
- description:
- contentType === "link"
- ? "Extracting content..."
- : "Processing content...",
+ contentType === "link" ? "Processing..." : getPlainTextTitle(content),
+ description: "Processing...",
containerTags: [project],
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
- status: "queued",
- type: contentType,
- metadata: {
- processingStage: "queued",
- processingMessage: "Added to processing queue",
- },
memoryEntries: [],
isOptimistic: true,
- }
- console.log("🎯 Created optimistic memory:", optimisticMemory)
+ };
- // Optimistically update to include the new memory
+ console.log("🎯 Adding optimistic memory:", optimisticMemory);
+
+ // Add to cache optimistically
queryClient.setQueryData(
["documents-with-memories", project],
(old: any) => {
- console.log("🔄 Old data:", old)
- const newData = old
+ if (old?.pages) {
+ // Infinite query structure
+ const newPages = [...old.pages];
+ if (newPages.length > 0) {
+ newPages[0] = {
+ ...newPages[0],
+ documents: [optimisticMemory, ...(newPages[0].documents || [])],
+ };
+ } else {
+ newPages.push({
+ documents: [optimisticMemory],
+ pagination: { currentPage: 1, totalPages: 1, totalCount: 1 },
+ });
+ }
+ return { ...old, pages: newPages };
+ }
+
+ // Regular query structure
+ return old
? {
...old,
documents: [optimisticMemory, ...(old.documents || [])],
totalCount: (old.totalCount || 0) + 1,
}
- : { documents: [optimisticMemory], totalCount: 1 }
- console.log("✨ New data:", newData)
- return newData
+ : { documents: [optimisticMemory], totalCount: 1 };
},
- )
+ );
- console.log("✅ onMutate completed")
- return { previousMemories, optimisticId: optimisticMemory.id }
+ return { previousMemories, optimisticId: tempId };
},
- // If the mutation fails, roll back to the previous value
onError: (error, variables, context) => {
+ console.log("❌ Mutation failed, rolling back");
if (context?.previousMemories) {
queryClient.setQueryData(
["documents-with-memories", variables.project],
context.previousMemories,
- )
+ );
}
+ toast.error(`Failed to add ${variables.contentType}`);
},
- onSuccess: (_data, variables) => {
+ onSuccess: (data, variables, context) => {
+ console.log(
+ "✅ Mutation succeeded, starting simple polling for memory:",
+ data.id,
+ );
analytics.memoryAdded({
type: variables.contentType === "link" ? "link" : "note",
project_id: variables.project,
content_length: variables.content.length,
- })
-
- queryClient.invalidateQueries({
- queryKey: ["documents-with-memories", variables.project],
- })
-
- setTimeout(() => {
- queryClient.invalidateQueries({
- queryKey: ["documents-with-memories", variables.project],
- })
- }, 30000) // 30 seconds
-
- setTimeout(() => {
- queryClient.invalidateQueries({
- queryKey: ["documents-with-memories", variables.project],
- })
- }, 120000) // 2 minutes
-
- setShowAddDialog(false)
- onClose?.()
+ });
+
+ toast.success(
+ `${variables.contentType === "link" ? "Link" : "Note"} added successfully!`,
+ );
+ setShowAddDialog(false);
+ onClose?.();
+
+ // Simple polling to replace optimistic update when ready
+ const pollMemory = async () => {
+ try {
+ const memory = await $fetch(`@get/documents/${data.id}`);
+ console.log("🔍 Polling memory:", memory.data);
+
+ if (memory.data && !memory.error) {
+ // Check if memory has been processed (has memory entries, substantial content, and NOT untitled/processing)
+ const hasRealTitle =
+ memory.data.title &&
+ !memory.data.title.toLowerCase().includes("untitled") &&
+ !memory.data.title.toLowerCase().includes("processing") &&
+ memory.data.title.length > 3;
+
+ const isReady =
+ memory.data.memoryEntries?.length > 0 ||
+ (memory.data.content &&
+ memory.data.content.length > 10 &&
+ hasRealTitle);
+
+ console.log("📊 Memory ready check:", {
+ isReady,
+ hasMemoryEntries: memory.data.memoryEntries?.length,
+ hasContent: memory.data.content?.length,
+ title: memory.data.title,
+ hasRealTitle,
+ titleNotUntitled:
+ memory.data.title &&
+ !memory.data.title.toLowerCase().includes("untitled"),
+ titleNotProcessing:
+ memory.data.title &&
+ !memory.data.title.toLowerCase().includes("processing"),
+ });
+
+ if (isReady) {
+ console.log("✅ Memory ready, replacing optimistic update");
+ // Replace optimistic memory with real data
+ queryClient.setQueryData(
+ ["documents-with-memories", variables.project],
+ (old: any) => {
+ if (old?.pages) {
+ const newPages = old.pages.map((page: any) => ({
+ ...page,
+ documents: page.documents.map((doc: any) => {
+ if (doc.isOptimistic && doc.id.startsWith("temp-")) {
+ return { ...memory.data, isOptimistic: false };
+ }
+ return doc;
+ }),
+ }));
+ return { ...old, pages: newPages };
+ }
+ return old;
+ },
+ );
+ return true; // Stop polling
+ }
+ }
+ return false; // Continue polling
+ } catch (error) {
+ console.error("❌ Error polling memory:", error);
+ return false;
+ }
+ };
+
+ // Poll every 3 seconds for up to 30 attempts (90 seconds)
+ let attempts = 0;
+ const maxAttempts = 30;
+
+ const poll = async () => {
+ if (attempts >= maxAttempts) {
+ console.log("⚠️ Polling stopped after max attempts");
+ return;
+ }
+
+ const isDone = await pollMemory();
+ attempts++;
+
+ if (!isDone && attempts < maxAttempts) {
+ setTimeout(poll, 3000);
+ }
+ };
+
+ // Start polling after 2 seconds
+ setTimeout(poll, 2000);
},
- })
+ });
const fileUploadMutation = useMutation({
mutationFn: async ({
@@ -420,10 +430,10 @@ export function AddMemoryView({
description,
project,
}: {
- file: File
- title?: string
- description?: string
- project: string
+ file: File;
+ title?: string;
+ description?: string;
+ project: string;
}) => {
// TEMPORARILY DISABLED: Limit check disabled
// Check if user can add more memories
@@ -433,9 +443,9 @@ export function AddMemoryView({
// );
// }
- const formData = new FormData()
- formData.append("file", file)
- formData.append("containerTags", JSON.stringify([project]))
+ const formData = new FormData();
+ formData.append("file", file);
+ formData.append("containerTags", JSON.stringify([project]));
const response = await fetch(
`${process.env.NEXT_PUBLIC_BACKEND_URL}/v3/documents/file`,
@@ -444,14 +454,14 @@ export function AddMemoryView({
body: formData,
credentials: "include",
},
- )
+ );
if (!response.ok) {
- const error = await response.json()
- throw new Error(error.error || "Failed to upload file")
+ const error = await response.json();
+ throw new Error(error.error || "Failed to upload file");
}
- const data = await response.json()
+ const data = await response.json();
// If we have metadata, we can update the document after creation
if (title || description) {
@@ -463,23 +473,23 @@ export function AddMemoryView({
sm_source: "consumer", // Use "consumer" source to bypass limits
},
},
- })
+ });
}
- return data
+ return data;
},
// Optimistic update
onMutate: async ({ file, title, description, project }) => {
// Cancel any outgoing refetches
await queryClient.cancelQueries({
queryKey: ["documents-with-memories", project],
- })
+ });
// Snapshot the previous value
const previousMemories = queryClient.getQueryData([
"documents-with-memories",
project,
- ])
+ ]);
// Create optimistic memory for the file
const optimisticMemory = {
@@ -499,23 +509,50 @@ export function AddMemoryView({
mimeType: file.type,
},
memoryEntries: [],
- }
+ };
// Optimistically update to include the new memory
queryClient.setQueryData(
["documents-with-memories", project],
(old: any) => {
- if (!old) return { documents: [optimisticMemory], totalCount: 1 }
+ // Handle infinite query structure
+ if (old?.pages) {
+ // This is an infinite query - add to the first page
+ const newPages = [...old.pages];
+ if (newPages.length > 0) {
+ // Add to the first page
+ const firstPage = { ...newPages[0] };
+ firstPage.documents = [
+ optimisticMemory,
+ ...(firstPage.documents || []),
+ ];
+ newPages[0] = firstPage;
+ } else {
+ // No pages yet, create the first page
+ newPages.push({
+ documents: [optimisticMemory],
+ pagination: { currentPage: 1, totalPages: 1, totalCount: 1 },
+ totalCount: 1,
+ });
+ }
+
+ return {
+ ...old,
+ pages: newPages,
+ };
+ }
+ // Fallback for regular query structure
+ if (!old) return { documents: [optimisticMemory], totalCount: 1 };
return {
...old,
documents: [optimisticMemory, ...(old.documents || [])],
totalCount: (old.totalCount || 0) + 1,
- }
+ };
},
- )
+ );
// Return a context object with the snapshotted value
- return { previousMemories }
+ return { previousMemories };
},
// If the mutation fails, roll back to the previous value
onError: (error, variables, context) => {
@@ -523,11 +560,11 @@ export function AddMemoryView({
queryClient.setQueryData(
["documents-with-memories", variables.project],
context.previousMemories,
- )
+ );
}
toast.error("Failed to upload file", {
description: error instanceof Error ? error.message : "Unknown error",
- })
+ });
},
onSuccess: (_data, variables) => {
analytics.memoryAdded({
@@ -535,18 +572,18 @@ export function AddMemoryView({
project_id: variables.project,
file_size: variables.file.size,
file_type: variables.file.type,
- })
+ });
toast.success("File uploaded successfully!", {
description: "Your file is being processed",
- })
- setShowAddDialog(false)
- onClose?.()
- },
- // Always refetch after error or success
- onSettled: () => {
- queryClient.invalidateQueries({ queryKey: ["documents-with-memories"] })
+ });
+ setShowAddDialog(false);
+ onClose?.();
},
- })
+ // Don't invalidate queries immediately - let optimistic updates work
+ // onSettled: () => {
+ // queryClient.invalidateQueries({ queryKey: ["documents-with-memories"] })
+ // },
+ });
return (
<AnimatePresence mode="wait">
@@ -554,8 +591,8 @@ export function AddMemoryView({
<Dialog
key="add-memory-dialog"
onOpenChange={(open) => {
- setShowAddDialog(open)
- if (!open) onClose?.()
+ setShowAddDialog(open);
+ if (!open) onClose?.();
}}
open={showAddDialog}
>
@@ -614,9 +651,9 @@ export function AddMemoryView({
<div className="space-y-4">
<form
onSubmit={(e) => {
- e.preventDefault()
- e.stopPropagation()
- addContentForm.handleSubmit()
+ e.preventDefault();
+ e.stopPropagation();
+ addContentForm.handleSubmit();
}}
>
<div className="grid gap-4">
@@ -632,9 +669,9 @@ export function AddMemoryView({
validators={{
onChange: ({ value }) => {
if (!value || value.trim() === "") {
- return "Note is required"
+ return "Note is required";
}
- return undefined
+ return undefined;
},
}}
>
@@ -716,9 +753,9 @@ export function AddMemoryView({
isSubmitDisabled={!addContentForm.state.canSubmit}
isSubmitting={addContentMutation.isPending}
onCancel={() => {
- setShowAddDialog(false)
- onClose?.()
- addContentForm.reset()
+ setShowAddDialog(false);
+ onClose?.();
+ addContentForm.reset();
}}
submitIcon={Plus}
submitText="Add Note"
@@ -732,9 +769,9 @@ export function AddMemoryView({
<div className="space-y-4">
<form
onSubmit={(e) => {
- e.preventDefault()
- e.stopPropagation()
- addContentForm.handleSubmit()
+ e.preventDefault();
+ e.stopPropagation();
+ addContentForm.handleSubmit();
}}
>
<div className="grid gap-4">
@@ -756,13 +793,13 @@ export function AddMemoryView({
validators={{
onChange: ({ value }) => {
if (!value || value.trim() === "") {
- return "Link is required"
+ return "Link is required";
}
try {
- new URL(value)
- return undefined
+ new URL(value);
+ return undefined;
} catch {
- return "Please enter a valid link"
+ return "Please enter a valid link";
}
},
}}
@@ -842,9 +879,9 @@ export function AddMemoryView({
isSubmitDisabled={!addContentForm.state.canSubmit}
isSubmitting={addContentMutation.isPending}
onCancel={() => {
- setShowAddDialog(false)
- onClose?.()
- addContentForm.reset()
+ setShowAddDialog(false);
+ onClose?.();
+ addContentForm.reset();
}}
submitIcon={Plus}
submitText="Add Link"
@@ -858,9 +895,9 @@ export function AddMemoryView({
<div className="space-y-4">
<form
onSubmit={(e) => {
- e.preventDefault()
- e.stopPropagation()
- fileUploadForm.handleSubmit()
+ e.preventDefault();
+ e.stopPropagation();
+ fileUploadForm.handleSubmit();
}}
>
<div className="grid gap-4">
@@ -994,10 +1031,10 @@ export function AddMemoryView({
isSubmitDisabled={selectedFiles.length === 0}
isSubmitting={fileUploadMutation.isPending}
onCancel={() => {
- setShowAddDialog(false)
- onClose?.()
- fileUploadForm.reset()
- setSelectedFiles([])
+ setShowAddDialog(false);
+ onClose?.();
+ fileUploadForm.reset();
+ setSelectedFiles([]);
}}
submitIcon={UploadIcon}
submitText="Upload File"
@@ -1065,8 +1102,8 @@ export function AddMemoryView({
<Button
className="bg-white/5 hover:bg-white/10 border-white/10 text-white w-full sm:w-auto"
onClick={() => {
- setShowCreateProjectDialog(false)
- setNewProjectName("")
+ setShowCreateProjectDialog(false);
+ setNewProjectName("");
}}
type="button"
variant="outline"
@@ -1103,19 +1140,19 @@ export function AddMemoryView({
</Dialog>
)}
</AnimatePresence>
- )
+ );
}
export function AddMemoryExpandedView() {
- const [showDialog, setShowDialog] = useState(false)
+ const [showDialog, setShowDialog] = useState(false);
const [selectedTab, setSelectedTab] = useState<
"note" | "link" | "file" | "connect"
- >("note")
+ >("note");
const handleOpenDialog = (tab: "note" | "link" | "file" | "connect") => {
- setSelectedTab(tab)
- setShowDialog(true)
- }
+ setSelectedTab(tab);
+ setShowDialog(true);
+ };
return (
<>
@@ -1186,5 +1223,5 @@ export function AddMemoryExpandedView() {
/>
)}
</>
- )
+ );
}
diff --git a/packages/ai-sdk/src/index.ts b/packages/ai-sdk/src/index.ts
index e419075c..2eed2172 100644
--- a/packages/ai-sdk/src/index.ts
+++ b/packages/ai-sdk/src/index.ts
@@ -1 +1 @@
-export * from "./tools"
+export * from "./tools";
diff --git a/packages/validation/schemas.ts b/packages/validation/schemas.ts
index 92524164..a704b755 100644
--- a/packages/validation/schemas.ts
+++ b/packages/validation/schemas.ts
@@ -1,12 +1,12 @@
-import { z } from "zod"
+import { z } from "zod";
export const MetadataSchema = z.record(
z.union([z.string(), z.number(), z.boolean()]),
-)
-export type Metadata = z.infer<typeof MetadataSchema>
+);
+export type Metadata = z.infer<typeof MetadataSchema>;
-export const VisibilityEnum = z.enum(["public", "private", "unlisted"])
-export type Visibility = z.infer<typeof VisibilityEnum>
+export const VisibilityEnum = z.enum(["public", "private", "unlisted"]);
+export type Visibility = z.infer<typeof VisibilityEnum>;
export const DocumentTypeEnum = z.enum([
"text",
@@ -20,8 +20,8 @@ export const DocumentTypeEnum = z.enum([
"notion_doc",
"webpage",
"onedrive",
-])
-export type DocumentType = z.infer<typeof DocumentTypeEnum>
+]);
+export type DocumentType = z.infer<typeof DocumentTypeEnum>;
export const DocumentStatusEnum = z.enum([
"unknown",
@@ -32,8 +32,8 @@ export const DocumentStatusEnum = z.enum([
"indexing",
"done",
"failed",
-])
-export type DocumentStatus = z.infer<typeof DocumentStatusEnum>
+]);
+export type DocumentStatus = z.infer<typeof DocumentStatusEnum>;
export const ProcessingStepSchema = z.object({
name: z.string(),
@@ -43,8 +43,8 @@ export const ProcessingStepSchema = z.object({
error: z.string().optional(),
metadata: z.record(z.unknown()).optional(),
finalStatus: z.enum(["done", "failed"]).optional(),
-})
-export type ProcessingStep = z.infer<typeof ProcessingStepSchema>
+});
+export type ProcessingStep = z.infer<typeof ProcessingStepSchema>;
export const ProcessingMetadataSchema = z.object({
startTime: z.number(),
@@ -55,8 +55,8 @@ export const ProcessingMetadataSchema = z.object({
chunkingStrategy: z.string().optional(),
tokenCount: z.number().optional(),
steps: z.array(ProcessingStepSchema),
-})
-export type ProcessingMetadata = z.infer<typeof ProcessingMetadataSchema>
+});
+export type ProcessingMetadata = z.infer<typeof ProcessingMetadataSchema>;
export const DocumentSchema = z.object({
id: z.string(),
@@ -97,11 +97,11 @@ export const DocumentSchema = z.object({
// Timestamps
createdAt: z.coerce.date(),
updatedAt: z.coerce.date(),
-})
-export type Document = z.infer<typeof DocumentSchema>
+});
+export type Document = z.infer<typeof DocumentSchema>;
-export const ChunkTypeEnum = z.enum(["text", "image"])
-export type ChunkType = z.infer<typeof ChunkTypeEnum>
+export const ChunkTypeEnum = z.enum(["text", "image"]);
+export type ChunkType = z.infer<typeof ChunkTypeEnum>;
export const ChunkSchema = z.object({
id: z.string(),
@@ -120,15 +120,15 @@ export const ChunkSchema = z.object({
matryokshaEmbeddingModel: z.string().nullable().optional(),
createdAt: z.coerce.date(),
-})
-export type Chunk = z.infer<typeof ChunkSchema>
+});
+export type Chunk = z.infer<typeof ChunkSchema>;
export const ConnectionProviderEnum = z.enum([
"notion",
"google-drive",
"onedrive",
-])
-export type ConnectionProvider = z.infer<typeof ConnectionProviderEnum>
+]);
+export type ConnectionProvider = z.infer<typeof ConnectionProviderEnum>;
export const ConnectionStateSchema = z.object({
stateToken: z.string(),
@@ -142,8 +142,8 @@ export const ConnectionStateSchema = z.object({
containerTags: z.array(z.string()).nullable().optional(),
createdAt: z.coerce.date(),
expiresAt: z.coerce.date().nullable().optional(),
-})
-export type ConnectionState = z.infer<typeof ConnectionStateSchema>
+});
+export type ConnectionState = z.infer<typeof ConnectionStateSchema>;
export const ConnectionSchema = z.object({
id: z.string(),
@@ -163,8 +163,8 @@ export const ConnectionSchema = z.object({
metadata: z.record(z.unknown()),
createdAt: z.coerce.date(),
-})
-export type Connection = z.infer<typeof ConnectionSchema>
+});
+export type Connection = z.infer<typeof ConnectionSchema>;
export const RequestTypeEnum = z.enum([
"add",
@@ -175,8 +175,8 @@ export const RequestTypeEnum = z.enum([
"delete",
"chat",
"search_v4",
-])
-export type RequestType = z.infer<typeof RequestTypeEnum>
+]);
+export type RequestType = z.infer<typeof RequestTypeEnum>;
export const ApiRequestSchema = z.object({
id: z.string(),
@@ -212,8 +212,8 @@ export const ApiRequestSchema = z.object({
origin: z.string().default("api"),
createdAt: z.coerce.date(),
-})
-export type ApiRequest = z.infer<typeof ApiRequestSchema>
+});
+export type ApiRequest = z.infer<typeof ApiRequestSchema>;
export const SpaceSchema = z.object({
id: z.string(),
@@ -233,11 +233,11 @@ export const SpaceSchema = z.object({
createdAt: z.coerce.date(),
updatedAt: z.coerce.date(),
-})
-export type Space = z.infer<typeof SpaceSchema>
+});
+export type Space = z.infer<typeof SpaceSchema>;
-export const MemoryRelationEnum = z.enum(["updates", "extends", "derives"])
-export type MemoryRelation = z.infer<typeof MemoryRelationEnum>
+export const MemoryRelationEnum = z.enum(["updates", "extends", "derives"]);
+export type MemoryRelation = z.infer<typeof MemoryRelationEnum>;
export const MemoryEntrySchema = z.object({
id: z.string(),
@@ -274,14 +274,14 @@ export const MemoryEntrySchema = z.object({
createdAt: z.coerce.date(),
updatedAt: z.coerce.date(),
-})
-export type MemoryEntry = z.infer<typeof MemoryEntrySchema>
+});
+export type MemoryEntry = z.infer<typeof MemoryEntrySchema>;
export const DocumentsToSpacesSchema = z.object({
documentId: z.string(),
spaceId: z.string(),
-})
-export type DocumentsToSpaces = z.infer<typeof DocumentsToSpacesSchema>
+});
+export type DocumentsToSpaces = z.infer<typeof DocumentsToSpacesSchema>;
export const MemoryDocumentSourceSchema = z.object({
memoryEntryId: z.string(),
@@ -289,11 +289,11 @@ export const MemoryDocumentSourceSchema = z.object({
relevanceScore: z.number().default(100),
metadata: z.record(z.unknown()).nullable().optional(),
addedAt: z.coerce.date(),
-})
-export type MemoryDocumentSource = z.infer<typeof MemoryDocumentSourceSchema>
+});
+export type MemoryDocumentSource = z.infer<typeof MemoryDocumentSourceSchema>;
-export const SpaceRoleEnum = z.enum(["owner", "admin", "editor", "viewer"])
-export type SpaceRole = z.infer<typeof SpaceRoleEnum>
+export const SpaceRoleEnum = z.enum(["owner", "admin", "editor", "viewer"]);
+export type SpaceRole = z.infer<typeof SpaceRoleEnum>;
export const SpacesToMembersSchema = z.object({
spaceId: z.string(),
@@ -302,8 +302,8 @@ export const SpacesToMembersSchema = z.object({
metadata: MetadataSchema.nullable().optional(),
createdAt: z.coerce.date(),
updatedAt: z.coerce.date(),
-})
-export type SpacesToMembers = z.infer<typeof SpacesToMembersSchema>
+});
+export type SpacesToMembers = z.infer<typeof SpacesToMembersSchema>;
export const OrganizationSettingsSchema = z.object({
id: z.string(),
@@ -331,8 +331,8 @@ export const OrganizationSettingsSchema = z.object({
onedriveClientSecret: z.string().nullable().optional(),
updatedAt: z.coerce.date(),
-})
-export type OrganizationSettings = z.infer<typeof OrganizationSettingsSchema>
+});
+export type OrganizationSettings = z.infer<typeof OrganizationSettingsSchema>;
export const schemas = {
// Base types
@@ -368,4 +368,4 @@ export const schemas = {
// Auth
OrganizationSettingsSchema,
-} as const
+} as const;