ConusAI vendor v0.1.0-beta.114
ci / gate (push) Waiting to run

This commit is contained in:
ConusAI vendor v0.1.0-beta.114
2026-11-14 09:56:45 +00:00
commit 1ce8d23c35
867 changed files with 122104 additions and 0 deletions
+180
View File
@@ -0,0 +1,180 @@
// The ports the knowledge module's use cases are written against, and the tokens the composition
// roots bind. Core imports no SDK and no adapter (spec §2): the search service and the enablement
// platform are shapes, and adapters/ is the only place an SDK exists.
import { type Container, type TenantScope, token } from "@conusai-kit/core"
import type { DataSource } from "@conusai-kit/data"
import type { FilesDeps } from "@conusai-kit/files"
import type { EnablementReason, KnowledgeRefusal, ServiceState } from "../contract/index.js"
import { config, KNOWLEDGE_FLAG_DEFAULTS } from "../contract/index.js"
/** A project scope: collections and documents live at this level. */
export type KnowledgeScope = TenantScope & { workspaceId: string; projectId: string }
/** An org scope: the enablement configuration is the organization's own. */
export type KnowledgeOrgScope = TenantScope & { orgId: string }
export interface KnowledgeDeps {
readonly data: DataSource
readonly files: FilesDeps
readonly flags: KnowledgeFlags
readonly beforeIndex?: (subjectUserId: string) => Promise<void>
/**
* The search service. Present for the effect procedure (search); a read or write procedure's deps
* never carry it, because resolving an effect port from either is a kind violation — the write
* only records the row and publishes the event, and the status read asks `KNOWLEDGE_READINESS`.
*/
readonly search?: KnowledgeSearchService | undefined
/** The document reader and the passage index, for the automatic PDF intake (core-11); bound in the worker. */
readonly reader?: KnowledgeReader | undefined
readonly passages?: KnowledgePassageIndex | undefined
/** The organization's eligibility and indexing quota, which the PDF intake checks before each read. */
readonly enablement?: DocumentAiPlatform | undefined
}
/** One cited search answer. */
export interface KnowledgeHit {
fileId: string
filename: string
collectionName: string
/** 0..1; higher is closer. */
score: number
chunk: number
snippet: string
page?: number
blockId?: string
chunkId?: string
}
/**
* The search service: index a document, search a collection with citations, remove a document, and
* report readiness. Its one adapter is the platform's managed documents-index service (ADR-061), which
* owns chunking, embeddings and the vector store. Every call leaves the process, so this port is an
* effect. `refusal` is why the service will not serve this deployment, when that is the answer.
*/
export interface KnowledgeSearchService {
readiness(scope: KnowledgeScope): Promise<{ state: ServiceState; reasons: string[]; refusal?: KnowledgeRefusal }>
index(
scope: KnowledgeScope,
input: { collectionName: string; fileId: string; filename: string; bytes: Uint8Array; signal?: AbortSignal },
): Promise<void>
search(
scope: KnowledgeScope,
input: { collectionName: string; query: string; limit: number; actorId: string },
): Promise<readonly KnowledgeHit[]>
/** `storageKey` names the object, resolved when the removal was asked for; only an older record lacks it, and then the file is looked up. */
remove(scope: KnowledgeScope, input: { collectionName: string; fileId: string; storageKey?: string }): Promise<void>
}
/**
* One passage of a read PDF, as the automatic intake indexes it: its page (1-based), its place on that
* page (0-based; with the page, its stable name across reads of the same document), the document's own
* block ids it holds lines of, and its text.
*/
export interface KnowledgePassage {
readonly page: number
readonly index: number
readonly blockIds: readonly string[]
readonly text: string
}
/**
* The platform's document reader, as the automatic intake uses it: a PDF in, its passages out. The
* same `idempotencyKey` reads the same document once. A document it can never make passages of throws
* with `permanent: "not_indexable"`; anything else is transient, and the queue retries the job.
*/
export interface KnowledgeReader {
read(
bytes: Uint8Array,
options: { filename: string; idempotencyKey: string; signal?: AbortSignal },
): Promise<readonly KnowledgePassage[]>
}
/**
* The knowledge service's inline index, one item per passage, each under the file's reference plus its
* page and place. The reference is asked for first, so the caller can record what it is about to index
* before any of it is sent, and `remove` takes it back because the file may be gone by then: the service
* removes a reference only exactly, so removal names every passage that was indexed.
*/
export interface KnowledgePassageIndex {
reference(scope: KnowledgeScope, fileId: string): Promise<string>
index(
scope: KnowledgeScope,
input: {
collectionName: string
documentRef: string
fileId: string
passages: readonly KnowledgePassage[]
signal?: AbortSignal
},
): Promise<void>
remove(
scope: KnowledgeScope,
input: { documentRef: string; passages: ReadonlyArray<Pick<KnowledgePassage, "page" | "index">> },
): Promise<void>
}
/**
* What the status read asks of the deployment's search backend. It is a port of its own because a read
* handler may not resolve `KNOWLEDGE_SEARCH`, and must not build it: a factory runs with the resolving
* handler's restrictions, so a search service built for a read would hold a read-only data source.
*/
export type KnowledgeReadiness = Pick<KnowledgeSearchService, "readiness">
/**
* The enablement platform: the checks the enablement flow needs, over the platform's own APIs.
* `checkEligibility` is where the plan capability and the rollout flags surface as typed reasons;
* `indexingQuota` answers today's remaining indexing volume.
*/
export interface DocumentAiPlatform {
checkEligibility(
scope: KnowledgeOrgScope,
): Promise<{ ok: true } | { ok: false; reason: EnablementReason; detail?: string }>
indexingQuota(scope: KnowledgeOrgScope): Promise<number>
}
/** What a flag service answers for the project a call runs in. */
export interface KnowledgeFlags {
enabled(flag: keyof typeof KNOWLEDGE_FLAG_DEFAULTS, scope: KnowledgeScope): boolean | Promise<boolean>
}
/**
* Bound by an app that evaluates flags (the platform's flag service). Unbound: knowledge is on for an
* organization once an administrator has enabled document AI, and off until then (`enablementFlags`).
*/
export const KNOWLEDGE_FLAGS = token<KnowledgeFlags>("knowledge.flags")
/** The organization's own switch: the row `knowledge.settings.enableDocumentAi` writes. */
export const enablementFlags = (data: DataSource): KnowledgeFlags => ({
enabled: async (flag, scope) => {
const row = (await data.forScope(scope).of(config).list({ limit: 1 })).items[0]
return row?.enabled ?? KNOWLEDGE_FLAG_DEFAULTS[flag]
},
})
export const flagsOf = (container: Container, data: DataSource): KnowledgeFlags =>
container.has(KNOWLEDGE_FLAGS) ? container.resolve(KNOWLEDGE_FLAGS) : enablementFlags(data)
/** Bound by the worker and the web shell with the module's search backend adapter. */
export const KNOWLEDGE_SEARCH = token<KnowledgeSearchService>("knowledge.search", { access: "effect" })
/** Bound by the worker with the platform's document reader: the automatic PDF intake reads through it. */
export const KNOWLEDGE_READER = token<KnowledgeReader>("knowledge.reader", { access: "effect" })
/** Bound by the worker with the knowledge service's inline index: the PDF intake's passages, and their removal. */
export const KNOWLEDGE_PASSAGES = token<KnowledgePassageIndex>("knowledge.passages", { access: "effect" })
/** Bound by the web shell with the readiness of the service `KNOWLEDGE_SEARCH` serves. */
export const KNOWLEDGE_READINESS = token<KnowledgeReadiness>("knowledge.readiness")
/**
* Bound by the web shell (the enablement flow runs in a request) and by the worker (the PDF intake checks
* eligibility and quota again before each read, core-11).
*/
export const KNOWLEDGE_ENABLEMENT = token<DocumentAiPlatform>("knowledge.enablement", { access: "effect" })
/** The per-run scope, under the module's own name (decision AT-F6). */
export const KNOWLEDGE_SCOPE = token<TenantScope>("knowledge.scope")
export const knowledgeEnabled = async (deps: Pick<KnowledgeDeps, "flags">, scope: KnowledgeScope): Promise<boolean> =>
deps.flags.enabled("knowledge.enabled", scope)