Skip to main content

AI-powered accessibility text generation and labeling tools for markdown content

Project description

alt-text-llm

AI-powered alt text generation and labeling tools for markdown content. Originally developed for my website (repo).

Features

  • Intelligent scanning - Detects images/videos missing meaningful alt text (ignores empty alt="")
  • AI-powered generation - Uses LLM of your choice to create context-aware alt text suggestions
  • Interactive labeling - Manually review and edit LLM suggestions. Images (and inline video previews) display directly in your terminal
  • Automatic application - Apply approved captions back to your markdown files

A labeled example of the labeling pipeline: 1) view the context for an image, 2) view the image itself, while 3) editing the AI-generated label suggestion.

Installation

From PyPI

pip install alt-text-llm

Automated setup (includes system dependencies)

git clone https://github.com/alexander-turner/alt-text-llm.git
cd alt-text-llm
./setup.sh

Prerequisites

macOS:

brew install imagemagick ffmpeg imgcat

Linux:

sudo apt-get install imagemagick ffmpeg
# imgcat: curl -sL https://iterm2.com/utilities/imgcat -o ~/.local/bin/imgcat && chmod +x ~/.local/bin/imgcat

Alt text is generated through OpenRouter. Get an API key at https://openrouter.ai/keys and export it before running generate:

export OPENROUTER_API_KEY=sk-or-...

Usage

The tool provides four main commands: scan, generate, label, and apply.

1. Scan for missing alt text

Scan your markdown files to find images without meaningful alt text:

alt-text-llm scan --root /path/to/markdown/files

This creates asset_queue.json with all assets needing alt text. --root defaults to the current directory.

2. Generate AI suggestions

Generate alt text suggestions using an LLM:

alt-text-llm generate \
  --root /path/to/markdown/files \
  --model google/gemini-3.1-flash-lite \
  --suggestions-file suggested_alts.json

Available options:

  • --model - OpenRouter model id of the form provider/model-slug (default: google/gemini-3.1-flash-lite, a cheap, current, video-capable model ~6x cheaper than gemini-2.5-pro). Other options: google/gemini-3-flash-preview (higher quality), google/gemini-2.5-pro (highest quality), anthropic/claude-sonnet-4.5 (strong vision, no video). Pass the full provider/slug form — a bare slug yields "Unknown model". Browse all available ids at https://openrouter.ai/models.
  • --root - Markdown root directory (default: current directory)
  • --max-chars - Maximum characters for alt text (default: 300)
  • --timeout - LLM timeout in seconds (default: 120)
  • --captions - Existing/final captions JSON path used to skip already-captioned assets (default: asset_captions.json)
  • --suggestions-file - Path to read/write suggestions JSON (default: suggested_alts.json)
  • --estimate-only - Only show cost estimate without generating
  • --process-existing - Also process assets that already have captions

Note: Video alt text generation requires a model that accepts video input (the Google Gemini models).

Cost estimation:

Cost estimates are pulled live from OpenRouter's model catalogue (https://openrouter.ai/api/v1/models), so --estimate-only works for any model OpenRouter prices. After a real run, the tool also prints the actual total cost reported by OpenRouter per request.

alt-text-llm generate \
  --root /path/to/markdown/files \
  --model google/gemini-3.1-flash-lite \
  --estimate-only

3. Label and approve suggestions

Interactively review and approve the AI-generated suggestions:

alt-text-llm label \
  --suggestions-file suggested_alts.json \
  --output asset_captions.json

Interactive commands:

  • Edit the suggested alt text (pass --vi-mode for vim keybindings)
  • Press Enter to accept the suggestion as-is
  • Submit undo or u to go back to the previous item
  • Images display in your terminal (requires imgcat)
  • Videos (.mp4, .webm, etc.) preview inline as a short animated clip (requires ffmpeg and imgcat). When an inline preview isn't possible (missing tools, tmux, or an unsupported terminal), the video opens in your default application without stealing focus from the terminal, so you can keep editing
  • The next asset is downloaded and its preview built in the background while you label the current one, so advancing is usually instant; a spinner shows progress whenever a download or conversion isn't ready yet
  • Pass --no-skip-existing to relabel assets already present in the output file

4. Apply approved captions

Apply the approved captions back to your markdown files:

alt-text-llm apply \
  --captions-file asset_captions.json

Available options:

  • --captions-file - Path to the captions JSON file with final_alt populated (default: asset_captions.json)
  • --dry-run - Preview changes without modifying files

What it does:

  • Reads approved captions from the captions file
  • Locates corresponding images/videos in markdown files
  • Updates alt text for all supported formats:
    • Markdown images: ![alt](path)
    • HTML img tags: <img src="path" alt="alt">
    • Wikilink images: ![[path|alt]]
  • Preserves file formatting and handles special characters

Example workflow

# 1. Scan markdown files for missing alt text
alt-text-llm scan --root ./content

# 2. Estimate the cost
alt-text-llm generate \
  --root ./content \
  --model google/gemini-3.1-flash-lite \
  --estimate-only

# 3. Generate suggestions (if cost is acceptable)
alt-text-llm generate \
  --root ./content \
  --model google/gemini-3.1-flash-lite

# 4. Review and approve suggestions
alt-text-llm label

# 5. Apply approved captions to markdown files
alt-text-llm apply

Configuration

LLM Integration

This tool calls OpenRouter directly over HTTPS to generate alt text, which provides access to many models from many providers through a single API key.

Setting up your model

  1. Create an OpenRouter API key at https://openrouter.ai/keys.

  2. Export it in your shell:

    export OPENROUTER_API_KEY=sk-or-...
    
  3. Pass an OpenRouter model id of the form provider/model-slug to --model, for example:

    • google/gemini-3.1-flash-lite (default — cheap, current, video-capable)
    • google/gemini-3-flash-preview (higher quality, still cheap)
    • google/gemini-2.5-pro (highest quality, most expensive)
    • anthropic/claude-sonnet-4.5 (strong vision, but no video support)

Browse the full catalogue of available model ids at https://openrouter.ai/models. Note that only video-capable models (the Google Gemini models) can generate alt text for videos.

Shell completion

Tab-completion is provided through argcomplete. Completing the --model flag suggests live OpenRouter model ids (this needs network access; the public model list does not require an API key).

Enable it globally for bash (one-time, then restart your shell):

activate-global-python-argcomplete

Or enable it per-shell.

bash:

eval "$(register-python-argcomplete alt-text-llm)"

zsh:

autoload -U bashcompinit && bashcompinit
eval "$(register-python-argcomplete alt-text-llm)"

For zsh's native completion system you can instead use register-python-argcomplete --shell zsh alt-text-llm.

Output files

  • asset_queue.json - Queue of assets needing alt text (from scan)
  • suggested_alts.json - AI-generated suggestions (from generate)
  • asset_captions.json - Approved final captions (from label)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

alt_text_llm-1.6.7.tar.gz (85.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

alt_text_llm-1.6.7-py3-none-any.whl (40.4 kB view details)

Uploaded Python 3

File details

Details for the file alt_text_llm-1.6.7.tar.gz.

File metadata

  • Download URL: alt_text_llm-1.6.7.tar.gz
  • Upload date:
  • Size: 85.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for alt_text_llm-1.6.7.tar.gz
Algorithm Hash digest
SHA256 ce2f1f239c519bb7c435f14a78f7ec5a62e3d9c9c6ee3a76fc66ba1b46e030d6
MD5 562a1b4c3f174ce6a9decdcd6bec4b4a
BLAKE2b-256 6574900f74a4eb0d3258db0344e8dc571c6eda7ba6246678d737c34aa9b474f9

See more details on using hashes here.

Provenance

The following attestation bundles were made for alt_text_llm-1.6.7.tar.gz:

Publisher: publish.yml on AlexanderMattTurner/alt-text-llm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file alt_text_llm-1.6.7-py3-none-any.whl.

File metadata

  • Download URL: alt_text_llm-1.6.7-py3-none-any.whl
  • Upload date:
  • Size: 40.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for alt_text_llm-1.6.7-py3-none-any.whl
Algorithm Hash digest
SHA256 bdb26858a20df53a3991d24206af4e7560cfa24ac9b6dc523ea8a0df2edcaf30
MD5 3d1e05c1b92a942598fd3d79d42850ce
BLAKE2b-256 b07e468b4359975150cf7a8085a7062fcf2b01f99da731a8de414947ab421a52

See more details on using hashes here.

Provenance

The following attestation bundles were made for alt_text_llm-1.6.7-py3-none-any.whl:

Publisher: publish.yml on AlexanderMattTurner/alt-text-llm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page