Packages
allm_pipeline
0.1.0
A step-based LLM pipeline framework: typed steps with persistent step logs, artifact lineage, run lifecycle ownership, and a declarative pipeline DSL.
Current section
Files
Jump to
Current section
Files
allm_pipeline
README.md
README.md
# ALLM.Pipeline
A step-based LLM pipeline framework for Elixir: typed steps with persistent
step logs, artifact lineage (DynamoDB/S3-tiered), run lifecycle ownership, and
a declarative pipeline DSL (`use ALLM.Pipeline`).
Extracted from a production Elixir umbrella (Phases 1–8 of the ALLM pipeline
extraction plan), which is currently its sole consumer — it consumes this repo
as a path dependency. The framework is host-neutral and onboarding-ready: a new
host wires in through `use ALLM.Pipeline.Registry` following the
[host-wiring guide](guides/host_wiring.md). Publishable to Hex as `allm_pipeline`
via `scripts/release.exs` (see "Releasing to Hex"); not yet published.
## What's here
- `ALLM.Pipeline.Step` — the step behaviour: typed Input/Output structs
(`use ALLM.Pipeline.Schema`), executed with lineage via
`ALLM.Pipeline.Executor.run_step/5`.
- `ALLM.Pipeline.StepLog` / `PipelineRun` / `PipelineMetric` — persistent
step logs and run records (Ecto schemas; the **host** owns the repo and the
migrations — table names are contract).
- `ALLM.Pipeline.ArtifactStore` — artifact bodies, tiered across DynamoDB
(small) and S3 (large).
- `use ALLM.Pipeline` — the pipeline DSL: stages, fan-out, skips, lineage,
metrics, run ownership. See `ALLM.Pipeline`'s moduledoc.
- `use ALLM.Pipeline.LLMStep` — generated LLM call path (strict-mode JSON
schema derived from the Output declaration) on top of
[`allm`](https://hex.pm/packages/allm).
- `use ALLM.Pipeline.Registry` — how a host wires its repo and adapters in at
boot. The package resolves host collaborators at runtime; nothing in `lib/`
may name a host module.
Read `CLAUDE.md` before working in this repo.
## Test setup
The suite is self-contained: it brings its own `ALLM.Pipeline.TestRepo`, a
test-only migration (`priv/test_repo/migrations/`), and a test registry
(`test/support/test_registry.ex`). Requirements:
- Postgres on `localhost:5432` (override with `DATABASE_HOST` /
`DATABASE_USER` / `DATABASE_PASSWORD`). The `test` alias creates and
migrates `allm_pipeline_test` itself.
- **Optional:** DynamoDB Local on `localhost:4028` (`DYNAMODB_ENDPOINT`) and
MinIO on `localhost:4026` (`MEDIA_ENDPOINT`). When either is down the
affected tests are excluded with an operator message, not failed — the
two-direction check is:
```bash
mix test # stack up: no "Excluding tags" line
DYNAMODB_ENDPOINT=http://127.0.0.1:9 mix test # stack down: exclusions fire, still exit 0
```
Toolchain is pinned by `.tool-versions` (`erlang 27.1.2` /
`elixir 1.17.3-otp-27`, via asdf).
### Service stack (`docker-compose.yml`)
```bash
docker compose up -d # DynamoDB Local :4028 + MinIO :4026 (+ bucket)
docker compose --profile postgres up -d # …plus Postgres :5432 if the host has none
```
Same images and ports as the host umbrella's stack — run one or the other;
if the umbrella's is already up, this suite just uses it.
### Devcontainer
`.devcontainer/` gives the same toolchain in a container (erlang/elixir via
asdf at the `.tool-versions` pins, Claude Code, `docker-outside-of-docker`).
The service stack runs on the **host** daemon (`docker compose up -d` from
inside the container publishes on the host) and is reached back through
`host.docker.internal` — `containerEnv` presets `DATABASE_HOST`,
`DYNAMODB_ENDPOINT` and `MEDIA_ENDPOINT` accordingly, with
`DATABASE_USER=postgres` matching the compose `postgres` profile. Unlike the
umbrella's devcontainer, this repo is the workspace (read-write), so
`mix test` and `mix precommit` run inside it.
## Gates
```bash
mix precommit # compile --warnings-as-errors, format, test --warnings-as-errors
mix dialyzer # separate manual step, matching the host convention
mix docs # hexdocs preview in doc/
```
## Releasing to Hex
Same two-phase pattern as [`allm`](https://github.com/cykod/ALLM)'s
`scripts/release.exs`. The script never publishes or pushes itself.
```bash
# 0. Write the release notes first — the script requires a `## … vX.Y.Z` heading.
/changelog # (Claude Code skill) or edit CHANGELOG.md by hand
# Phase A — every gate, then bump mix.exs:@version (no commit)
mix run scripts/release.exs patch # or minor | major | 0.2.0-rc.1
mix run scripts/release.exs patch --dry-run # gates only, no mutations
# Publish by hand so Hex's prompts / OAuth device flow get a real terminal
mix hex.publish
# Phase B — commit mix.exs + CHANGELOG.md, annotated tag vX.Y.Z (no push)
mix run scripts/release.exs --finalize
git push origin main vX.Y.Z
```
Gates run by Phase A: `deps.get`, `compile --warnings-as-errors`,
`format --check-formatted`, `test --warnings-as-errors`, `dialyzer`
(`--skip-dialyzer` to skip), `hex.build`. It warns — does not fail — when the
test run excluded the `:dynamo` tags (stack down) or when
`priv/test_repo/migrations/` changed since the last tag (re-run the host
schema-parity check first). Hex auth is `~/.hex/hex.config` per maintainer
(`mix hex.user auth` on a browser-capable machine; in the devcontainer copy
the file in or set `HEX_API_KEY`). Hotfix runbook and co-maintainer
onboarding are in the script's header.
Publishing does not change the host umbrella, which consumes this repo as a
path dep until it opts into `{:allm_pipeline, "~> X.Y"}`.
## Host consumption
A host wires the framework at runtime through `use ALLM.Pipeline.Registry` —
the repo, the seam adapters, and the table DDL are the host's to supply. **New
consumers: start with the [host-wiring guide](guides/host_wiring.md)**, which
walks through the registry declaration, the optional `llm:` seam, adopting the
production DDL, provisioning the artifact backends, and the test-suite pattern.
### The path-dep umbrella
The first consumer, an internal umbrella, consumes this repo as
`{:allm_pipeline, path: ...}` — sibling checkout at `~/Projects/ALLM.Pipeline`
on the host, readonly bind mount at `/workspaces/ALLM.Pipeline` in its
devcontainer (the mount appears only after a container rebuild — see the
umbrella's `CLAUDE.md` on devcontainer declarations), and a vendored copy
staged by its `scripts/deploy.sh` for production Docker builds. Inside that
devcontainer this suite is **not runnable** (readonly mount — `_build` can't be
written); run it on the host.