Knowledge / RAG
Configure namespace-bound retrieval and admin-only ingestion with the shipped knowledge factories and source types.
Knowledge lets an agent retrieve developer-curated content. The chat handler
receives only a read-only RetrieverFactory; ingestion uses a separate
write-capable KnowledgeStoreFactory in trusted admin code.
Enable retrieval
import { createChatHandler } from '@mordn/chat-widget/server';
import { getDefaultEmbedder }
from '@mordn/chat-widget/server/knowledge';
import { createKnowledgeDrizzleRetriever }
from '@mordn/chat-widget/server/knowledge/drizzle';
const embedder = getDefaultEmbedder(); // GEMINI_API_KEY or GOOGLE_GENERATIVE_AI_API_KEY
export const { GET, POST, DELETE } = createChatHandler({
getUserId: getChatUserId,
model: anthropic('claude-sonnet-4-5'),
store: createDrizzleChatStore(),
retrieval: {
store: createKnowledgeDrizzleRetriever({ embedder }),
resolveNamespaces: (ctx) => [
'agent:support-bot',
`user:${ctx.userId}:support-bot`,
],
mode: 'tool',
topK: 5,
minScore: 0.2,
vectorWeight: 1,
citations: true,
},
});
Use createEmbedder(model, dimensions?) only when wrapping an AI SDK embedding
model. The no-argument built-in Gemini path is getDefaultEmbedder() (or
createGeminiEmbedder(options)). Dimensions must match the vector column.
Retrieval options
storeRetrieverFactoryrequiredRead-only retriever factory.
resolveNamespaces(ctx) => string[] | Promise<string[]>requiredTrusted namespace resolver.
mode'tool' | 'auto'Default:
toolExpose
searchKnowledge or retrieve every turn.topKnumberDefault:
5Result cap; the default store clamps to 20.
minScorenumberDefault:
0.2Minimum relevance.
vectorWeightnumberDefault:
1Vector/lexical blend where supported.
citationsbooleanDefault:
trueEmit source-url parts.
buildQuery(messages, ctx) => string | Promise<string>Custom query builder for auto mode.
renderContext(chunks) => stringCustom injected context rendering.
Ingestion
The default docs-aware pipeline is:
SSRF-guarded loader → HTML-to-Markdown or Markdown passthrough
→ heading-aware chunking → content-hash diff → embedding → idempotent upsert
Set docsMode: false to use the legacy clean-text/chunk-text path.
Supported sources:
type IngestSource =
| { type: 'url'; url: string; title?: string }
| { type: 'sitemap'; url: string; limit?: number }
| { type: 'crawl'; url: string; depth?: number; maxPages?: number; sameOriginOnly?: boolean }
| { type: 'llms'; url: string; limit?: number }
| { type: 'file'; path?: string; fileKey?: string; filename?: string; mediaType?: string }
| { type: 'text'; text: string; title?: string };
Programmatic ingest
import { getDefaultEmbedder, ingest }
from '@mordn/chat-widget/server/knowledge';
import { createKnowledgeDrizzleStore }
from '@mordn/chat-widget/server/knowledge/drizzle';
const embedder = getDefaultEmbedder();
const factory = createKnowledgeDrizzleStore({ embedder });
export async function ingestText(namespace: string, text: string) {
const store = factory(namespace);
return ingest({
store,
namespace,
sources: [{ type: 'text', text, title: 'Uploaded note' }],
});
}
Use the CLI for URL, sitemap, crawl, llms.txt, status, and evaluation workflows.