This commit is contained in:
@@ -0,0 +1,187 @@
|
||||
// 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 }
|
||||
Reference in New Issue
Block a user