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.3.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.3-py3-none-any.whl (40.4 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for alt_text_llm-1.6.3.tar.gz
Algorithm Hash digest
SHA256 1eb9d156dbd1e3112d90ec8ae8f4ec771715fe597337c8f75248cbd1e146c541
MD5 b5cc05a709e82ef24cc3ce9b8690d850
BLAKE2b-256 0bbc2d41a4ffbcad3c96cb28e6f71f44279e046f5e400c0713fe7fd37789ebcc

See more details on using hashes here.

Provenance

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

Publisher: publish.yml on alexander-turner/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.3-py3-none-any.whl.

File metadata

  • Download URL: alt_text_llm-1.6.3-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.12

File hashes

Hashes for alt_text_llm-1.6.3-py3-none-any.whl
Algorithm Hash digest
SHA256 379675d2b37f36e5c4d704a7030116ad727073079184a972c0508312b4621b36
MD5 967f02b869f863bd080bfbd7b03423c7
BLAKE2b-256 60e5d4d3176cdae3aa57bdd210f8d5938dc16677993394c208f080331edd272a

See more details on using hashes here.

Provenance

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

Publisher: publish.yml on alexander-turner/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