-
Notifications
You must be signed in to change notification settings - Fork 0
feat: wire document update to MCP, CLI, and REST API (#182) #270
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -9,6 +9,7 @@ import { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| getDocument, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| indexDocument, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| deleteDocument, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| updateDocument, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| listTopics, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| createTopic, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| listTags, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -418,6 +419,42 @@ export async function handleRequest( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (docId && method === "PATCH") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const body = await parseJsonBody(req); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!body || typeof body !== "object") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sendError(res, 400, "VALIDATION_ERROR", "Request body must be a JSON object"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const b = body as Record<string, unknown>; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const title = typeof b["title"] === "string" ? b["title"] : undefined; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const content = typeof b["content"] === "string" ? b["content"] : undefined; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const metadata: Record<string, string | null | undefined> = {}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (b["library"] !== undefined) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| metadata.library = typeof b["library"] === "string" ? b["library"] : null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (b["version"] !== undefined) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| metadata.version = typeof b["version"] === "string" ? b["version"] : null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (b["url"] !== undefined) metadata.url = typeof b["url"] === "string" ? b["url"] : null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (b["topicId"] !== undefined) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| metadata.topicId = typeof b["topicId"] === "string" ? b["topicId"] : null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+432
to
+438
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (b["library"] !== undefined) | |
| metadata.library = typeof b["library"] === "string" ? b["library"] : null; | |
| if (b["version"] !== undefined) | |
| metadata.version = typeof b["version"] === "string" ? b["version"] : null; | |
| if (b["url"] !== undefined) metadata.url = typeof b["url"] === "string" ? b["url"] : null; | |
| if (b["topicId"] !== undefined) | |
| metadata.topicId = typeof b["topicId"] === "string" ? b["topicId"] : null; | |
| const library = b["library"]; | |
| if (library !== undefined) { | |
| if (library !== null && typeof library !== "string") { | |
| sendError(res, 400, "VALIDATION_ERROR", "Field 'library' must be a string or null"); | |
| return; | |
| } | |
| metadata.library = library as string | null; | |
| } | |
| const version = b["version"]; | |
| if (version !== undefined) { | |
| if (version !== null && typeof version !== "string") { | |
| sendError(res, 400, "VALIDATION_ERROR", "Field 'version' must be a string or null"); | |
| return; | |
| } | |
| metadata.version = version as string | null; | |
| } | |
| const url = b["url"]; | |
| if (url !== undefined) { | |
| if (url !== null && typeof url !== "string") { | |
| sendError(res, 400, "VALIDATION_ERROR", "Field 'url' must be a string or null"); | |
| return; | |
| } | |
| metadata.url = url as string | null; | |
| } | |
| const topicId = b["topicId"]; | |
| if (topicId !== undefined) { | |
| if (topicId !== null && typeof topicId !== "string") { | |
| sendError(res, 400, "VALIDATION_ERROR", "Field 'topicId' must be a string or null"); | |
| return; | |
| } | |
| metadata.topicId = topicId as string | null; | |
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,7 +9,7 @@ import { searchDocuments } from "../core/search.js"; | |
| import { askQuestion, createLlmProvider } from "../core/rag.js"; | ||
| import { getDocumentRatings, listRatings } from "../core/ratings.js"; | ||
| import { createTopic, listTopics } from "../core/topics.js"; | ||
| import { getDocument, listDocuments, deleteDocument } from "../core/documents.js"; | ||
| import { getDocument, listDocuments, deleteDocument, updateDocument } from "../core/documents.js"; | ||
| import { createLink, getDocumentLinks, deleteLink, getPrerequisiteChain } from "../core/links.js"; | ||
| import type { LinkType } from "../core/links.js"; | ||
| import { getVersionHistory, rollbackToVersion } from "../core/versioning.js"; | ||
|
|
@@ -706,6 +706,55 @@ docsCmd | |
| } | ||
| }); | ||
|
|
||
| docsCmd | ||
| .command("update <documentId>") | ||
| .description("Update an existing document") | ||
| .option("--title <title>", "New title") | ||
| .option("--content <content>", "New content (will re-chunk and re-index)") | ||
| .option("--library <name>", "New library name") | ||
| .option("--version <ver>", "New version") | ||
| .option("--url <url>", "New URL") | ||
| .option("--topic <topicId>", "New topic ID") | ||
| .action( | ||
| async ( | ||
| documentId: string, | ||
| opts: { | ||
| title?: string; | ||
| content?: string; | ||
| library?: string; | ||
| version?: string; | ||
| url?: string; | ||
| topic?: string; | ||
| }, | ||
| ) => { | ||
| const { db, provider } = initializeAppWithEmbedding(); | ||
| try { | ||
| const metadata: Record<string, string | null | undefined> = {}; | ||
| if (opts.library !== undefined) metadata.library = opts.library; | ||
| if (opts.version !== undefined) metadata.version = opts.version; | ||
| if (opts.url !== undefined) metadata.url = opts.url; | ||
|
Comment on lines
+730
to
+735
|
||
| if (opts.topic !== undefined) metadata.topicId = opts.topic; | ||
|
|
||
| const doc = await updateDocument(db, provider, documentId, { | ||
| title: opts.title, | ||
| content: opts.content, | ||
| metadata: | ||
| Object.keys(metadata).length > 0 | ||
| ? (metadata as { | ||
| library?: string | null; | ||
| version?: string | null; | ||
| url?: string | null; | ||
| topicId?: string | null; | ||
| }) | ||
| : undefined, | ||
| }); | ||
| console.log(`✓ Updated "${doc.title}" (${doc.id})`); | ||
| } finally { | ||
| closeDatabase(); | ||
| } | ||
| }, | ||
| ); | ||
|
|
||
| // tag | ||
| const tagCmd = program.command("tag").description("Manage document tags"); | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,7 +7,7 @@ import { getActiveWorkspace, getWorkspacePath } from "../core/workspace.js"; | |
| import { createEmbeddingProvider } from "../providers/index.js"; | ||
| import { searchDocuments } from "../core/search.js"; | ||
| import { askQuestion, createLlmProvider, type LlmProvider } from "../core/rag.js"; | ||
| import { getDocument, listDocuments, deleteDocument } from "../core/documents.js"; | ||
| import { getDocument, listDocuments, deleteDocument, updateDocument } from "../core/documents.js"; | ||
| import { rateDocument, getDocumentRatings } from "../core/ratings.js"; | ||
| import { indexDocument } from "../core/indexing.js"; | ||
| import { listTopics } from "../core/topics.js"; | ||
|
|
@@ -241,6 +241,45 @@ async function main(): Promise<void> { | |
| }), | ||
| ); | ||
|
|
||
| // Tool: update-document | ||
| server.tool( | ||
| "update-document", | ||
| "Update an existing document's title, content, or metadata", | ||
| { | ||
| documentId: z.string().describe("The document ID to update"), | ||
| title: z.string().optional().describe("New title"), | ||
| content: z.string().optional().describe("New content (will re-chunk and re-index)"), | ||
| library: z.string().nullable().optional().describe("New library name (null to clear)"), | ||
| version: z.string().nullable().optional().describe("New version (null to clear)"), | ||
| url: z.string().nullable().optional().describe("New URL (null to clear)"), | ||
| topicId: z.string().nullable().optional().describe("New topic ID (null to clear)"), | ||
| }, | ||
| withErrorHandling(async (params) => { | ||
| const metadata: Record<string, string | null | undefined> = {}; | ||
| if (params.library !== undefined) metadata.library = params.library; | ||
| if (params.version !== undefined) metadata.version = params.version; | ||
| if (params.url !== undefined) metadata.url = params.url; | ||
| if (params.topicId !== undefined) metadata.topicId = params.topicId; | ||
|
|
||
|
Comment on lines
+257
to
+263
|
||
| const doc = await updateDocument(db, provider, params.documentId, { | ||
| title: params.title, | ||
| content: params.content, | ||
| metadata: | ||
| Object.keys(metadata).length > 0 | ||
| ? (metadata as { | ||
| library?: string | null; | ||
| version?: string | null; | ||
| url?: string | null; | ||
| topicId?: string | null; | ||
| }) | ||
| : undefined, | ||
| }); | ||
| return { | ||
| content: [{ type: "text" as const, text: `Document updated: ${doc.title} (${doc.id})` }], | ||
| }; | ||
| }), | ||
| ); | ||
|
|
||
| // Tool: rate-document | ||
| server.tool( | ||
| "rate-document", | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,6 +1,11 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { describe, it, expect, beforeEach } from "vitest"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { createTestDb, createTestDbWithVec } from "../fixtures/test-db.js"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { getDocument, deleteDocument, listDocuments } from "../../src/core/documents.js"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| getDocument, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| deleteDocument, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| listDocuments, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| updateDocument, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } from "../../src/core/documents.js"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { indexDocument, type IndexDocumentInput } from "../../src/core/indexing.js"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { MockEmbeddingProvider } from "../fixtures/mock-provider.js"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import type Database from "better-sqlite3"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -125,6 +130,92 @@ describe("documents", () => { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| describe("updateDocument", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let vecDb: Database.Database; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let provider: MockEmbeddingProvider; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| beforeEach(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| vecDb = createTestDbWithVec(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| provider = new MockEmbeddingProvider(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| it("should update title only", async () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const indexed = await indexDocument(vecDb, provider, { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| title: "Original Title", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| content: "Some content here.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sourceType: "manual", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const updated = await updateDocument(vecDb, provider, indexed.id, { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| title: "New Title", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(updated.title).toBe("New Title"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(updated.content).toBe("Some content here."); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| it("should update content and re-chunk", async () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const indexed = await indexDocument(vecDb, provider, { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| title: "Doc", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| content: "Old content.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sourceType: "manual", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const updated = await updateDocument(vecDb, provider, indexed.id, { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| content: "Brand new content that is different.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(updated.content).toBe("Brand new content that is different."); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Verify chunks were recreated | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const chunks = vecDb | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .prepare("SELECT id FROM chunks WHERE document_id = ?") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .all(indexed.id) as { id: string }[]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(chunks.length).toBeGreaterThan(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+164
to
+173
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const updated = await updateDocument(vecDb, provider, indexed.id, { | |
| content: "Brand new content that is different.", | |
| }); | |
| expect(updated.content).toBe("Brand new content that is different."); | |
| // Verify chunks were recreated | |
| const chunks = vecDb | |
| .prepare("SELECT id FROM chunks WHERE document_id = ?") | |
| .all(indexed.id) as { id: string }[]; | |
| expect(chunks.length).toBeGreaterThan(0); | |
| // Capture original chunks and their embeddings | |
| const originalChunks = vecDb | |
| .prepare( | |
| "SELECT id, content FROM chunks WHERE document_id = ? ORDER BY id", | |
| ) | |
| .all(indexed.id) as { id: string; content: string }[]; | |
| expect(originalChunks.length).toBeGreaterThan(0); | |
| const originalChunkIds = originalChunks.map((c) => c.id); | |
| const originalEmbeddings = vecDb | |
| .prepare( | |
| "SELECT chunk_id FROM chunk_embeddings WHERE document_id = ? ORDER BY chunk_id", | |
| ) | |
| .all(indexed.id) as { chunk_id: string }[]; | |
| expect(originalEmbeddings.length).toBeGreaterThan(0); | |
| const updated = await updateDocument(vecDb, provider, indexed.id, { | |
| content: "Brand new content that is different.", | |
| }); | |
| expect(updated.content).toBe("Brand new content that is different."); | |
| // Verify chunks were recreated: new chunks exist and have different IDs | |
| const newChunks = vecDb | |
| .prepare( | |
| "SELECT id, content FROM chunks WHERE document_id = ? ORDER BY id", | |
| ) | |
| .all(indexed.id) as { id: string; content: string }[]; | |
| expect(newChunks.length).toBeGreaterThan(0); | |
| const newChunkIds = newChunks.map((c) => c.id); | |
| // Ensure none of the original chunk IDs are reused | |
| expect( | |
| newChunkIds.every((id) => !originalChunkIds.includes(id)), | |
| ).toBe(true); | |
| // Verify embeddings now correspond to the new chunks | |
| const newEmbeddings = vecDb | |
| .prepare( | |
| "SELECT chunk_id FROM chunk_embeddings WHERE document_id = ? ORDER BY chunk_id", | |
| ) | |
| .all(indexed.id) as { chunk_id: string }[]; | |
| expect(newEmbeddings.length).toBeGreaterThan(0); | |
| const newEmbeddingChunkIds = newEmbeddings.map((e) => e.chunk_id); | |
| const sortedNewChunkIds = [...newChunkIds].sort(); | |
| const sortedNewEmbeddingChunkIds = [...newEmbeddingChunkIds].sort(); | |
| expect(sortedNewEmbeddingChunkIds).toEqual(sortedNewChunkIds); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PUT/PATCHcurrently accept an empty JSON object (or a body with no supported fields) and still callupdateDocument, which will create a new version / update timestamps despite being a no-op. Consider validating that at least one updatable field is present and return 400 for empty updates (matches the issue’s “invalid/empty body” requirement).