188 lines
6.1 KiB
TypeScript
188 lines
6.1 KiB
TypeScript
// The procedure contracts of the knowledge module: schemas and names, no handler and no port.
|
|
//
|
|
// The module is a retrieval layer over the app's documents: a collection of text files is indexed
|
|
// (in the worker: chunk, embed through the AI gateway, upsert into the vector store), searched
|
|
// with cited snippets, and managed through an explicit, idempotent enablement flow. The enablement
|
|
// procedure is named enableDocumentAi per the Document AI enablement contract; its permission is the module's
|
|
// manage permission, so the admin gate is one line in the manifest.
|
|
|
|
import { procedure } from "@conusai-kit/core"
|
|
import { z } from "zod"
|
|
import { COLLECTION_CREATED, DOCUMENT_AI_DISABLED, DOCUMENT_AI_ENABLED } from "./constants.js"
|
|
import type { DocumentStatus, ServiceState } from "./statuses.js"
|
|
|
|
const inProject = { org: z.string(), workspace: z.string(), project: z.string() }
|
|
const inOrg = { org: z.string() }
|
|
|
|
export const collectionName = z
|
|
.string()
|
|
.min(1)
|
|
.max(100)
|
|
.regex(/^[a-z][a-z0-9-]*$/, "lower-case letters, digits and dashes")
|
|
|
|
export const collectionViewSchema = z.object({
|
|
name: collectionName,
|
|
documentCount: z.number().int(),
|
|
indexedCount: z.number().int(),
|
|
createdBy: z.string(),
|
|
})
|
|
export type CollectionView = z.output<typeof collectionViewSchema>
|
|
|
|
export const documentViewSchema = z.object({
|
|
documentId: z.string(),
|
|
collectionName: collectionName,
|
|
fileId: z.string(),
|
|
filename: z.string(),
|
|
status: z.enum(["queued", "indexing", "indexed", "failed", "cancelled"]),
|
|
error: z.string().nullable(),
|
|
indexedAt: z.string().nullable(),
|
|
requestedBy: z.string(),
|
|
})
|
|
export type DocumentView = z.output<typeof documentViewSchema>
|
|
|
|
export const searchResultSchema = z.object({
|
|
fileId: z.string(),
|
|
filename: z.string(),
|
|
collectionName: collectionName,
|
|
snippet: z.string(),
|
|
/** 0..1; higher is closer. */
|
|
score: z.number(),
|
|
/** 1-based chunk position in the document: where a citation points. */
|
|
chunk: z.number().int().min(1),
|
|
})
|
|
export type SearchResult = z.output<typeof searchResultSchema>
|
|
|
|
export const collectionList = procedure("knowledge.collection.list")
|
|
.kind("read")
|
|
.scope("project")
|
|
.permission("knowledge.knowledge.search")
|
|
.input(z.object({ ...inProject }))
|
|
.output(z.object({ collections: z.array(collectionViewSchema) }))
|
|
.contract()
|
|
|
|
export const collectionCreate = procedure("knowledge.collection.create")
|
|
.kind("write")
|
|
.scope("project")
|
|
.permission("knowledge.collection.manage")
|
|
.input(z.object({ ...inProject, name: collectionName }))
|
|
.output(collectionViewSchema)
|
|
.audit(COLLECTION_CREATED)
|
|
.contract()
|
|
|
|
export const documentIndex = procedure("knowledge.document.index")
|
|
.kind("write")
|
|
.scope("project")
|
|
.permission("knowledge.knowledge.manage")
|
|
.input(z.object({ ...inProject, collectionName: collectionName, fileId: z.string() }))
|
|
.output(documentViewSchema)
|
|
.contract()
|
|
|
|
export const documentList = procedure("knowledge.document.list")
|
|
.kind("read")
|
|
.scope("project")
|
|
.permission("knowledge.knowledge.search")
|
|
.input(z.object({ ...inProject, collectionName: collectionName }))
|
|
.output(z.object({ documents: z.array(documentViewSchema) }))
|
|
.contract()
|
|
|
|
export const documentRemove = procedure("knowledge.document.remove")
|
|
.kind("write")
|
|
.scope("project")
|
|
.permission("knowledge.knowledge.manage")
|
|
.input(z.object({ ...inProject, collectionName: collectionName, fileId: z.string() }))
|
|
.output(documentViewSchema)
|
|
.contract()
|
|
|
|
/**
|
|
* The retrieval call. It embeds the query through the AI gateway before it searches, so it is an
|
|
* effect: a read procedure cannot leave the process. Exposed as an MCP tool on purpose (`.tool()`).
|
|
*/
|
|
export const knowledgeSearch = procedure("knowledge.search")
|
|
.kind("effect")
|
|
.scope("project")
|
|
.permission("knowledge.knowledge.search")
|
|
.tool()
|
|
.input(
|
|
z.object({
|
|
...inProject,
|
|
collectionName: collectionName,
|
|
query: z.string().min(1).max(500),
|
|
limit: z.number().int().min(1).max(25).optional(),
|
|
}),
|
|
)
|
|
.output(z.object({ results: z.array(searchResultSchema) }))
|
|
.contract()
|
|
|
|
export const knowledgeStatus = procedure("knowledge.status")
|
|
.kind("read")
|
|
.scope("project")
|
|
.permission("knowledge.knowledge.search")
|
|
.input(z.object({ ...inProject }))
|
|
.output(
|
|
z.object({
|
|
/** The module flag's answer for this project. */
|
|
enabled: z.boolean(),
|
|
/** The service readiness, kept separate from the indexing indicator below. */
|
|
service: z.object({
|
|
state: z.enum(["registered", "indexing", "ready", "degraded", "disabled", "unavailable"]),
|
|
reasons: z.array(z.string()),
|
|
}),
|
|
documents: z.object({
|
|
queued: z.number().int(),
|
|
indexing: z.number().int(),
|
|
indexed: z.number().int(),
|
|
failed: z.number().int(),
|
|
cancelled: z.number().int(),
|
|
}),
|
|
}),
|
|
)
|
|
.contract()
|
|
|
|
export const settingsGet = procedure("knowledge.settings.get")
|
|
.kind("read")
|
|
.scope("org")
|
|
.permission("knowledge.settings.manage")
|
|
.input(z.object({ ...inOrg }))
|
|
.output(
|
|
z.object({
|
|
enabled: z.boolean(),
|
|
backend: z.enum(["vectors", "platform"]).nullable(),
|
|
source: z.enum(["operator", "managed"]).nullable(),
|
|
url: z.string().nullable(),
|
|
}),
|
|
)
|
|
.contract()
|
|
|
|
export const settingsEnableDocumentAi = procedure("knowledge.settings.enableDocumentAi")
|
|
.kind("write")
|
|
.scope("org")
|
|
.permission("knowledge.settings.manage")
|
|
.input(z.object({ ...inOrg }))
|
|
.output(z.object({ enabled: z.boolean(), backend: z.enum(["vectors", "platform"]) }))
|
|
.audit(DOCUMENT_AI_ENABLED)
|
|
.contract()
|
|
|
|
export const settingsDisableDocumentAi = procedure("knowledge.settings.disableDocumentAi")
|
|
.kind("write")
|
|
.scope("org")
|
|
.permission("knowledge.settings.manage")
|
|
.input(z.object({ ...inOrg }))
|
|
.output(z.object({ enabled: z.boolean() }))
|
|
.audit(DOCUMENT_AI_DISABLED)
|
|
.contract()
|
|
|
|
export const KNOWLEDGE_PROCEDURES = [
|
|
collectionList,
|
|
collectionCreate,
|
|
documentIndex,
|
|
documentList,
|
|
documentRemove,
|
|
knowledgeSearch,
|
|
knowledgeStatus,
|
|
settingsGet,
|
|
settingsEnableDocumentAi,
|
|
settingsDisableDocumentAi,
|
|
]
|
|
|
|
export type { DocumentStatus, ServiceState }
|