Skip to main content

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)

Metadata

Release files for alt-text-llm 1.6.14

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for alt-text-llm 1.6.14
File Size Uploaded Provenance
alt_text_llm-1.6.14.tar.gz 85.4 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for alt-text-llm 1.6.14
File Interpreter ABI Platform
alt_text_llm-1.6.14-py3-none-any.whl Python 3 none any Details

Total release size: 125.8 kB

Release files / alt_text_llm-1.6.14.tar.gz

Download URL alt_text_llm-1.6.14.tar.gz
Size 85.4 kB
Tags Source
SHA-256 checksum
How to use checksums
0bafce29a2c9dd79b4d83da889ca771a63f81ba674694325b9bf48ca8a1528c8
BLAKE2b-256 checksum
How to use checksums
79bc77dd83e36c4136cc239b4b1669083e8789525c5216b92140e07a8e28809c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 4, 2026.

Transparency log

Release files / alt_text_llm-1.6.14-py3-none-any.whl

Download URL alt_text_llm-1.6.14-py3-none-any.whl
Size 40.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6076e7eb3fba0f34e2b99b31fda7b06b1ce8668ee043983d0b4009b575767d18
BLAKE2b-256 checksum
How to use checksums
996c73d4becc692470241ab467e6dd48a4a711b8cb8937a105d8b56e45004aa6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 4, 2026.

Transparency log

Security for alt-text-llm 1.6.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for alt_text_llm-1.6.14.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 4, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page