Skip to content

Example 24: LLM Adapter

What It Is

LLM Adapter is the provider boundary for model-backed DAG nodes. The Archivist can run against local Ollama, browser models, or cloud APIs because nodes depend on an LlmAdapterInterface, not on a provider SDK.

The application builds an adapter or cascade before execution, injects it through services, and lets DAG routes handle the response variant. Provider probing, request timeout, system prompt injection, and tool-call support stay behind the adapter boundary.

How It Works

Nodes depend on an LlmAdapterInterface supplied through services. The host builds an adapter or cascade before execution, injects it into node constructors, and lets DAG routes handle the model response variant. Provider probing, request timeout, system prompt injection, and tool-call support stay behind the adapter boundary.

This keeps provider choice outside the graph. The DAG still says "classify," "extract," "rank," and "compose"; the service layer decides whether those calls go to a local model, a browser runtime, or a cloud backend.

Diagrams, Examples, and Outputs

DAG registration and diagram

The adapter is injected service state; the DAG shows where model-backed nodes sit in the flow. The Archivist is the in-browser owner for adapter selection.

Archivist LLM adapter DAG

55 placements
DAG JSON-LD registered with the dispatcher
{
  "@context": {
    "@version": 1.1,
    "name": {
      "@id": "https://noocodec.dev/ontology/dag/name"
    },
    "version": {
      "@id": "https://noocodec.dev/ontology/dag/version"
    },
    "entrypoints": {
      "@id": "https://noocodec.dev/ontology/dag/entrypoints",
      "@container": "@index"
    },
    "nodes": {
      "@id": "https://noocodec.dev/ontology/dag/nodes",
      "@container": "@set"
    },
    "outputs": {
      "@id": "https://noocodec.dev/ontology/dag/outputs"
    },
    "node": {
      "@id": "https://noocodec.dev/ontology/dag/node"
    },
    "dag": {
      "@id": "https://noocodec.dev/ontology/dag/dag"
    },
    "body": {
      "@id": "https://noocodec.dev/ontology/dag/body"
    },
    "source": {
      "@id": "https://noocodec.dev/ontology/dag/source"
    },
    "sources": {
      "@id": "https://noocodec.dev/ontology/dag/sources",
      "@container": "@index"
    },
    "itemKey": {
      "@id": "https://noocodec.dev/ontology/dag/itemKey"
    },
    "execution": {
      "@id": "https://noocodec.dev/ontology/dag/execution"
    },
    "concurrency": {
      "@id": "https://noocodec.dev/ontology/dag/concurrency"
    },
    "throttle": {
      "@id": "https://noocodec.dev/ontology/dag/throttle"
    },
    "reservoir": {
      "@id": "https://noocodec.dev/ontology/dag/reservoir"
    },
    "gather": {
      "@id": "https://noocodec.dev/ontology/dag/gather"
    },
    "dagReference": {
      "@id": "https://noocodec.dev/ontology/dag/dagReference",
      "@type": "@id"
    },
    "DagReference": {
      "@id": "https://noocodec.dev/ontology/dag/DagReference"
    },
    "from": {
      "@id": "https://noocodec.dev/ontology/dag/from"
    },
    "path": {
      "@id": "https://noocodec.dev/ontology/dag/path"
    },
    "candidates": {
      "@id": "https://noocodec.dev/ontology/dag/candidates",
      "@container": "@set"
    },
    "candidateDag": {
      "@id": "https://noocodec.dev/ontology/dag/candidateDag",
      "@type": "@id"
    },
    "selectedDag": {
      "@id": "https://noocodec.dev/ontology/dag/selectedDag",
      "@type": "@id"
    },
    "resultField": {
      "@id": "https://noocodec.dev/ontology/dag/resultField"
    },
    "policy": {
      "@id": "https://noocodec.dev/ontology/dag/policy"
    },
    "reducer": {
      "@id": "https://noocodec.dev/ontology/dag/reducer"
    },
    "outcome": {
      "@id": "https://noocodec.dev/ontology/dag/outcome"
    },
    "phase": {
      "@id": "https://noocodec.dev/ontology/dag/phase"
    },
    "stateMapping": {
      "@id": "https://noocodec.dev/ontology/dag/stateMapping"
    },
    "container": {
      "@id": "https://noocodec.dev/ontology/dag/container"
    },
    "DAG": {
      "@id": "https://noocodec.dev/ontology/dag/DAG"
    },
    "Placement": {
      "@id": "https://noocodec.dev/ontology/dag/Placement"
    },
    "SingleNode": {
      "@id": "https://noocodec.dev/ontology/dag/SingleNode"
    },
    "ScatterNode": {
      "@id": "https://noocodec.dev/ontology/dag/ScatterNode"
    },
    "EmbeddedDAGNode": {
      "@id": "https://noocodec.dev/ontology/dag/EmbeddedDAGNode"
    },
    "GatherNode": {
      "@id": "https://noocodec.dev/ontology/dag/GatherNode"
    },
    "TerminalNode": {
      "@id": "https://noocodec.dev/ontology/dag/TerminalNode"
    },
    "PhaseNode": {
      "@id": "https://noocodec.dev/ontology/dag/PhaseNode"
    }
  },
  "@id": "urn:noocodec:dag:the-archivist",
  "@type": "DAG",
  "name": "the-archivist",
  "version": "6.0",
  "entrypoints": {
    "main": "urn:noocodec:dag:the-archivist/node/park-for-input"
  },
  "nodes": [
    {
      "@id": "urn:noocodec:dag:the-archivist/node/setup",
      "@type": "PhaseNode",
      "name": "setup",
      "node": "urn:noocodec:node:pre-run-setup",
      "phase": "pre"
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/park-for-input",
      "@type": "SingleNode",
      "name": "park-for-input",
      "node": "urn:noocodec:node:park-for-input",
      "outputs": {
        "parked": "urn:noocodec:dag:the-archivist/node/park-for-input",
        "resumed": "urn:noocodec:dag:the-archivist/node/recall-context"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/recall-context",
      "@type": "SingleNode",
      "name": "recall-context",
      "node": "urn:noocodec:node:recall-context",
      "outputs": {
        "recalled": "urn:noocodec:dag:the-archivist/node/classify-intent"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/classify-intent",
      "@type": "SingleNode",
      "name": "classify-intent",
      "node": "urn:noocodec:node:classify-intent",
      "outputs": {
        "lookup-author": "urn:noocodec:dag:the-archivist/node/author-search",
        "find-reviews": "urn:noocodec:dag:the-archivist/node/reviews-extract",
        "describe-book": "urn:noocodec:dag:the-archivist/node/describe-extract",
        "recommend-similar": "urn:noocodec:dag:the-archivist/node/recommend-similar",
        "recall-memories": "urn:noocodec:dag:the-archivist/node/memory-recall",
        "on-topic": "urn:noocodec:dag:the-archivist/node/on-topic-search",
        "recommend-top-rated": "urn:noocodec:dag:the-archivist/node/recommend-extract",
        "off-topic": "urn:noocodec:dag:the-archivist/node/decline-off-topic",
        "retry": "urn:noocodec:dag:the-archivist/node/classify-intent",
        "salvage": "urn:noocodec:dag:the-archivist/node/classify-intent-salvage"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/classify-intent-salvage",
      "@type": "SingleNode",
      "name": "classify-intent-salvage",
      "node": "urn:noocodec:node:classify-intent-salvage",
      "outputs": {
        "done": "urn:noocodec:dag:the-archivist/node/on-topic-search"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/on-topic-search",
      "@type": "EmbeddedDAGNode",
      "name": "on-topic-search",
      "outputs": {
        "success": "urn:noocodec:dag:the-archivist/node/compose-loop",
        "error": "urn:noocodec:dag:the-archivist/node/compose-empty"
      },
      "dag": "urn:noocodec:dag:book-search-scatter",
      "stateMapping": {
        "output": {
          "terms": "terms",
          "toolPlan": "toolPlan",
          "candidates": "candidates",
          "shortlist": "shortlist",
          "priorContext": "priorContext",
          "failureCause": "failureCause"
        }
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/author-search",
      "@type": "EmbeddedDAGNode",
      "name": "author-search",
      "outputs": {
        "success": "urn:noocodec:dag:the-archivist/node/group-by-year",
        "error": "urn:noocodec:dag:the-archivist/node/compose-empty"
      },
      "dag": "urn:noocodec:dag:book-search-scatter",
      "stateMapping": {
        "output": {
          "terms": "terms",
          "toolPlan": "toolPlan",
          "candidates": "candidates",
          "shortlist": "shortlist",
          "priorContext": "priorContext",
          "failureCause": "failureCause"
        }
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/group-by-year",
      "@type": "SingleNode",
      "name": "group-by-year",
      "node": "urn:noocodec:node:group-by-year",
      "outputs": {
        "ordered": "urn:noocodec:dag:the-archivist/node/compose-loop"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/reviews-extract",
      "@type": "SingleNode",
      "name": "reviews-extract",
      "node": "urn:noocodec:node:extract-query",
      "outputs": {
        "success": "urn:noocodec:dag:the-archivist/node/reviews-decide-tools",
        "retry": "urn:noocodec:dag:the-archivist/node/reviews-extract",
        "salvage": "urn:noocodec:dag:the-archivist/node/reviews-extract-salvage"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/reviews-extract-salvage",
      "@type": "SingleNode",
      "name": "reviews-extract-salvage",
      "node": "urn:noocodec:node:extract-query-salvage",
      "outputs": {
        "done": "urn:noocodec:dag:the-archivist/node/reviews-decide-tools"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/reviews-decide-tools",
      "@type": "SingleNode",
      "name": "reviews-decide-tools",
      "node": "urn:noocodec:node:decide-tools",
      "outputs": {
        "tools": "urn:noocodec:dag:the-archivist/node/reviews-build-worksets",
        "no-tools": "urn:noocodec:dag:the-archivist/node/reviews-build-worksets",
        "retry": "urn:noocodec:dag:the-archivist/node/reviews-decide-tools",
        "salvage": "urn:noocodec:dag:the-archivist/node/reviews-decide-tools-salvage"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/reviews-decide-tools-salvage",
      "@type": "SingleNode",
      "name": "reviews-decide-tools-salvage",
      "node": "urn:noocodec:node:decide-tools-salvage",
      "outputs": {
        "done": "urn:noocodec:dag:the-archivist/node/reviews-build-worksets"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/reviews-build-worksets",
      "@type": "SingleNode",
      "name": "reviews-build-worksets",
      "node": "urn:noocodec:node:build-book-worksets",
      "outputs": {
        "ready": "urn:noocodec:dag:the-archivist/node/reviews-scatter"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/reviews-scatter",
      "@type": "ScatterNode",
      "name": "reviews-scatter",
      "source": "bookWorksets",
      "body": {
        "dag": {
          "@type": "DagReference",
          "from": "item",
          "path": "dagIri",
          "candidates": [
            "urn:noocodec:tool:web_search_books",
            "urn:noocodec:tool:google_books_search",
            "urn:noocodec:tool:subject_search",
            "urn:noocodec:tool:wikipedia_summary"
          ]
        }
      },
      "outputs": {
        "success": "urn:noocodec:dag:the-archivist/node/reviews-gather",
        "error": "urn:noocodec:dag:the-archivist/node/reviews-gather",
        "empty": "urn:noocodec:dag:the-archivist/node/reviews-rank"
      },
      "itemKey": "currentItem",
      "reducer": "any-success",
      "execution": {
        "mode": "item",
        "concurrency": 4
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/reviews-gather",
      "@type": "GatherNode",
      "name": "reviews-gather",
      "sources": {
        "urn:noocodec:dag:the-archivist/node/reviews-scatter": {}
      },
      "gather": {
        "strategy": "tool-candidate-merge"
      },
      "outputs": {
        "success": "urn:noocodec:dag:the-archivist/node/reviews-rank",
        "error": "urn:noocodec:dag:the-archivist/node/reviews-rank",
        "empty": "urn:noocodec:dag:the-archivist/node/reviews-rank"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/reviews-rank",
      "@type": "SingleNode",
      "name": "reviews-rank",
      "node": "urn:noocodec:node:rank-by-rating",
      "outputs": {
        "ranked": "urn:noocodec:dag:the-archivist/node/reviews-merge"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/reviews-merge",
      "@type": "SingleNode",
      "name": "reviews-merge",
      "node": "urn:noocodec:node:merge-candidates",
      "outputs": {
        "ranked": "urn:noocodec:dag:the-archivist/node/reviews-record",
        "empty": "urn:noocodec:dag:the-archivist/node/compose-empty"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/reviews-record",
      "@type": "SingleNode",
      "name": "reviews-record",
      "node": "urn:noocodec:node:record-findings",
      "outputs": {
        "recorded": "urn:noocodec:dag:the-archivist/node/reviews-gate"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/reviews-gate",
      "@type": "SingleNode",
      "name": "reviews-gate",
      "node": "urn:noocodec:node:has-citations-gate",
      "outputs": {
        "pass": "urn:noocodec:dag:the-archivist/node/reviews-recall",
        "fail": "urn:noocodec:dag:the-archivist/node/compose-empty"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/reviews-recall",
      "@type": "SingleNode",
      "name": "reviews-recall",
      "node": "urn:noocodec:node:recall-past-visits",
      "outputs": {
        "recalled": "urn:noocodec:dag:the-archivist/node/compose-loop"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/recommend-extract",
      "@type": "SingleNode",
      "name": "recommend-extract",
      "node": "urn:noocodec:node:extract-query",
      "outputs": {
        "success": "urn:noocodec:dag:the-archivist/node/recommend-decide-tools",
        "retry": "urn:noocodec:dag:the-archivist/node/recommend-extract",
        "salvage": "urn:noocodec:dag:the-archivist/node/recommend-extract-salvage"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/recommend-extract-salvage",
      "@type": "SingleNode",
      "name": "recommend-extract-salvage",
      "node": "urn:noocodec:node:extract-query-salvage",
      "outputs": {
        "done": "urn:noocodec:dag:the-archivist/node/recommend-decide-tools"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/recommend-decide-tools",
      "@type": "SingleNode",
      "name": "recommend-decide-tools",
      "node": "urn:noocodec:node:decide-tools",
      "outputs": {
        "tools": "urn:noocodec:dag:the-archivist/node/recommend-build-worksets",
        "no-tools": "urn:noocodec:dag:the-archivist/node/recommend-build-worksets",
        "retry": "urn:noocodec:dag:the-archivist/node/recommend-decide-tools",
        "salvage": "urn:noocodec:dag:the-archivist/node/recommend-decide-tools-salvage"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/recommend-decide-tools-salvage",
      "@type": "SingleNode",
      "name": "recommend-decide-tools-salvage",
      "node": "urn:noocodec:node:decide-tools-salvage",
      "outputs": {
        "done": "urn:noocodec:dag:the-archivist/node/recommend-build-worksets"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/recommend-build-worksets",
      "@type": "SingleNode",
      "name": "recommend-build-worksets",
      "node": "urn:noocodec:node:build-book-worksets",
      "outputs": {
        "ready": "urn:noocodec:dag:the-archivist/node/recommend-scatter"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/recommend-scatter",
      "@type": "ScatterNode",
      "name": "recommend-scatter",
      "source": "bookWorksets",
      "body": {
        "dag": {
          "@type": "DagReference",
          "from": "item",
          "path": "dagIri",
          "candidates": [
            "urn:noocodec:tool:web_search_books",
            "urn:noocodec:tool:google_books_search",
            "urn:noocodec:tool:subject_search",
            "urn:noocodec:tool:wikipedia_summary"
          ]
        }
      },
      "outputs": {
        "success": "urn:noocodec:dag:the-archivist/node/recommend-gather",
        "error": "urn:noocodec:dag:the-archivist/node/recommend-gather",
        "empty": "urn:noocodec:dag:the-archivist/node/recommend-rank"
      },
      "itemKey": "currentItem",
      "reducer": "any-success",
      "execution": {
        "mode": "item",
        "concurrency": 4
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/recommend-gather",
      "@type": "GatherNode",
      "name": "recommend-gather",
      "sources": {
        "urn:noocodec:dag:the-archivist/node/recommend-scatter": {}
      },
      "gather": {
        "strategy": "tool-candidate-merge"
      },
      "outputs": {
        "success": "urn:noocodec:dag:the-archivist/node/recommend-rank",
        "error": "urn:noocodec:dag:the-archivist/node/recommend-rank",
        "empty": "urn:noocodec:dag:the-archivist/node/recommend-rank"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/recommend-rank",
      "@type": "SingleNode",
      "name": "recommend-rank",
      "node": "urn:noocodec:node:rank-by-rating",
      "outputs": {
        "ranked": "urn:noocodec:dag:the-archivist/node/recommend-merge"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/recommend-merge",
      "@type": "SingleNode",
      "name": "recommend-merge",
      "node": "urn:noocodec:node:merge-candidates",
      "outputs": {
        "ranked": "urn:noocodec:dag:the-archivist/node/recommend-record",
        "empty": "urn:noocodec:dag:the-archivist/node/compose-empty"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/recommend-record",
      "@type": "SingleNode",
      "name": "recommend-record",
      "node": "urn:noocodec:node:record-findings",
      "outputs": {
        "recorded": "urn:noocodec:dag:the-archivist/node/recommend-gate"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/recommend-gate",
      "@type": "SingleNode",
      "name": "recommend-gate",
      "node": "urn:noocodec:node:has-citations-gate",
      "outputs": {
        "pass": "urn:noocodec:dag:the-archivist/node/recommend-recall",
        "fail": "urn:noocodec:dag:the-archivist/node/compose-empty"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/recommend-recall",
      "@type": "SingleNode",
      "name": "recommend-recall",
      "node": "urn:noocodec:node:recall-past-visits",
      "outputs": {
        "recalled": "urn:noocodec:dag:the-archivist/node/compose-loop"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/describe-extract",
      "@type": "SingleNode",
      "name": "describe-extract",
      "node": "urn:noocodec:node:extract-query",
      "outputs": {
        "success": "urn:noocodec:dag:the-archivist/node/describe-decide-tools",
        "retry": "urn:noocodec:dag:the-archivist/node/describe-extract",
        "salvage": "urn:noocodec:dag:the-archivist/node/describe-extract-salvage"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/describe-extract-salvage",
      "@type": "SingleNode",
      "name": "describe-extract-salvage",
      "node": "urn:noocodec:node:extract-query-salvage",
      "outputs": {
        "done": "urn:noocodec:dag:the-archivist/node/describe-decide-tools"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/describe-decide-tools",
      "@type": "SingleNode",
      "name": "describe-decide-tools",
      "node": "urn:noocodec:node:decide-tools",
      "outputs": {
        "tools": "urn:noocodec:dag:the-archivist/node/describe-build-worksets",
        "no-tools": "urn:noocodec:dag:the-archivist/node/describe-build-worksets",
        "retry": "urn:noocodec:dag:the-archivist/node/describe-decide-tools",
        "salvage": "urn:noocodec:dag:the-archivist/node/describe-decide-tools-salvage"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/describe-decide-tools-salvage",
      "@type": "SingleNode",
      "name": "describe-decide-tools-salvage",
      "node": "urn:noocodec:node:decide-tools-salvage",
      "outputs": {
        "done": "urn:noocodec:dag:the-archivist/node/describe-build-worksets"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/describe-build-worksets",
      "@type": "SingleNode",
      "name": "describe-build-worksets",
      "node": "urn:noocodec:node:build-book-worksets",
      "outputs": {
        "ready": "urn:noocodec:dag:the-archivist/node/describe-scatter"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/describe-scatter",
      "@type": "ScatterNode",
      "name": "describe-scatter",
      "source": "bookWorksets",
      "body": {
        "dag": {
          "@type": "DagReference",
          "from": "item",
          "path": "dagIri",
          "candidates": [
            "urn:noocodec:tool:web_search_books",
            "urn:noocodec:tool:google_books_search",
            "urn:noocodec:tool:subject_search",
            "urn:noocodec:tool:wikipedia_summary"
          ]
        }
      },
      "outputs": {
        "success": "urn:noocodec:dag:the-archivist/node/describe-gather",
        "error": "urn:noocodec:dag:the-archivist/node/compose-empty",
        "empty": "urn:noocodec:dag:the-archivist/node/compose-empty"
      },
      "itemKey": "currentItem",
      "reducer": "any-success",
      "execution": {
        "mode": "item",
        "concurrency": 4
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/describe-gather",
      "@type": "GatherNode",
      "name": "describe-gather",
      "sources": {
        "urn:noocodec:dag:the-archivist/node/describe-scatter": {}
      },
      "gather": {
        "strategy": "tool-candidate-merge"
      },
      "outputs": {
        "success": "urn:noocodec:dag:the-archivist/node/describe-pick",
        "error": "urn:noocodec:dag:the-archivist/node/compose-empty",
        "empty": "urn:noocodec:dag:the-archivist/node/compose-empty"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/describe-pick",
      "@type": "SingleNode",
      "name": "describe-pick",
      "node": "urn:noocodec:node:pick-best-match",
      "outputs": {
        "picked": "urn:noocodec:dag:the-archivist/node/describe-merge"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/describe-merge",
      "@type": "SingleNode",
      "name": "describe-merge",
      "node": "urn:noocodec:node:merge-candidates",
      "outputs": {
        "ranked": "urn:noocodec:dag:the-archivist/node/describe-record",
        "empty": "urn:noocodec:dag:the-archivist/node/compose-empty"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/describe-record",
      "@type": "SingleNode",
      "name": "describe-record",
      "node": "urn:noocodec:node:record-findings",
      "outputs": {
        "recorded": "urn:noocodec:dag:the-archivist/node/describe-gate"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/describe-gate",
      "@type": "SingleNode",
      "name": "describe-gate",
      "node": "urn:noocodec:node:has-citations-gate",
      "outputs": {
        "pass": "urn:noocodec:dag:the-archivist/node/describe-recall",
        "fail": "urn:noocodec:dag:the-archivist/node/compose-empty"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/describe-recall",
      "@type": "SingleNode",
      "name": "describe-recall",
      "node": "urn:noocodec:node:recall-past-visits",
      "outputs": {
        "recalled": "urn:noocodec:dag:the-archivist/node/compose-loop"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/recommend-similar",
      "@type": "SingleNode",
      "name": "recommend-similar",
      "node": "urn:noocodec:node:recommend-similar",
      "outputs": {
        "seeded": "urn:noocodec:dag:the-archivist/node/similar-search",
        "empty": "urn:noocodec:dag:the-archivist/node/compose-empty"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/similar-search",
      "@type": "EmbeddedDAGNode",
      "name": "similar-search",
      "outputs": {
        "success": "urn:noocodec:dag:the-archivist/node/compose-loop",
        "error": "urn:noocodec:dag:the-archivist/node/compose-empty"
      },
      "dag": "urn:noocodec:dag:book-search-scatter",
      "stateMapping": {
        "output": {
          "terms": "terms",
          "toolPlan": "toolPlan",
          "candidates": "candidates",
          "shortlist": "shortlist",
          "priorContext": "priorContext",
          "failureCause": "failureCause"
        }
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/compose-loop",
      "@type": "EmbeddedDAGNode",
      "name": "compose-loop",
      "outputs": {
        "success": "urn:noocodec:dag:the-archivist/node/respond-to-visitor",
        "error": "urn:noocodec:dag:the-archivist/node/compose-empty"
      },
      "dag": "urn:noocodec:dag:compose-retry-loop",
      "stateMapping": {
        "output": {
          "draft": "draft",
          "approvalState": "approvalState"
        }
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/respond-to-visitor",
      "@type": "SingleNode",
      "name": "respond-to-visitor",
      "node": "urn:noocodec:node:respond-to-visitor",
      "outputs": {
        "success": "urn:noocodec:dag:the-archivist/node/end"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/memory-recall",
      "@type": "SingleNode",
      "name": "memory-recall",
      "node": "urn:noocodec:node:recall-memories",
      "outputs": {
        "recalled": "urn:noocodec:dag:the-archivist/node/compose-memory-recall"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/compose-memory-recall",
      "@type": "SingleNode",
      "name": "compose-memory-recall",
      "node": "urn:noocodec:node:compose-memory-response",
      "outputs": {
        "drafted": "urn:noocodec:dag:the-archivist/node/respond-to-visitor",
        "retry": "urn:noocodec:dag:the-archivist/node/compose-memory-recall",
        "salvage": "urn:noocodec:dag:the-archivist/node/compose-memory-salvage"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/compose-memory-salvage",
      "@type": "SingleNode",
      "name": "compose-memory-salvage",
      "node": "urn:noocodec:node:compose-memory-salvage",
      "outputs": {
        "done": "urn:noocodec:dag:the-archivist/node/respond-to-visitor"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/decline-off-topic",
      "@type": "SingleNode",
      "name": "decline-off-topic",
      "node": "urn:noocodec:node:decline-off-topic",
      "outputs": {
        "success": "urn:noocodec:dag:the-archivist/node/end"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/compose-empty",
      "@type": "SingleNode",
      "name": "compose-empty",
      "node": "urn:noocodec:node:compose-empty",
      "outputs": {
        "drafted": "urn:noocodec:dag:the-archivist/node/respond-to-visitor",
        "retry": "urn:noocodec:dag:the-archivist/node/compose-empty",
        "salvage": "urn:noocodec:dag:the-archivist/node/compose-empty-salvage"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/compose-empty-salvage",
      "@type": "SingleNode",
      "name": "compose-empty-salvage",
      "node": "urn:noocodec:node:compose-empty-salvage",
      "outputs": {
        "done": "urn:noocodec:dag:the-archivist/node/respond-to-visitor"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/end",
      "@type": "TerminalNode",
      "name": "end",
      "outcome": "completed"
    }
  ]
}
Mermaid generated from the same DAG
Mermaid source
%%{init: {"flowchart":{"nodeSpacing":92,"rankSpacing":104,"padding":28}}}%%
flowchart TB
  %% the-archivist (v6.0)
  entry_main(["main"])
  entry_main --> urn_noocodec_dag_the-archivist/node/park-for-input
  urn_noocodec_dag_the-archivist/node/setup(["setup (pre)"])
  urn_noocodec_dag_the-archivist/node/park-for-input["park-for-input"]
  urn_noocodec_dag_the-archivist/node/park-for-input -->|parked| urn_noocodec_dag_the-archivist/node/park-for-input
  urn_noocodec_dag_the-archivist/node/park-for-input -->|resumed| urn_noocodec_dag_the-archivist/node/recall-context
  urn_noocodec_dag_the-archivist/node/recall-context["recall-context"]
  urn_noocodec_dag_the-archivist/node/recall-context -->|recalled| urn_noocodec_dag_the-archivist/node/classify-intent
  urn_noocodec_dag_the-archivist/node/classify-intent["classify-intent"]
  urn_noocodec_dag_the-archivist/node/classify-intent -->|lookup-author| urn_noocodec_dag_the-archivist/node/author-search
  urn_noocodec_dag_the-archivist/node/classify-intent -->|find-reviews| urn_noocodec_dag_the-archivist/node/reviews-extract
  urn_noocodec_dag_the-archivist/node/classify-intent -->|describe-book| urn_noocodec_dag_the-archivist/node/describe-extract
  urn_noocodec_dag_the-archivist/node/classify-intent -->|recommend-similar| urn_noocodec_dag_the-archivist/node/recommend-similar
  urn_noocodec_dag_the-archivist/node/classify-intent -->|recall-memories| urn_noocodec_dag_the-archivist/node/memory-recall
  urn_noocodec_dag_the-archivist/node/classify-intent -->|on-topic| urn_noocodec_dag_the-archivist/node/on-topic-search
  urn_noocodec_dag_the-archivist/node/classify-intent -->|recommend-top-rated| urn_noocodec_dag_the-archivist/node/recommend-extract
  urn_noocodec_dag_the-archivist/node/classify-intent -->|off-topic| urn_noocodec_dag_the-archivist/node/decline-off-topic
  urn_noocodec_dag_the-archivist/node/classify-intent -->|retry| urn_noocodec_dag_the-archivist/node/classify-intent
  urn_noocodec_dag_the-archivist/node/classify-intent -->|salvage| urn_noocodec_dag_the-archivist/node/classify-intent-salvage
  urn_noocodec_dag_the-archivist/node/classify-intent-salvage["classify-intent-salvage"]
  urn_noocodec_dag_the-archivist/node/classify-intent-salvage -->|done| urn_noocodec_dag_the-archivist/node/on-topic-search
  urn_noocodec_dag_the-archivist/node/on-topic-search[["on-topic-search"]]
  urn_noocodec_dag_the-archivist/node/on-topic-search -->|success| urn_noocodec_dag_the-archivist/node/compose-loop
  urn_noocodec_dag_the-archivist/node/on-topic-search -->|error| urn_noocodec_dag_the-archivist/node/compose-empty
  urn_noocodec_dag_the-archivist/node/author-search[["author-search"]]
  urn_noocodec_dag_the-archivist/node/author-search -->|success| urn_noocodec_dag_the-archivist/node/group-by-year
  urn_noocodec_dag_the-archivist/node/author-search -->|error| urn_noocodec_dag_the-archivist/node/compose-empty
  urn_noocodec_dag_the-archivist/node/group-by-year["group-by-year"]
  urn_noocodec_dag_the-archivist/node/group-by-year -->|ordered| urn_noocodec_dag_the-archivist/node/compose-loop
  urn_noocodec_dag_the-archivist/node/reviews-extract["reviews-extract"]
  urn_noocodec_dag_the-archivist/node/reviews-extract -->|success| urn_noocodec_dag_the-archivist/node/reviews-decide-tools
  urn_noocodec_dag_the-archivist/node/reviews-extract -->|retry| urn_noocodec_dag_the-archivist/node/reviews-extract
  urn_noocodec_dag_the-archivist/node/reviews-extract -->|salvage| urn_noocodec_dag_the-archivist/node/reviews-extract-salvage
  urn_noocodec_dag_the-archivist/node/reviews-extract-salvage["reviews-extract-salvage"]
  urn_noocodec_dag_the-archivist/node/reviews-extract-salvage -->|done| urn_noocodec_dag_the-archivist/node/reviews-decide-tools
  urn_noocodec_dag_the-archivist/node/reviews-decide-tools["reviews-decide-tools"]
  urn_noocodec_dag_the-archivist/node/reviews-decide-tools -->|tools| urn_noocodec_dag_the-archivist/node/reviews-build-worksets
  urn_noocodec_dag_the-archivist/node/reviews-decide-tools -->|no-tools| urn_noocodec_dag_the-archivist/node/reviews-build-worksets
  urn_noocodec_dag_the-archivist/node/reviews-decide-tools -->|retry| urn_noocodec_dag_the-archivist/node/reviews-decide-tools
  urn_noocodec_dag_the-archivist/node/reviews-decide-tools -->|salvage| urn_noocodec_dag_the-archivist/node/reviews-decide-tools-salvage
  urn_noocodec_dag_the-archivist/node/reviews-decide-tools-salvage["reviews-decide-tools-salvage"]
  urn_noocodec_dag_the-archivist/node/reviews-decide-tools-salvage -->|done| urn_noocodec_dag_the-archivist/node/reviews-build-worksets
  urn_noocodec_dag_the-archivist/node/reviews-build-worksets["reviews-build-worksets"]
  urn_noocodec_dag_the-archivist/node/reviews-build-worksets -->|ready| urn_noocodec_dag_the-archivist/node/reviews-scatter
  urn_noocodec_dag_the-archivist/node/reviews-scatter[/"reviews-scatter"/]
  urn_noocodec_dag_the-archivist/node/reviews-scatter -->|success| urn_noocodec_dag_the-archivist/node/reviews-gather
  urn_noocodec_dag_the-archivist/node/reviews-scatter -->|error| urn_noocodec_dag_the-archivist/node/reviews-gather
  urn_noocodec_dag_the-archivist/node/reviews-scatter -->|empty| urn_noocodec_dag_the-archivist/node/reviews-rank
  urn_noocodec_dag_the-archivist/node/reviews-gather{"reviews-gather"}
  urn_noocodec_dag_the-archivist/node/reviews-gather -->|success| urn_noocodec_dag_the-archivist/node/reviews-rank
  urn_noocodec_dag_the-archivist/node/reviews-gather -->|error| urn_noocodec_dag_the-archivist/node/reviews-rank
  urn_noocodec_dag_the-archivist/node/reviews-gather -->|empty| urn_noocodec_dag_the-archivist/node/reviews-rank
  urn_noocodec_dag_the-archivist/node/reviews-rank["reviews-rank"]
  urn_noocodec_dag_the-archivist/node/reviews-rank -->|ranked| urn_noocodec_dag_the-archivist/node/reviews-merge
  urn_noocodec_dag_the-archivist/node/reviews-merge["reviews-merge"]
  urn_noocodec_dag_the-archivist/node/reviews-merge -->|ranked| urn_noocodec_dag_the-archivist/node/reviews-record
  urn_noocodec_dag_the-archivist/node/reviews-merge -->|empty| urn_noocodec_dag_the-archivist/node/compose-empty
  urn_noocodec_dag_the-archivist/node/reviews-record["reviews-record"]
  urn_noocodec_dag_the-archivist/node/reviews-record -->|recorded| urn_noocodec_dag_the-archivist/node/reviews-gate
  urn_noocodec_dag_the-archivist/node/reviews-gate["reviews-gate"]
  urn_noocodec_dag_the-archivist/node/reviews-gate -->|pass| urn_noocodec_dag_the-archivist/node/reviews-recall
  urn_noocodec_dag_the-archivist/node/reviews-gate -->|fail| urn_noocodec_dag_the-archivist/node/compose-empty
  urn_noocodec_dag_the-archivist/node/reviews-recall["reviews-recall"]
  urn_noocodec_dag_the-archivist/node/reviews-recall -->|recalled| urn_noocodec_dag_the-archivist/node/compose-loop
  urn_noocodec_dag_the-archivist/node/recommend-extract["recommend-extract"]
  urn_noocodec_dag_the-archivist/node/recommend-extract -->|success| urn_noocodec_dag_the-archivist/node/recommend-decide-tools
  urn_noocodec_dag_the-archivist/node/recommend-extract -->|retry| urn_noocodec_dag_the-archivist/node/recommend-extract
  urn_noocodec_dag_the-archivist/node/recommend-extract -->|salvage| urn_noocodec_dag_the-archivist/node/recommend-extract-salvage
  urn_noocodec_dag_the-archivist/node/recommend-extract-salvage["recommend-extract-salvage"]
  urn_noocodec_dag_the-archivist/node/recommend-extract-salvage -->|done| urn_noocodec_dag_the-archivist/node/recommend-decide-tools
  urn_noocodec_dag_the-archivist/node/recommend-decide-tools["recommend-decide-tools"]
  urn_noocodec_dag_the-archivist/node/recommend-decide-tools -->|tools| urn_noocodec_dag_the-archivist/node/recommend-build-worksets
  urn_noocodec_dag_the-archivist/node/recommend-decide-tools -->|no-tools| urn_noocodec_dag_the-archivist/node/recommend-build-worksets
  urn_noocodec_dag_the-archivist/node/recommend-decide-tools -->|retry| urn_noocodec_dag_the-archivist/node/recommend-decide-tools
  urn_noocodec_dag_the-archivist/node/recommend-decide-tools -->|salvage| urn_noocodec_dag_the-archivist/node/recommend-decide-tools-salvage
  urn_noocodec_dag_the-archivist/node/recommend-decide-tools-salvage["recommend-decide-tools-salvage"]
  urn_noocodec_dag_the-archivist/node/recommend-decide-tools-salvage -->|done| urn_noocodec_dag_the-archivist/node/recommend-build-worksets
  urn_noocodec_dag_the-archivist/node/recommend-build-worksets["recommend-build-worksets"]
  urn_noocodec_dag_the-archivist/node/recommend-build-worksets -->|ready| urn_noocodec_dag_the-archivist/node/recommend-scatter
  urn_noocodec_dag_the-archivist/node/recommend-scatter[/"recommend-scatter"/]
  urn_noocodec_dag_the-archivist/node/recommend-scatter -->|success| urn_noocodec_dag_the-archivist/node/recommend-gather
  urn_noocodec_dag_the-archivist/node/recommend-scatter -->|error| urn_noocodec_dag_the-archivist/node/recommend-gather
  urn_noocodec_dag_the-archivist/node/recommend-scatter -->|empty| urn_noocodec_dag_the-archivist/node/recommend-rank
  urn_noocodec_dag_the-archivist/node/recommend-gather{"recommend-gather"}
  urn_noocodec_dag_the-archivist/node/recommend-gather -->|success| urn_noocodec_dag_the-archivist/node/recommend-rank
  urn_noocodec_dag_the-archivist/node/recommend-gather -->|error| urn_noocodec_dag_the-archivist/node/recommend-rank
  urn_noocodec_dag_the-archivist/node/recommend-gather -->|empty| urn_noocodec_dag_the-archivist/node/recommend-rank
  urn_noocodec_dag_the-archivist/node/recommend-rank["recommend-rank"]
  urn_noocodec_dag_the-archivist/node/recommend-rank -->|ranked| urn_noocodec_dag_the-archivist/node/recommend-merge
  urn_noocodec_dag_the-archivist/node/recommend-merge["recommend-merge"]
  urn_noocodec_dag_the-archivist/node/recommend-merge -->|ranked| urn_noocodec_dag_the-archivist/node/recommend-record
  urn_noocodec_dag_the-archivist/node/recommend-merge -->|empty| urn_noocodec_dag_the-archivist/node/compose-empty
  urn_noocodec_dag_the-archivist/node/recommend-record["recommend-record"]
  urn_noocodec_dag_the-archivist/node/recommend-record -->|recorded| urn_noocodec_dag_the-archivist/node/recommend-gate
  urn_noocodec_dag_the-archivist/node/recommend-gate["recommend-gate"]
  urn_noocodec_dag_the-archivist/node/recommend-gate -->|pass| urn_noocodec_dag_the-archivist/node/recommend-recall
  urn_noocodec_dag_the-archivist/node/recommend-gate -->|fail| urn_noocodec_dag_the-archivist/node/compose-empty
  urn_noocodec_dag_the-archivist/node/recommend-recall["recommend-recall"]
  urn_noocodec_dag_the-archivist/node/recommend-recall -->|recalled| urn_noocodec_dag_the-archivist/node/compose-loop
  urn_noocodec_dag_the-archivist/node/describe-extract["describe-extract"]
  urn_noocodec_dag_the-archivist/node/describe-extract -->|success| urn_noocodec_dag_the-archivist/node/describe-decide-tools
  urn_noocodec_dag_the-archivist/node/describe-extract -->|retry| urn_noocodec_dag_the-archivist/node/describe-extract
  urn_noocodec_dag_the-archivist/node/describe-extract -->|salvage| urn_noocodec_dag_the-archivist/node/describe-extract-salvage
  urn_noocodec_dag_the-archivist/node/describe-extract-salvage["describe-extract-salvage"]
  urn_noocodec_dag_the-archivist/node/describe-extract-salvage -->|done| urn_noocodec_dag_the-archivist/node/describe-decide-tools
  urn_noocodec_dag_the-archivist/node/describe-decide-tools["describe-decide-tools"]
  urn_noocodec_dag_the-archivist/node/describe-decide-tools -->|tools| urn_noocodec_dag_the-archivist/node/describe-build-worksets
  urn_noocodec_dag_the-archivist/node/describe-decide-tools -->|no-tools| urn_noocodec_dag_the-archivist/node/describe-build-worksets
  urn_noocodec_dag_the-archivist/node/describe-decide-tools -->|retry| urn_noocodec_dag_the-archivist/node/describe-decide-tools
  urn_noocodec_dag_the-archivist/node/describe-decide-tools -->|salvage| urn_noocodec_dag_the-archivist/node/describe-decide-tools-salvage
  urn_noocodec_dag_the-archivist/node/describe-decide-tools-salvage["describe-decide-tools-salvage"]
  urn_noocodec_dag_the-archivist/node/describe-decide-tools-salvage -->|done| urn_noocodec_dag_the-archivist/node/describe-build-worksets
  urn_noocodec_dag_the-archivist/node/describe-build-worksets["describe-build-worksets"]
  urn_noocodec_dag_the-archivist/node/describe-build-worksets -->|ready| urn_noocodec_dag_the-archivist/node/describe-scatter
  urn_noocodec_dag_the-archivist/node/describe-scatter[/"describe-scatter"/]
  urn_noocodec_dag_the-archivist/node/describe-scatter -->|success| urn_noocodec_dag_the-archivist/node/describe-gather
  urn_noocodec_dag_the-archivist/node/describe-scatter -->|error| urn_noocodec_dag_the-archivist/node/compose-empty
  urn_noocodec_dag_the-archivist/node/describe-scatter -->|empty| urn_noocodec_dag_the-archivist/node/compose-empty
  urn_noocodec_dag_the-archivist/node/describe-gather{"describe-gather"}
  urn_noocodec_dag_the-archivist/node/describe-gather -->|success| urn_noocodec_dag_the-archivist/node/describe-pick
  urn_noocodec_dag_the-archivist/node/describe-gather -->|error| urn_noocodec_dag_the-archivist/node/compose-empty
  urn_noocodec_dag_the-archivist/node/describe-gather -->|empty| urn_noocodec_dag_the-archivist/node/compose-empty
  urn_noocodec_dag_the-archivist/node/describe-pick["describe-pick"]
  urn_noocodec_dag_the-archivist/node/describe-pick -->|picked| urn_noocodec_dag_the-archivist/node/describe-merge
  urn_noocodec_dag_the-archivist/node/describe-merge["describe-merge"]
  urn_noocodec_dag_the-archivist/node/describe-merge -->|ranked| urn_noocodec_dag_the-archivist/node/describe-record
  urn_noocodec_dag_the-archivist/node/describe-merge -->|empty| urn_noocodec_dag_the-archivist/node/compose-empty
  urn_noocodec_dag_the-archivist/node/describe-record["describe-record"]
  urn_noocodec_dag_the-archivist/node/describe-record -->|recorded| urn_noocodec_dag_the-archivist/node/describe-gate
  urn_noocodec_dag_the-archivist/node/describe-gate["describe-gate"]
  urn_noocodec_dag_the-archivist/node/describe-gate -->|pass| urn_noocodec_dag_the-archivist/node/describe-recall
  urn_noocodec_dag_the-archivist/node/describe-gate -->|fail| urn_noocodec_dag_the-archivist/node/compose-empty
  urn_noocodec_dag_the-archivist/node/describe-recall["describe-recall"]
  urn_noocodec_dag_the-archivist/node/describe-recall -->|recalled| urn_noocodec_dag_the-archivist/node/compose-loop
  urn_noocodec_dag_the-archivist/node/recommend-similar["recommend-similar"]
  urn_noocodec_dag_the-archivist/node/recommend-similar -->|seeded| urn_noocodec_dag_the-archivist/node/similar-search
  urn_noocodec_dag_the-archivist/node/recommend-similar -->|empty| urn_noocodec_dag_the-archivist/node/compose-empty
  urn_noocodec_dag_the-archivist/node/similar-search[["similar-search"]]
  urn_noocodec_dag_the-archivist/node/similar-search -->|success| urn_noocodec_dag_the-archivist/node/compose-loop
  urn_noocodec_dag_the-archivist/node/similar-search -->|error| urn_noocodec_dag_the-archivist/node/compose-empty
  urn_noocodec_dag_the-archivist/node/compose-loop[["compose-loop"]]
  urn_noocodec_dag_the-archivist/node/compose-loop -->|success| urn_noocodec_dag_the-archivist/node/respond-to-visitor
  urn_noocodec_dag_the-archivist/node/compose-loop -->|error| urn_noocodec_dag_the-archivist/node/compose-empty
  urn_noocodec_dag_the-archivist/node/respond-to-visitor["respond-to-visitor"]
  urn_noocodec_dag_the-archivist/node/respond-to-visitor -->|success| urn_noocodec_dag_the-archivist/node/end
  urn_noocodec_dag_the-archivist/node/memory-recall["memory-recall"]
  urn_noocodec_dag_the-archivist/node/memory-recall -->|recalled| urn_noocodec_dag_the-archivist/node/compose-memory-recall
  urn_noocodec_dag_the-archivist/node/compose-memory-recall["compose-memory-recall"]
  urn_noocodec_dag_the-archivist/node/compose-memory-recall -->|drafted| urn_noocodec_dag_the-archivist/node/respond-to-visitor
  urn_noocodec_dag_the-archivist/node/compose-memory-recall -->|retry| urn_noocodec_dag_the-archivist/node/compose-memory-recall
  urn_noocodec_dag_the-archivist/node/compose-memory-recall -->|salvage| urn_noocodec_dag_the-archivist/node/compose-memory-salvage
  urn_noocodec_dag_the-archivist/node/compose-memory-salvage["compose-memory-salvage"]
  urn_noocodec_dag_the-archivist/node/compose-memory-salvage -->|done| urn_noocodec_dag_the-archivist/node/respond-to-visitor
  urn_noocodec_dag_the-archivist/node/decline-off-topic["decline-off-topic"]
  urn_noocodec_dag_the-archivist/node/decline-off-topic -->|success| urn_noocodec_dag_the-archivist/node/end
  urn_noocodec_dag_the-archivist/node/compose-empty["compose-empty"]
  urn_noocodec_dag_the-archivist/node/compose-empty -->|drafted| urn_noocodec_dag_the-archivist/node/respond-to-visitor
  urn_noocodec_dag_the-archivist/node/compose-empty -->|retry| urn_noocodec_dag_the-archivist/node/compose-empty
  urn_noocodec_dag_the-archivist/node/compose-empty -->|salvage| urn_noocodec_dag_the-archivist/node/compose-empty-salvage
  urn_noocodec_dag_the-archivist/node/compose-empty-salvage["compose-empty-salvage"]
  urn_noocodec_dag_the-archivist/node/compose-empty-salvage -->|done| urn_noocodec_dag_the-archivist/node/respond-to-visitor
  urn_noocodec_dag_the-archivist/node/end((("end")))

The LLM adapter surface provides a provider-agnostic interface for chat completion. The browser Archivist lets the user select among configured backends, instantiates the selected adapter, and injects it into ArchivistServices for classify/extract/rank/compose nodes.

Run

bash
npm run docs:dev

Open The Archivist and choose a backend in the Config panel.

What It Lets You Do

LLM adapters let applications swap model providers without changing DAG topology or node contracts. Use them when the same graph should run against local Ollama, browser models, cloud APIs, or a cascade that selects the first available backend.

They also give the application one place to enforce provider policy: request timeout, system prompt defaults, tool-call support, JSON mode, and capability probing.

Code Samples

The browser snippets show provider selection and service injection. The CLI snippet shows a preference-ordered cascade across multiple providers.

vue
/** Construct an LLM client for the active backend, or null when none is selected. */
function makeLlm() {
  if (activeBackend.value === null) return null;
  return ProviderInstantiator.instantiate(activeBackend.value, {
    'apiKeys': apiKeys.value,
    'model':   resolvedModel.value,
    ...(intentClassifier.value !== null ? { 'intentClassifier': intentClassifier.value } : {}),
  });
}
vue
/** Build a real services record for the checkpoint resume path. */
function buildServices(): ArchivistServices {
  const llm = makeLlm();
  if (llm === null) throw new Error('no backend selected');
  return {
    'webSearch':         webSearchTool,
    'googleBooks':       googleBooksTool,
    'subjectSearch':     subjectSearchTool,
    'wikipediaSummary':  wikipediaSummaryTool,
    'memory':            memoryStore,
    'llm':               llm,
    'embedder':          embedder.value,
    'nodeTimeouts':      {},
  };
}
ts
// Build a preference-ordered catalogue: probe each provider, discover the best
// available chat model, and add a catalogue entry only when a model resolves.
// The async discovery runs before `LlmAdapterCascade.create(...)`.
// Each factory closes over the already-constructed
// adapter instance; probe() runs lazily when cascade.select() is called.
const catalogue: CatalogueEntryType[] = [];

// Local-first: Ollama — no key required. Skip when daemon is unreachable or
// no chat model is installed.
const ollamaAdapter = new OllamaApiAdapter({ 'baseUrl': OLLAMA_BASE_URL });
const resolvedOllamaModel = await ollamaAdapter.selectChatModel({
  ...(Env.get('OLLAMA_MODEL').length > 0 ? { 'preferred': Env.get('OLLAMA_MODEL') } : {}),
});
if (resolvedOllamaModel !== null) {
  catalogue.push({
    'descriptor': { 'provider': 'ollama', 'model': resolvedOllamaModel, 'capabilities': CAPS_PARTIAL_TOOLS },
    'factory': () => ollamaAdapter,
  });
}

// Keyed providers: skip when the key is missing so `NO_ADAPTER_AVAILABLE`
// lists only the providers the user actually configured.
if (Env.get('GEMINI_API_KEY').length > 0) {
  const geminiAdapter = new GeminiApiAdapter(Env.get('GEMINI_API_KEY'));
  const geminiModel = await geminiAdapter.selectChatModel({
    ...(Env.get('GEMINI_MODEL').length > 0 ? { 'preferred': Env.get('GEMINI_MODEL') } : {}),
  });
  if (geminiModel !== null) {
    catalogue.push({
      'descriptor': { 'provider': 'gemini-api', 'model': geminiModel, 'capabilities': CAPS_FULL_TOOLS },
      'factory': () => geminiAdapter,
    });
  }
}
if (Env.get('CEREBRAS_API_KEY').length > 0) {
  const cerebrasAdapter = OpenAiCompatibleAdapter.cerebras(Env.get('CEREBRAS_API_KEY'));
  const cerebrasModel = await cerebrasAdapter.selectChatModel({
    ...(Env.get('CEREBRAS_MODEL').length > 0 ? { 'preferred': Env.get('CEREBRAS_MODEL') } : {}),
  });
  if (cerebrasModel !== null) {
    catalogue.push({
      'descriptor': { 'provider': 'cerebras', 'model': cerebrasModel, 'capabilities': CAPS_PARTIAL_TOOLS },
      'factory': () => cerebrasAdapter,
    });
  }
}
if (Env.get('GROQ_API_KEY').length > 0) {
  const groqAdapter = OpenAiCompatibleAdapter.groq(Env.get('GROQ_API_KEY'));
  const groqModel = await groqAdapter.selectChatModel({
    ...(Env.get('GROQ_MODEL').length > 0 ? { 'preferred': Env.get('GROQ_MODEL') } : {}),
  });
  if (groqModel !== null) {
    catalogue.push({
      'descriptor': { 'provider': 'groq', 'model': groqModel, 'capabilities': CAPS_PARTIAL_TOOLS },
      'factory': () => groqAdapter,
    });
  }
}
if (Env.get('MISTRAL_API_KEY').length > 0) {
  const mistralAdapter = OpenAiCompatibleAdapter.mistral(Env.get('MISTRAL_API_KEY'));
  const mistralModel = await mistralAdapter.selectChatModel({
    ...(Env.get('MISTRAL_MODEL').length > 0 ? { 'preferred': Env.get('MISTRAL_MODEL') } : {}),
  });
  if (mistralModel !== null) {
    catalogue.push({
      'descriptor': { 'provider': 'mistral', 'model': mistralModel, 'capabilities': CAPS_PARTIAL_TOOLS },
      'factory': () => mistralAdapter,
    });
  }
}
if (Env.get('OPENROUTER_API_KEY').length > 0) {
  const openRouterAdapter = OpenAiCompatibleAdapter.openRouter(Env.get('OPENROUTER_API_KEY'));
  const openRouterModel = await openRouterAdapter.selectChatModel({
    ...(Env.get('OPENROUTER_MODEL').length > 0 ? { 'preferred': Env.get('OPENROUTER_MODEL') } : {}),
  });
  if (openRouterModel !== null) {
    catalogue.push({
      'descriptor': { 'provider': 'openrouter', 'model': openRouterModel, 'capabilities': CAPS_PARTIAL_TOOLS },
      'factory': () => openRouterAdapter,
    });
  }
}

const cascade = LlmAdapterCascade.create(catalogue);
const adapter = await cascade.select();
logger.note(`backend: ${adapter.id} (${adapter.displayName})`);

Details for Nerds

Adapter options

Every adapter extends BaseAdapter, so two options are uniform across the whole surface — the cloud HTTP adapters (OpenAiCompatibleAdapter and its groq / cerebras / mistral / openRouter presets, anthropic, gemini-api, ollama) and the on-device adapters (gemini-nano, web-llm) alike:

  • systemPrompt. A default directive the base injects as the leading system message of any request that carries no system message. Leading position is load-bearing for on-device backends (the Chrome Prompt API rejects a system turn at any index but 0). A caller-supplied system turn is never overridden; an empty string is a no-op.
  • timeoutMs (default 60_000). A per-request deadline. The HTTP adapters enforce it around the network call; gemini-nano composes it into the LanguageModel.create() / session.prompt() abort signal, and web-llm races the non-cancellable MLC generation against it. On expiry the adapter rejects with a TIMEOUT-classified LlmError, so a cascade falls through to the next adapter instead of hanging.
ts
import { OpenAiCompatibleAdapter } from '@studnicky/dagonizer/adapter';

const adapter = OpenAiCompatibleAdapter.groq(process.env.GROQ_API_KEY ?? '', {
  systemPrompt: 'You are the Archivist. Answer concisely.',
  timeoutMs:    30_000,
});

Cascade creation

LlmAdapterCascade.create(catalogue) assembles a cascade from data. Async discovery runs before the create call — resolve models, filter nulls, then pass the finished catalogue. Each factory closes over the already-constructed adapter; probe() runs lazily inside cascade.select().

ts
import { LlmAdapterCascade, OpenAiCompatibleAdapter, type CatalogueEntryType } from '@studnicky/dagonizer/adapter';

// Async discovery step — happens before create()
const catalogue: CatalogueEntryType[] = [];

const ollamaAdapter = new OllamaApiAdapter();
const ollamaModel = await ollamaAdapter.selectChatModel();
if (ollamaModel !== null) {
  catalogue.push({
    descriptor: { provider: 'ollama', model: ollamaModel, capabilities: { toolUse: 'partial', structuredOutput: true, jsonMode: true } },
    factory:    () => ollamaAdapter,   // synchronous; closes over already-constructed adapter
  });
}

if (process.env.GROQ_API_KEY) {
  const groqAdapter = OpenAiCompatibleAdapter.groq(process.env.GROQ_API_KEY);
  const groqModel = await groqAdapter.selectChatModel();
  if (groqModel !== null) {
    catalogue.push({
      descriptor: { provider: 'groq', model: groqModel, capabilities: { toolUse: 'partial', structuredOutput: true, jsonMode: true } },
      factory:    () => groqAdapter,
    });
  }
}

// create() is synchronous — catalogue is already resolved
const cascade = LlmAdapterCascade.create(catalogue);
const adapter  = await cascade.select(); // probes in catalogue order

The Archivist CLI (examples/the-archivist/runArchivist.ts) uses this exact pattern across six providers.

  • OllamaApiAdapter. Wraps Ollama's OpenAI-compatible endpoint (/v1/chat/completions). Constructed with { model, baseUrl? }: model is required (Ollama models are pulled per-host); baseUrl defaults to http://127.0.0.1:11434. No API key required for local usage.
  • probe(). Each adapter implementation overrides probe() to report availability. OllamaApiAdapter.probe() issues a GET /api/tags with a short timeout (500 ms); returns true when the daemon answers 2xx, false on timeout or connection error. Never throws.
  • LlmAdapterCascade.create. Static factory that assembles an LlmAdapterCascade from a preference-ordered catalogue. Each CatalogueEntryType pairs an AdapterDescriptorShapeType (provider + model + capabilities) with a zero-arg factory. The factory creates a fresh LlmAdapterRegistry, registers every entry in catalogue order, and returns the configured cascade. Import both from @studnicky/dagonizer/adapter.
  • LlmAdapterRegistry. Stores adapters keyed by (provider, model) pairs. Created internally by the builder; access it directly only when you need dynamic runtime registration.
  • LlmAdapterCascade. Accepts a preference list and a registry. cascade.select() walks the list in order, calls adapter.probe() on each, and returns the first available adapter. Throws when no adapter is available.
  • Response routing. response.message.variant is 'text' for a plain completion or 'tools' when the model makes tool calls. The DAG node routes on variant to separate the two paths.

Watched over by the Order of Dagon.