Skip to content

dependencies.ts ​

Source: examples/control-flow/sequence/dependencies.ts · Download source · Example guide

ts
import { pathToFileURL } from "node:url";
import type { ContextItem } from "@codesoul-co/ditto/contracts";
import { createDitto, graph } from "@codesoul-co/ditto/runtime";
import { createContextWorker } from "@codesoul-co/ditto/worker/context";
import { createInteractionWorker, type InteractionOutputInput } from "@codesoul-co/ditto/worker/interaction";

export interface DependenciesInput {
  readonly items: readonly ContextItem[];
  readonly additions: readonly ContextItem[];
  readonly query: string;
  readonly limit: number;
  readonly deliveryId: string;
}

export const dependenciesInput: DependenciesInput = {
  items: [
    { id: "graph", content: "A Graph declares dependencies between steps." },
    { id: "memory", content: "Memory stores information across tasks." },
  ],
  additions: [{ id: "binding", content: "A bind function maps dependency outputs to the next input." }],
  query: "bind",
  limit: 1,
  deliveryId: "sequence-dependencies",
};

export const dependenciesGraph = graph<DependenciesInput>("sequence-dependencies")
  .node("loaded", "CONTEXT.LOAD", [], input => ({ sources: input.items }))
  .node("updated", "CONTEXT.UPDATE", ["loaded"], (input, { loaded }) => ({
    context: loaded,
    add: input.additions,
  }))
  .node("selected", "CONTEXT.SELECT", ["updated"], (input, { updated }) => ({
    context: updated,
    purpose: "infer",
    query: input.query,
    limit: input.limit,
  }))
  // Transitive dependencies are not visible in bind: request loaded explicitly.
  .node("delivered", "INTERACTION.OUTPUT", ["loaded", "selected"], (input, { loaded, selected }) => ({
    deliveryId: input.deliveryId,
    message: {
      role: "assistant",
      content: {
        originalItemIds: loaded.items.map(item => item.id),
        selectedItemIds: selected.selectedItemIds,
        selectedContent: selected.context.items.map(item =>
          typeof item.content === "string" ? item.content : JSON.stringify(item.content)),
      },
    },
  }));

export async function runDependencies(input: DependenciesInput = dependenciesInput) {
  // This application sink accepts deliveries into a local collection.
  const deliveries: InteractionOutputInput[] = [];
  const runtime = createDitto({ workers: [
    createContextWorker(),
    createInteractionWorker({ output: {
      async deliver(delivery) {
        deliveries.push(delivery);
        return { deliveryId: delivery.deliveryId, status: "accepted" };
      },
    } }),
  ] });
  try {
    const results = await runtime.run(dependenciesGraph, input);
    // A returned receipt is ordinary graph data, not an automatic success check.
    if (results.delivered.status !== "accepted") {
      throw new Error(`Delivery was not accepted: ${results.delivered.status}`);
    }
    return { ...results, deliveries };
  } finally {
    await runtime.close();
  }
}

if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
  const result = await runDependencies();
  console.log(JSON.stringify({
    message: result.deliveries[0]?.message.content,
    receipt: result.delivered,
  }, null, 2));
}

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