๐Ÿ“ฆ EqualifyEverything / equalify-iris

Equalify Iris

โ˜… 2 stars โ‘‚ 2 forks ๐Ÿ‘ 2 watching โš–๏ธ GNU Affero General Public License v3.0
๐Ÿ“ฅ Clone https://github.com/EqualifyEverything/equalify-iris.git
HTTPS git clone https://github.com/EqualifyEverything/equalify-iris.git
SSH git clone git@github.com:EqualifyEverything/equalify-iris.git
CLI gh repo clone EqualifyEverything/equalify-iris
Blake Bertuccelli-Booth Blake Bertuccelli-Booth a11y(demo): make the device-code sign-in screen-reader friendly ca51c48 23 days ago ๐Ÿ“ History
๐Ÿ“‚ ca51c48180062023f96d09e8dda74627d80094c4 View all commits โ†’
๐Ÿ“ agents
๐Ÿ“ docs
๐Ÿ“ public
๐Ÿ“ src
๐Ÿ“ test
๐Ÿ“„ .dockerignore
๐Ÿ“„ .env.example
๐Ÿ“„ .gitignore
๐Ÿ“„ docker-compose.yml
๐Ÿ“„ Dockerfile
๐Ÿ“„ package-lock.json
๐Ÿ“„ package.json
๐Ÿ“„ prd.md
๐Ÿ“„ README.md
๐Ÿ“„ tsconfig.json
๐Ÿ“„ README.md

Equalify Iris

Image-to-Accessible-HTML parsing service. Iris converts a sequential set of image files (e.g. the rendered pages of a PDF) into a single content-only, WCAG 2.2 AA accessible HTML document, using specialized per-content-type agents, a self-extending builder, and an iterative reader/copy-editor review loop.

## Sustainability
> Equalify Iris is Open Source. Sustainability is key to sustaining its growth. With that
in mind, we hope you use and alter the codebase.
> Iris is built by Equalify Inc (https://equalify.app/). Continued
support and development are paid for when you hire us to host or support any instance. Please
consider hiring us.

How it works

The pipeline runs in five phases (PRD ยง6):

  • Triage โ€” the Image Analysis Agent analyzes each image and writes a notes file listing
content types, fragment edges, and which content agents to call.
  • Extraction โ€” the listed content agents convert their regions to accessible HTML
fragments. If no agent exists for a content type, the Builder Agent drafts one for the session.
  • Reconciliation โ€” fragments cut off at image boundaries are conservatively stitched.
  • Assembly โ€” fragments are combined into one accessible document shell, provenance
comments preserved, and validated with axe-core.
  • Review โ€” the Reader flags reading-order / semantic / accessibility issues; the Copy
Editor proposes fixes against the source image; the Assembler applies them. Loops up to max_review_iterations (default 3).

Agents built during a session are ephemeral. The only way one becomes permanent is the GitHub PR workflow on session close: Iris opens a PR upstream, a maintainer merges it, and the user pulls the updated agents/ library (PRD ยง7.13).

Quick start

Requires Node.js 24+ (the service runs TypeScript directly via Node's built-in type stripping and uses the built-in node:sqlite), and a git checkout of the agent library (this repo's agents/ directory works).

git clone https://github.com/EqualifyEverything/equalify-iris
cd equalify-iris
npm install

cp .env.example .env          # fill in GitHub OAuth + a model provider key
cp config.example.yaml config.yaml

# load env and run
set -a; source .env; set +a
npm start                     # -> http://localhost:8080

Or with Docker (multi-arch; Mac Mini / Linux ARM are first-class targets):

cp .env.example .env          # fill in values
docker compose up

Check it's alive:

curl http://localhost:8080/v1/health

Or just open the accessible browser demo for a no-API walkthrough (sign in with GitHub โ†’ upload page images โ†’ convert โ†’ preview/download the accessible HTML):

http://localhost:8080/demo

Configuration

Deployment is configured in config.yaml (PRD ยง10.3). ${ENV_VAR} references are expanded from the environment at startup; changes require a restart.

  • Storage (ยง10.2): local filesystem + a single SQLite file by default. agents/ is a git
checkout modified only by git pull from upstream.
  • Model providers (ยง10.3): each agent declares a capability (vision,
structured_output, text); the deployment maps capabilities to a provider + concrete model. v1 ships OpenRouter and Amazon Bedrock adapters. Adding a provider is a small adapter implementing the ModelProvider interface in src/providers/types.ts. Models are set per provider (default_model + per_capability), and can be overridden per agent via providers.per_agent โ€” either a string (provider only) or { provider, model }. Resolution falls back: per-agent model โ†’ provider per_capability โ†’ provider default_model.
  • GitHub (ยง9.1): OAuth is the only auth mechanism. The token that authenticates a request
is the same token used to open PRs on /close, so repo scope is required. By default the service uses a bundled OAuth App via the device flow โ€” no per-operator app setup, no secret (the same approach the gh CLI uses). Set github.client_id only to point at your own OAuth App; client_secret is needed only if you enable the web redirect flow.

API

All endpoints are under /v1 and (except auth and health) require Authorization: Bearer <github_token>.

Method & pathPurpose
GET /v1/healthLiveness probe
GET /v1/auth/github/startBegin OAuth (web clients)
GET /v1/auth/github/callbackOAuth callback โ†’ returns access token
POST /v1/auth/github/deviceBegin device flow (CLI clients)
POST /v1/auth/github/device/pollPoll device flow (send { "device_code": ... })
GET /v1/meCurrent GitHub user + config
GET /v1/sessionsList the caller's sessions
POST /v1/sessionsCreate a session, upload images (multipart/form-data)
GET /v1/sessions/{id}Poll status; preview pending PRs when ready
GET /v1/sessions/{id}/outputFetch the HTML when ready
POST /v1/sessions/{id}/feedbackSubmit feedback, trigger a re-run
POST /v1/sessions/{id}/closeAccept output, open PRs, clean tmp (?skip_prs=true to skip)
GET /v1/sessions/{id}/logsFetch the run log (ndjson)
Full copy-pasteable bash/curl walkthrough of every endpoint: docs/API.md. To prove the endpoints work end-to-end (mock GitHub + mock model, no credentials needed): ./test/e2e.sh.

Example โ€” create a session (order of images parts is the processing order, ยง9.2):

curl -X POST http://localhost:8080/v1/sessions \
  -H "Authorization: Bearer $TOKEN" \
  -F "images=@page-001.png" \
  -F "images=@page-002.png" \
  -F 'config={"max_review_iterations": 3}'

Then poll GET /v1/sessions/{id} until status is ready_for_review, fetch GET /v1/sessions/{id}/output, and POST /v1/sessions/{id}/close to finalize.

Layout

agents/                  # the agent library (git checkout; v1 content agents)
src/
  config.ts              # config loader (${ENV} expansion)
  providers/             # ModelProvider interface + openrouter & bedrock adapters
  agents/loader.ts       # loads agent .md files, pins git SHA (ยง7.3)
  pipeline/              # triage, extraction, builder, reconciliation, assembly, review
  auth/                  # GitHub OAuth + device flow + bearer middleware
  github/                # fork + PR contribution workflow (ยง7.13)
  store/                 # node:sqlite metadata store + on-disk session layout (ยง8.1)
  routes/                # /v1 endpoints
  index.ts               # server entry point
data/                    # sessions/, tmp/, and the SQLite DB (created at runtime)

Implementation notes & PRD coverage

A few places where the PRD left a decision open, and where v1 intentionally stops:

  • runs/<run-id> vs sessions/<session-id>. The PRD references both (ยง7.3/ยง7.5 vs ยง8.1).
This implementation treats the run id as the session id and writes the log, new-agents.md, etc. under sessions/<session-id>/, matching the authoritative layout in ยง8.1.
  • Reader chunking (ยง7.8). Chunks use a fixed character budget with overlap rather than a
literal 30%-of-context computation, since the per-model context window is not exposed through the provider abstraction. The two-view (HTML + flattened) cross-check is implemented as specified.
  • Color-contrast lint. Output is content-only with no styling (ยง4), so axe-core's
color-contrast rule is disabled โ€” it cannot be assessed without rendering and is out of scope.
  • Feedback re-runs (ยง7.12). Re-runs are logged separately (a feedback_rerun event) and the
prior output.html is snapshotted to sessions/<id>/history/ so it can be reverted to. A revert endpoint is out of v1 API scope (not in ยง9); the data is preserved to enable it.
  • PR contents (ยง7.13). The PRD calls for committing test fixtures (input image, produced
output, lint pass) alongside the agent. We deviate to keep the agent library code-only: a new-agent PR commits only the agent file, and puts the produced sample output (in a collapsible block) and the axe-core lint result in the PR description instead. The produced HTML isn't a deterministic regression artifact anyway, so the description serves the reviewer without cluttering the tree with per-agent fixture directories.

Intentionally not built in v1 (the PRD frames each as optional / alternative / out of scope): PostgreSQL and S3 backends (ยง10.2 โ€” "supported alternative," SQLite + local FS is the v1 reference), the per-user config endpoint (ยง9.1 โ€” "not specified in v1"), automated detection of agent-updates (ยง7.13 names no producer; the close flow opens update PRs if a agent-updates.md JSON file is present), and webhooks (ยง9.4 โ€” out of scope, API structured to add them later). The only endpoint beyond the PRD is GET /v1/health, a standard liveness probe.

License

MIT. See the Sustainability notice above โ€” and please consider hiring Equalify to host or support your instance.