Skip to content

Commit cb74397

Browse files
committed
Validate rerank config in createKnowledgePlane and re-export search result types
Standalone plane construction skipped the maxDocChars check that mount ran, so a bad override only degraded silently outside HTTP. Move validation into createKnowledgePlane so both paths share it. Re-export HybridSearchResult, SearchHit, and VisibilitySpec from the public surface, and list createKnowledgePlane in AGENTS.md and PRODUCT.md.
1 parent acec748 commit cb74397

4 files changed

Lines changed: 33 additions & 18 deletions

File tree

‎AGENTS.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ CI runs `typecheck` + `test` — both must pass before any push.
1818

1919
## Layout
2020

21-
- `src/index.ts` — public surface: `mountKnowledgeEngine`, `mountKnowledgeRoutes`
21+
- `src/index.ts` — public surface: `mountKnowledgeEngine`, `mountKnowledgeRoutes`, `createKnowledgePlane`
2222
- `src/mount-config.ts` / `src/config.ts` — mount config + engine config
2323
- `src/routes/` — Hono routes (capture, search, timeline)
2424
- `src/services/` — capture / search / transform logic

‎PRODUCT.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ SDK never creates one; it mounts onto yours. Nothing else ships in this repo.**
1111

1212
1. **Public surface**: `mountKnowledgeEngine(app, opts)` drops the knowledge
1313
plane + routes onto an Interchange `createApp`;
14+
`createKnowledgePlane(config)` builds the same plane without mounting HTTP
15+
(CLI seeders, batch ingesters, tests);
1416
`runKnowledgeMigrations(url)` applies the pgvector schema;
1517
`loadKnowledgeConfig()` / `KnowledgeConfig` is the mount config.
1618
2. **The SDK authenticates nothing.** Identity is the request principal, read
@@ -41,6 +43,7 @@ Claude Code / Codex / Workbench (clients)
4143
│ Host Interchange createApp │
4244
│ resolves principal + tenant + grants │
4345
│ + mountKnowledgeEngine(app, opts) │
46+
│ or createKnowledgePlane(config) │
4447
│ reads c.get("principal") from context │
4548
│ guards via host requireGrant(...) │
4649
│ │ in-process │
@@ -65,6 +68,8 @@ Access is gated by the host's grant system: pass `grants` (the same
6568
`{ grantStore, conditionRegistry }` you give `createApp`) to
6669
`mountKnowledgeEngine`. HTTP routes run `requireGrant("knowledge", <action>)`
6770
(`capture` for capture, `search` for search/timeline).
71+
`createKnowledgePlane` builds the same plane without routes — callers acting
72+
for a user must check the capability themselves (see README).
6873

6974
### On the wire
7075

‎src/index.ts‎

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ import {
1717
type GrantConfig,
1818
type RouteDeps,
1919
} from "./routes/mount.ts";
20-
import { toRerankClientConfig } from "./services/search.ts";
21-
import { validateRerankConfig } from "./core/rerank-client.ts";
2220

2321
// Config
2422
export type { KnowledgeConfig } from "./mount-config.ts";
@@ -31,13 +29,16 @@ export { RerankConfigError } from "./core/rerank-client.ts";
3129
// request — a CLI seeder, a batch ingester, or a test — without standing up a
3230
// Hono app just to get a plane. Callers acting on behalf of a user are
3331
// responsible for the capability check `requireGrant` would have performed; see
34-
// the README.
32+
// the README. Rerank config is validated at construction (same as mount).
3533
export { createKnowledgePlane } from "./knowledge.ts";
3634
export type {
35+
HybridSearchResult,
3736
KnowledgeCaptureParams,
3837
KnowledgeIdentity,
3938
KnowledgePlane,
4039
KnowledgeSearchParams,
40+
SearchHit,
41+
VisibilitySpec,
4142
} from "./knowledge.ts";
4243
export { KnowledgeError } from "./knowledge.ts";
4344
export { CaptureLog, type CaptureEvent } from "./capture-log.ts";
@@ -77,20 +78,8 @@ export function mountKnowledgeEngine(
7778
app: Hono<TenantEnv>,
7879
options: MountKnowledgeEngineOptions,
7980
): MountedKnowledgeEngine {
80-
// Catch a chunk-size / reranker-limit mismatch here, at mount time, rather
81-
// than silently on every search once the reranker starts rejecting
82-
// batches. This throws instead of warning: a mismatch here means every
83-
// rerank call for this host WILL 413 and silently degrade to fused
84-
// ranking, with no per-request signal — a boot-time failure surfaces that
85-
// once, loudly, instead of leaving reranking quietly broken indefinitely.
86-
// This is only safe to throw on because the per-model default budget
87-
// (`defaultMaxDocCharsForModel`) is self-consistent by construction —
88-
// validation can only fire on an operator's own `maxDocChars` override,
89-
// never spuriously on an unmodified config, regardless of which model
90-
// that config resolves to.
91-
const rerankConfig = toRerankClientConfig(options.config.knowledge.rerank);
92-
if (rerankConfig) validateRerankConfig(rerankConfig);
93-
81+
// Rerank config validation runs inside createKnowledgePlane so standalone
82+
// construction and the mount path share one check.
9483
const knowledge = createKnowledgePlane(options.config);
9584
const captureLog = new CaptureLog();
9685
const deps: RouteDeps = {

‎src/knowledge.ts‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,21 @@ import type { EngineConfig } from "./config.ts";
66
import { formatCaughtError, log } from "./log.ts";
77
import { createDb, type Db, type RawSql } from "./db/client.ts";
88
import type { VisibilitySpec } from "./core/schemas/document.ts";
9+
import { validateRerankConfig } from "./core/rerank-client.ts";
910
import { captureDocument } from "./services/capture.ts";
1011
import {
1112
hybridSearch,
1213
KnowledgeSearchInputError,
14+
toRerankClientConfig,
1315
type HybridSearchResult,
1416
} from "./services/search.ts";
1517
import type { KnowledgeConfig } from "./mount-config.ts";
1618

19+
// Re-export so hosts typing plane.search() results don't reach into services/.
20+
export type { HybridSearchResult } from "./services/search.ts";
21+
export type { SearchHit } from "./core/schemas/search.ts";
22+
export type { VisibilitySpec } from "./core/schemas/document.ts";
23+
1724
export type KnowledgeIdentity = {
1825
principalId: string;
1926
tenantId: string;
@@ -53,6 +60,20 @@ export type KnowledgePlane = {
5360
};
5461

5562
export function createKnowledgePlane(config: KnowledgeConfig): KnowledgePlane {
63+
// Catch a chunk-size / reranker-limit mismatch at construction time, rather
64+
// than silently on every search once the reranker starts rejecting batches.
65+
// Throws instead of warning: a mismatch means every rerank call for this
66+
// host WILL 413 and silently degrade to fused ranking, with no per-request
67+
// signal — a construction-time failure surfaces that once, loudly.
68+
// Safe to throw because the per-model default budget
69+
// (`defaultMaxDocCharsForModel`) is self-consistent by construction —
70+
// validation can only fire on an operator's own `maxDocChars` override,
71+
// never spuriously on an unmodified config.
72+
// Lives here (not only in mountKnowledgeEngine) so standalone construction
73+
// cannot silently degrade on a bad override.
74+
const rerankConfig = toRerankClientConfig(config.knowledge.rerank);
75+
if (rerankConfig) validateRerankConfig(rerankConfig);
76+
5677
const engineConfig: EngineConfig = config.knowledge;
5778
const { db, sql }: { db: Db; sql: RawSql } = createDb(engineConfig);
5879
const deps = { db, sql, config: engineConfig };

0 commit comments

Comments
 (0)