// The managed search service: the platform's documents-index service through the kit-platform // Knowledge client. Indexing and search run server-side there (the service owns chunking, // embeddings and the vector store); this module supplies the job lifecycle and the citations it // answers with. // // Contract notes (the service's real shapes): the index submission carries the content's SHA-256 // (computed here from the job's bytes) as its version assertion; a quota denial surfaces on the // job, which the poll below reads as the job's typed error. Blob pathnames and returned citations // resolve through the files module's scoped rows, so an opaque file id never becomes a pathname // and a foreign or stale service reference never becomes a valid app hit. import { createHash } from "node:crypto" import { type FilesDeps, findFileStorageByKey, getFileStorage, platformBlobPathname, platformBlobStorageKey, } from "@conusai-kit/files" import { type Knowledge, KnowledgeError } from "@conusai-kit/platform" import type { KnowledgeHit, KnowledgeScope, KnowledgeSearchService } from "../core/ports.js" export function platformKnowledgeSearch(client: Knowledge, files: FilesDeps): KnowledgeSearchService { const unwrap = (error: unknown): never => { // The client's errors are already typed by reason; a non-knowledge failure is unavailable. if (error instanceof KnowledgeError) throw error throw new KnowledgeError( "runtime_unavailable", error instanceof Error ? error.message : "the knowledge service failed", true, ) } const documentRef = async (scope: KnowledgeScope, fileId: string): Promise => { const reference = await getFileStorage(files, scope, fileId) return platformBlobPathname(scope, reference.storageKey) } return { async readiness() { try { const answer = await client.readiness() return { state: answer.state, reasons: [...answer.reasons] } } catch (error) { return { state: "unavailable", reasons: [error instanceof Error ? (error.message.split("\n")[0] ?? error.message) : "not ready"], } } }, async index(scope, input) { try { const { jobId } = await client.index({ collection: input.collectionName, documentRef: await documentRef(scope, input.fileId), options: { visibility: "public", contentSha256: createHash("sha256").update(input.bytes).digest("hex"), }, }) // The service's job is durable; poll until it leaves the queue. A caller-side cancel is // the job's own AbortSignal, checked between polls. for (;;) { const job = await client.job(jobId) if (job.status === "succeeded") return if (job.status === "failed") throw new KnowledgeError( job.error === "quota-exhausted" ? "quota_exhausted" : "runtime_unavailable", job.error ?? "the indexing job failed", ) if (job.status === "cancelled") throw new KnowledgeError("runtime_unavailable", "the indexing job was cancelled") await new Promise((resolve) => setTimeout(resolve, 1_000)) } } catch (error) { return unwrap(error) } }, async search(scope, input) { try { const results = await client.search({ collection: input.collectionName, query: input.query, limit: input.limit, }) const hits: KnowledgeHit[] = [] for (const [at, hit] of results.entries()) { const storageKey = platformBlobStorageKey(scope, hit.documentRef) if (storageKey === null) continue const reference = await findFileStorageByKey(files, scope, storageKey) if (reference === null) continue hits.push({ fileId: reference.file.fileId, filename: reference.file.filename, collectionName: input.collectionName, score: hit.score, chunk: at + 1, snippet: hit.snippet, }) } return hits } catch (error) { return unwrap(error) } }, async remove(scope, input) { try { await client.remove(await documentRef(scope, input.fileId)) } catch (error) { return unwrap(error) } }, } }