跳转到正文

domain.ts ​

源文件: examples/_shared/tools/retrieval/domain.ts · 下载原文件 · 示例使用说明

ts
import { createHash } from "node:crypto";
import type { JsonObject } from "@codesoul-co/ditto/contracts";
export const modes = ["document-search", "knowledge-base", "web-search", "web-read", "multi-source", "rewrite", "expand", "source-location"] as const;
export type Mode = typeof modes[number];
export type Source = "documents" | "knowledge-internal" | "knowledge-external" | "web";
export interface Request {
  id: string; tenant: string; mode: Mode;
  knowledge: "internal" | "external" | "both"; internalKnowledgeKeys: string[]; question: string; query: string;
  vocabulary: string[]; documents: string[]; urls: string[]; allowedOrigins: string[];
  searchEngine: "wikipedia" | "brave"; allowPartial: boolean;
}
export interface Evidence {
  id: string; source: Source; uri: string; title: string; text: string;
  location: string; snapshot: string; queries: string[];
}
export interface Collected { evidence: Evidence[]; failures: { source: Source; code: string }[] }
export interface Finding { sourceId: string; quote: string }
export interface Report extends Collected {
  requestId: string; question: string; queries: string[]; findings: Finding[];
  status: "completed" | "partial" | "no-evidence";
}
export const json = (value: unknown): JsonObject => JSON.parse(JSON.stringify(value)) as JsonObject;
export const digest = (value: string) => createHash("sha256").update(value).digest("hex");
export function object(value: unknown): Record<string, unknown> {
  if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Expected object");
  return value as Record<string, unknown>;
}
export function text(value: unknown, maximum = 600): string {
  if (typeof value !== "string" || !value.trim() || value.length > maximum || /[\0\r\n]/.test(value)) throw new Error("Invalid text");
  return value.trim();
}
export function request(value: unknown): Request {
  const r = object(value);
  if (!modes.includes(r.mode as Mode) || !["wikipedia", "brave"].includes(String(r.searchEngine)) || typeof r.allowPartial !== "boolean") throw new Error("Invalid request");
  for (const field of ["vocabulary", "documents", "urls", "allowedOrigins", "internalKnowledgeKeys"]) if (!Array.isArray(r[field]) || r[field].length > 12) throw new Error(`Invalid ${field}`);
  if (!["internal", "external", "both"].includes(String(r.knowledge))) throw new Error("Select internal, external or both knowledge sources");
  const result: Request = { knowledge: r.knowledge as Request["knowledge"], internalKnowledgeKeys: (r.internalKnowledgeKeys as unknown[]).map(v => text(v, 128)), id: text(r.id, 64), tenant: text(r.tenant, 64), mode: r.mode as Mode, question: text(r.question), query: text(r.query), vocabulary: (r.vocabulary as unknown[]).map(v => text(v, 64)), documents: (r.documents as unknown[]).map(v => text(v, 128)), urls: (r.urls as unknown[]).map(v => text(v, 2048)), allowedOrigins: (r.allowedOrigins as unknown[]).map(v => text(v, 256)), searchEngine: r.searchEngine as Request["searchEngine"], allowPartial: r.allowPartial };
  if (!/^[a-zA-Z0-9_-]+$/.test(result.id) || !/^[a-zA-Z0-9_-]+$/.test(result.tenant)) throw new Error("Invalid identity");
  if (new Set(result.internalKnowledgeKeys).size !== result.internalKnowledgeKeys.length || result.internalKnowledgeKeys.some(key => !key.startsWith(`knowledge:${result.tenant}:`) || !/^[a-zA-Z0-9:_-]+$/.test(key))) throw new Error("Internal knowledge key outside tenant");
  if (result.documents.some(v => !/^[a-zA-Z0-9_-]+\.md$/.test(v))) throw new Error("Document must be a Markdown filename");
  for (const origin of result.allowedOrigins) { const u = new URL(origin); if (u.origin !== origin || u.protocol !== "https:" || u.username || u.password) throw new Error("Invalid allowed origin"); }
  return result;
}
export function queries(value: unknown, r: Request): string[] {
  const v = object(value).queries;
  const limit = r.mode === "expand" ? 3 : 1;
  if (!Array.isArray(v) || !v.length || v.length > limit) throw new Error("Invalid query plan");
  const items = [...new Set(v.map(q => text(q, 200)))];
  if (["rewrite", "expand"].includes(r.mode) && items.some(q => !r.vocabulary.includes(q))) throw new Error("Query must be an exact supported vocabulary entry");
  if (r.mode === "expand" && items.length < 2) throw new Error("Expansion requires distinct queries");
  if (!["rewrite", "expand"].includes(r.mode) && (items.length !== 1 || items[0] !== r.query)) throw new Error("Query must preserve the trusted search topic");
  return items;
}
export function findings(value: unknown, evidence: Evidence[]): Finding[] {
  const items = object(value).findings;
  if (!Array.isArray(items) || !items.length || items.length > evidence.length) throw new Error("Invalid findings");
  const result = items.map(item => {
    const f = object(item), sourceId = text(f.sourceId, 128), quote = text(f.quote, 1000);
    const e = evidence.find(x => x.id === sourceId);
    if (!e || quote.length < 20 || !e.text.includes(quote)) throw new Error("Unsupported citation");
    return { sourceId, quote };
  });
  if (new Set(result.map(f => f.sourceId)).size !== result.length) throw new Error("Duplicate citation");
  // Cover every retrieved passage: no silent omission of a source or expanded query.
  if (result.length !== evidence.length) throw new Error("Missing source coverage");
  return result;
}
export const tokens = (query: string) => [...new Set(query.toLowerCase().match(/[\p{L}\p{N}_-]+/gu) ?? [])].slice(0, 24);
export const sourceList = (r: Request): Source[] => {
  const knowledge: Source[] = r.knowledge === "internal" ? ["knowledge-internal"] : r.knowledge === "external" ? ["knowledge-external"] : ["knowledge-internal", "knowledge-external"];
  return r.mode === "multi-source" ? ["documents", ...knowledge, "web"] : r.mode === "knowledge-base" ? knowledge : ["web-search", "web-read"].includes(r.mode) ? ["web"] : ["documents"];
};

Ditto · @codesoul-co/ditto · Node.js 24+