diff --git a/apps/web/hooks/use-document-mutations.ts b/apps/web/hooks/use-document-mutations.ts index c0b1c8643..09c751426 100644 --- a/apps/web/hooks/use-document-mutations.ts +++ b/apps/web/hooks/use-document-mutations.ts @@ -521,7 +521,8 @@ export function useDocumentMutations({ const data = (await response.json()) as { id: string } if (applyMeta && (title || description)) { - await $fetch(`@patch/documents/${data.id}`, { + await $fetch("@patch/documents/:id", { + params: { id: data.id }, body: { metadata: { ...(title && { title }), @@ -642,7 +643,8 @@ export function useDocumentMutations({ documentId: string content: string }) => { - const response = await $fetch(`@patch/documents/${documentId}`, { + const response = await $fetch("@patch/documents/:id", { + params: { id: documentId }, body: { content, metadata: { sm_source: "consumer" }, diff --git a/packages/lib/api.ts b/packages/lib/api.ts index 42901c0b1..b87f76cf6 100644 --- a/packages/lib/api.ts +++ b/packages/lib/api.ts @@ -11,6 +11,8 @@ import { CreateProjectSchema, DeleteProjectResponseSchema, DeleteProjectSchema, + DocumentFacetsQuerySchema, + DocumentFacetsResponseSchema, DocumentsWithMemoriesQuerySchema, DocumentsWithMemoriesResponseSchema, ListContainerTagsResponseSchema, @@ -18,6 +20,7 @@ import { ListProjectsResponseSchema, MemoryAddSchema, MemoryResponseSchema, + MemoryUpdateSchema, MigrateMCPRequestSchema, MigrateMCPResponseSchema, ProcessingDocumentsResponseSchema, @@ -265,6 +268,10 @@ export const apiSchema = createSchema({ input: DocumentsWithMemoriesQuerySchema, output: DocumentsWithMemoriesResponseSchema, }, + "@post/documents/documents/facets": { + input: DocumentFacetsQuerySchema, + output: DocumentFacetsResponseSchema, + }, "@post/documents/documents/by-ids": { input: z.object({ ids: z.array(z.string()), @@ -291,6 +298,13 @@ export const apiSchema = createSchema({ output: z.any(), }, + // Update a memory + "@patch/documents/:id": { + input: MemoryUpdateSchema, + output: MemoryResponseSchema, + params: z.object({ id: z.string() }), + }, + // Delete a memory "@delete/documents/:id": { output: z.any(), // 204 No-Content diff --git a/packages/validation/api.test.ts b/packages/validation/api.test.ts index 05c18934c..6282f5ba2 100644 --- a/packages/validation/api.test.ts +++ b/packages/validation/api.test.ts @@ -1,6 +1,11 @@ import { describe, expect, it } from "bun:test" import { readFileSync } from "node:fs" -import { SearchRequestSchema, Searchv4RequestSchema } from "./api" +import { + DocumentFacetsResponseSchema, + DocumentsWithMemoriesQuerySchema, + SearchRequestSchema, + Searchv4RequestSchema, +} from "./api" describe("search threshold schemas", () => { it("do not contain redundant number transforms or unreachable range guards", () => { @@ -80,3 +85,31 @@ describe("search threshold schemas", () => { ).toBe(false) }) }) + +describe("documents contract schemas", () => { + it("DocumentsWithMemoriesQuerySchema accepts the categories filter", () => { + const parsed = DocumentsWithMemoriesQuerySchema.parse({ + categories: ["webpage", "tweet"], + }) + expect(parsed.categories).toEqual(["webpage", "tweet"]) + }) + + it("DocumentsWithMemoriesQuerySchema leaves categories undefined when omitted", () => { + expect( + DocumentsWithMemoriesQuerySchema.parse({}).categories, + ).toBeUndefined() + }) + + it("DocumentFacetsResponseSchema parses a facets payload", () => { + const parsed = DocumentFacetsResponseSchema.parse({ + facets: [{ category: "webpage", count: 42, label: "Web pages" }], + total: 42, + }) + expect(parsed.facets[0]?.category).toBe("webpage") + }) + + it("DocumentFacetsResponseSchema tolerates a missing total", () => { + const parsed = DocumentFacetsResponseSchema.parse({ facets: [] }) + expect(parsed.total).toBeUndefined() + }) +}) diff --git a/packages/validation/api.ts b/packages/validation/api.ts index 8579ca408..b33ee2126 100644 --- a/packages/validation/api.ts +++ b/packages/validation/api.ts @@ -1100,6 +1100,13 @@ export const DocumentsWithMemoriesQuerySchema = z description: "Number of items per page", example: 10, }), + categories: z + .array(z.string()) + .optional() + .openapi({ + description: "Optional document categories to filter by", + example: ["webpage", "tweet"], + }), sort: z.enum(["createdAt", "updatedAt"]).default("createdAt").openapi({ description: "Field to sort by", example: "createdAt", @@ -1134,6 +1141,33 @@ export const DocumentFacetsQuerySchema = z description: "Query parameters for getting document facets", }) +export const DocumentFacetSchema = z + .object({ + category: z.string().openapi({ + description: "Document category identifier", + example: "webpage", + }), + count: z.number().openapi({ + description: "Number of documents in this category", + example: 42, + }), + label: z.string().openapi({ + description: "Human-readable category label", + example: "Web pages", + }), + }) + .openapi({ description: "A single document-category facet" }) + +export const DocumentFacetsResponseSchema = z + .object({ + facets: z.array(DocumentFacetSchema), + total: z.number().optional().openapi({ + description: "Total number of documents across all categories", + example: 120, + }), + }) + .openapi({ description: "Document category facets" }) + export const MigrateMCPRequestSchema = z .object({ userId: z.string().openapi({