跳转到正文

shared.ts ​

源文件: examples/control-flow/routing/shared.ts · 下载原文件 · 示例使用说明

ts
import { pathToFileURL } from "node:url";
import { createDitto, graph, loadRuntimeConfigFile, type DittoRuntime } from "@codesoul-co/ditto/runtime";
import { createInferWorker, type ModelConfig, type NodeResult, type SampleOutput } from "@codesoul-co/ditto/worker/infer";
import { createInteractionWorker, type RegisteredTool } from "@codesoul-co/ditto/worker/interaction";
import type { JsonObject } from "@codesoul-co/ditto/contracts";

export type Runner = Pick<DittoRuntime, "run">;
export interface Request { readonly id: string; readonly text: string; readonly model: ModelConfig }
export interface RecordValue { readonly code: string; readonly quantity: number }
export const extractInstruction = 'Extract the pickup record. Return ONLY JSON {"code":string,"quantity":integer}. Preserve the code exactly. Never invent missing fields.';
export const exampleRequest = { id: "pickup-731", text: "Pickup code PICKUP-731; quantity 3." };

export function json(result: NodeResult<SampleOutput>): Record<string, unknown> {
  if (result.status !== "success" || !result.output || result.output.finishReason !== "stop"
    || result.output.message.role !== "assistant" || typeof result.output.message.content !== "string") {
    throw new Error(`Model did not complete: ${result.error?.code ?? result.status}`);
  }
  const value: unknown = JSON.parse(result.output.message.content.trim().replace(/^```(?:json)?\s*/, "").replace(/\s*```$/, ""));
  if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Expected a JSON object");
  return value as Record<string, unknown>;
}
export function record(result: NodeResult<SampleOutput>): RecordValue {
  const value = json(result);
  if (typeof value.code !== "string" || !value.code.trim() || typeof value.quantity !== "number"
    || !Number.isSafeInteger(value.quantity) || value.quantity < 0) throw new Error("Invalid pickup record");
  return { code: value.code, quantity: value.quantity };
}
export function validateRequest(input: Request): void {
  if (!input.id.trim() || !input.text.trim()) throw new Error("Request id and text must be nonempty");
}
export function sampleGraph(id: string, instruction = extractInstruction) {
  return graph<Request>(id).node("sample", "INFER.REASONING.SAMPLE", [], input => ({
    model: input.model,
    messages: [{ role: "system", content: instruction }, { role: "user", content: input.text }],
  }));
}
export const deliveryGraph = graph<{ id: string; content: JsonObject }>("routing-delivery")
  .node("delivered", "INTERACTION.OUTPUT", [], input => ({
    deliveryId: input.id, message: { role: "assistant", content: input.content },
  }));
export async function deliver(runtime: Runner, id: string, content: JsonObject) {
  const output = await runtime.run(deliveryGraph, { id, content });
  if (output.delivered.status !== "accepted") throw new Error(`Delivery was not accepted: ${output.delivered.status}`);
  return output.delivered;
}
export function isMain(url: string): boolean {
  return !!process.argv[1] && url === pathToFileURL(process.argv[1]).href;
}
/** CLI bootstrap only. Importing an example performs no work. */
export async function cli(run: (runtime: DittoRuntime, model: ModelConfig) => Promise<unknown>, tools: readonly RegisteredTool[] = []) {
  const config = loadRuntimeConfigFile("ditto.yaml", process.env);
  if (!config.model) throw new Error("Configure the default INFER model in .env");
  const runtime = createDitto({ config, sandbox: { ...config.sandbox, tools: tools.map(tool => tool.name) }, workers: [
    createInferWorker(), createInteractionWorker({ tools, output: { async deliver(input) {
      console.log(JSON.stringify(input.message.content, null, 2));
      return { deliveryId: input.deliveryId, status: "accepted" };
    } } }),
  ] });
  try { await run(runtime, config.model); } finally { await runtime.close(); }
}

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