Skip to content

Latest commit

 

History

History
144 lines (101 loc) · 8.98 KB

File metadata and controls

144 lines (101 loc) · 8.98 KB

Architecture

Module-by-module walkthrough of the Lumen codebase. For the product pitch, see README.md. For the hands-on demo, see DEMO.md.


Overview

Lumen is a pipeline of four deterministic engines wrapped in two transport surfaces. A clinical recommendation enters via either MCP or A2A, fans out into the four engines, and exits as a signed FHIR transaction bundle.

                  ┌─────────────────────────────────────┐
                  │     transport surfaces              │
                  │  ┌──────────────┐  ┌────────────┐   │
   BYO agent ────►│  │ A2A          │  │ MCP        │   │◄─── direct
                  │  │ orchestrator │  │ FastMCP    │   │     (Claude / Cline / etc.)
                  │  │ FastAPI      │  │ workers=1  │   │
                  │  └──────┬───────┘  └─────┬──────┘   │
                  │         └─── self-fan ───┘          │
                  └────────────────┬────────────────────┘
                                   │
        ┌──────────────────────────┼──────────────────────────┐
        │                          │                          │
        ▼                          ▼                          ▼
   ┌─────────┐               ┌─────────┐                ┌─────────┐
   │ VERIFY  │               │SIMULATE │                │   VOI   │
   │ Z3 SMT  │               │  JAX MC │                │ entropy │
   │ Garden  │               │  DoWhy  │                │ Sobol   │
   │         │               │  EconML │                │         │
   └────┬────┘               └────┬────┘                └────┬────┘
        │                         │                          │
        └────────────┬────────────┴──────────────────────────┘
                     │
                     ▼
              ┌─────────────┐
              │  ADVERSARY  │   (only LLM call site)
              │  RAG + LLM  │
              │  → re-VERIFY│
              └──────┬──────┘
                     │
                     ▼
              ┌─────────────┐
              │   COMPOSE   │
              │  FHIR R4    │
              │  ed25519    │
              │  SHA-256    │
              └─────────────┘

Transport surfaces

lumen/lumen_mcp/ — FastMCP server

The MCP surface exposes five tools to any compatible client (Claude, Cline, MCP-aware orchestrators). Tools:

  • verify_recommendation → SMT verdict
  • simulate_project_trajectory → 30-day JAX projection
  • voi_rank_information_actions → ranked information-acquisition list
  • adversary_attack → LLM-drafted counter, re-verified
  • compose_brief → final signed FHIR bundle

Mounted at both / and /mcp (consistency with mixed routing in upstream MCP clients). Hard rule: --workers 1. FastMCP's in-process session cache breaks under multi-worker uvicorn; CI rejects any other value via tests/test_ci_invariants.py::test_workers_value_is_always_one.

lumen/lumen_orchestrator/ — A2A endpoint

FastAPI service that publishes an A2A agent card at /.well-known/agent-card.json. Receives a consult, self-fans-out into the MCP, runs the full pipeline, and returns a workspace-scoped task response. The fan-out is in-process — the orchestrator does not require the MCP to be a separate deployment.


Engine modules

lumen/lumen_core/garden/ — guideline DSL → SMT compiler

The Garden is the heart of Lumen. Each guideline is authored in a Python DSL (garden/source/<guideline>.py) whose AST compiles to SMT-LIB2 (garden/compiled/<guideline>.smt2). At verify time, the patient state is encoded as a model, the recommendation as an assertion, and Z3 discharges the theorem set.

Key files:

  • garden/dsl/ — primitive operators, predicates, and DSL types (Patient, Drug, Lab, Guideline, etc.)
  • garden/source/ — one file per guideline (NICE NG28, NG203, Beers 2023, STOPP/START v2, KDIGO AKI 2023)
  • garden/compiler.py — AST walker that emits SMT-LIB2
  • garden/compiled/ — checked-in .smt2 artefacts so re-checking is hermetic

Decidability is enforced: every theorem is in QF_NIA (quantifier-free non-linear integer arithmetic) and the DSL refuses operators that escape that fragment. See tests/test_garden_decidability.py.

lumen/lumen_core/cohort/ — Polars-indexed patient cohort

A parquet-backed Polars dataframe of workspace patients with PSM (propensity-score matching) for sub-population prior selection. When the simulation engine asks for "patients like Margaret" (CKD 3b + T2DM + HTN, age 70-75), the cohort module returns the matched set if N ≥ 30, otherwise falls back to named meta-analysis priors (and labels the brief accordingly).

lumen/lumen_core/pkpd/ — JAX PK/PD trajectory simulator

Differential-equation PK/PD models compiled with JAX for fast Monte Carlo. Inputs: patient state, candidate drug, dose, frequency. Output: posterior over per-day outcomes (eGFR, serum creatinine, AKI flag, etc.) with 10 000 samples. Causal identification via DoWhy/EconML.

Adversary path

The only LLM call site in the entire repo. shared/llm/adversary_prompt.py issues one call asking the model to attack the current recommendation — propose a better alternative. Whatever it returns is re-encoded as a Garden assertion and re-discharged by Z3. The LLM cannot ship anything past the theorem prover.


Shared infrastructure (shared/, vendored)

Originally extracted as a common module in the source monorepo. Vendored into this repo so it builds standalone.

  • shared/fhir/ — async HTTP FHIR client (R4 transactions), resource builders, intra-bundle reference resolution, graceful 422 retry for proxies that reject _total=accurate.
  • shared/audit/ — ed25519 signer/verifier, SHA-256 forward-hash chain, Chain.verify() that raises on tamper, swallowing audit logger primitive.
  • shared/llm/ — single LLM client wired through litellm with a hardcoded-model-string CI gate.
  • shared/types/ — Pydantic-strict shared types (PatientRef, Recommendation, Verdict, Provenance envelope).

Determinism invariants (enforced by tests/test_ci_invariants.py)

Invariant Test What it greps
Workers count test_workers_value_is_always_one Any --workers N where N != 1
LLM call sites test_no_hardcoded_llm_model_strings claude-3, gpt-4, o1-, haiku, gemini- outside shared/llm/
FHIR fullUrl scheme test_fhir_bundles_urn_uuid Any fullUrl that is not urn:uuid: + UUIDv4
Bundle reference closure test_fhir_bundles_resolve Any intra-bundle reference that doesn't resolve
FHIR R4 transaction shape test_fhir_transactions Any bundle that fails strict R4 schema

If you change LLM call sites, worker counts, or FHIR conformance, the build fails before the test suite even runs.


Deployment

Two Fly.io apps:

  • Dockerfile.mcp + fly.toml → Lumen MCP
  • Dockerfile.orchestrator + fly-orchestrator.toml → Lumen A2A orchestrator

docker-compose.yml boots both locally with the right network wiring. See DEMO.md for the demo invocation.


Where the bodies are buried

For honesty: these are the corners where the codebase has more friction than the pitch admits.

  • The DSL is incomplete. ≈260 theorems is enough for the five-guideline corpus and the demo patients, but real adoption needs another ~10× theorem count and more sophisticated temporal operators (Allen-style intervals beyond before/after/during).
  • Cohort PSM is not adversarially tested. If the workspace is small or skewed, the cohort prior can be wrong and the brief silently uses it. Frances Doyle's data_insufficient path is the only case that explicitly surfaces this; better detection is on the roadmap.
  • The adversary path can be jailbroken. The LLM is asked to attack the recommendation; if it refuses, the brief notes "adversary unhelpful" but the Z3 verdict still ships. Refusal does not invalidate the certificate — it just means we didn't get a free counter-test.
  • agent-config/ contains reference material from earlier iterations. Not used at runtime; preserved because the DSL evolved out of those rules and the lineage is useful to read.