Skip to main content

CLI tool to send prompts to an AI API and save JSON/Markdown outputs.

Project description

ai-prompt-runner

CI PyPI version Python License

AI Prompt Runner is a Python CLI designed to execute prompts across multiple AI providers and generate deterministic structured outputs (JSON and Markdown).

It acts as a stateless execution layer for prompt automation in scripts, CI pipelines, and reproducible AI workflows.

Quick Start

pipx install ai-prompt-runner
export AI_API_KEY="your_api_key"
ai-prompt-runner --provider openai --prompt "Explain retry logic"

By default, generated outputs are written to:

  • outputs/response.json
  • outputs/response.md

The outputs/ directory is created relative to the current working directory.

Design Philosophy

ai-prompt-runner is intentionally designed as a stateless execution layer:

1 input โ†’ 1 API call โ†’ 1 structured output

It does not manage conversational state, orchestration, agents, or multi-step workflows.

The project focuses on:

  • deterministic execution
  • explicit configuration precedence
  • contract-driven provider abstraction
  • strict failure-path handling
  • reproducible CI validation

Provider implementations must conform to a shared contract validated through reusable contract tests.

An official MockProvider is included to guarantee deterministic behavior and network-independent validation.

Public Roadmap & Engineering Approach

This project includes a detailed public roadmap and documentation of its structured AI-assisted development approach.

Explore the full project overview, roadmap and methodology here:

๐Ÿ”— AI Prompt Runner โ€“ Project Page

Table of Contents

Requirements

  • Python 3.11+
  • Virtual environment recommended

Installation

Recommended for CLI usage with pipx:

pipx install ai-prompt-runner

Verify the installed command:

ai-prompt-runner --version

Install from PyPI with pip (fallback):

python3 -m pip install ai-prompt-runner

If the installed command is not found, your Python script directory may not be on PATH. In that case, prefer pipx for CLI usage or run the module directly:

python3 -m ai_prompt_runner.cli --version

Install from source for development:

python3 -m venv .venv
source .venv/bin/activate
python3 -m pip install -e ".[dev]"

Docker Workflow

Docker support is provided for reproducible runtime execution and development validation.

Build runtime image:

docker build --target runtime -t ai-prompt-runner:runtime .

Run CLI version check in container:

docker run --rm ai-prompt-runner:runtime --version

Run a prompt using environment variables from a local .env file:

docker run --rm \
  --env-file .env \
  -v "$(pwd)/outputs:/app/outputs" \
  ai-prompt-runner:runtime \
  --provider openai \
  --prompt "Explain retry logic"

Build development image:

docker build --target dev -t ai-prompt-runner:dev .

Run tests in the dev container:

docker run --rm ai-prompt-runner:dev python3 -m pytest

Run lint checks in the dev container:

docker run --rm ai-prompt-runner:dev ruff check .

uv Workflow

uv is supported as the modern dependency and task runner workflow for local development.

Sync the project with development dependencies:

uv sync --extra dev

Run the test suite:

uv run pytest

Run tests with the CI coverage gate:

uv run pytest --cov=src --cov-report=term-missing --cov-fail-under=95

Run lint checks:

uv run ruff check .

Build package distributions:

uv run python3 -m build

pip-based commands remain supported, but uv is the recommended workflow for local development.

Environment Variables

Create a local .env file (or use CLI flags directly):

AI_API_ENDPOINT=
AI_API_KEY=
AI_API_MODEL=default

You can start from .env.example.

Configuration File (Optional)

You can provide a TOML config file with --config for non-sensitive runtime defaults.

Example (config.toml):

[ai_prompt_runner]
provider = "http"
api_endpoint = "http://localhost:11434/api/generate"
api_model = "llama3.2"
temperature = 0.2
max_tokens = 512
top_p = 0.9
timeout = 30
retries = 0
out_json = "outputs/response.json"
out_md = "outputs/response.md"
log_run_dir = "logs"

You can start from config.example.toml and copy it to a local config.toml (do not store secrets in this file)

Configuration precedence is:

CLI > environment variables (.env / shell env) > TOML config > built-in defaults

Security note:

  • api_key is intentionally not supported in the TOML config file.
  • Use AI_API_KEY (recommended) or --api-key for secrets.

Runtime controls:

  • temperature, max_tokens, and top_p are optional and provider-forwarded.
  • If omitted, provider defaults are used.

CLI Usage

Use either command style:

  • Installed console script: ai-prompt-runner ...
  • Module fallback: python3 -m ai_prompt_runner.cli ...

Show help:

ai-prompt-runner --help
python3 -m ai_prompt_runner.cli --help

Run with .env values:

ai-prompt-runner --prompt "Hello world"
python3 -m ai_prompt_runner.cli --prompt "Hello world"

Run with an optional TOML config file:

ai-prompt-runner --config config.toml --prompt "Hello world"
python3 -m ai_prompt_runner.cli --config config.toml --prompt "Hello world"

CLI flags and environment variables override values from the config file.

Library Usage

ai-prompt-runner is CLI-first, but also exposes a minimal Python API for one-shot execution.

from ai_prompt_runner import run_prompt

payload = run_prompt(
    prompt="Explain retry logic",
    provider="openai",
    api_key="your_api_key",
)

print(payload["response"])

Library mode returns a normalized payload dictionary and does not write output files automatically.

Supported Providers

The provider factory is protocol-first and registry-driven.

OpenAI-compatible protocol providers (same runtime class, different defaults/aliases):

  • openai_compatible
  • openai
  • openrouter
  • groq
  • together
  • fireworks
  • perplexity
  • inception
  • x
  • xai
  • lmstudio
  • ollama

Other protocol providers:

  • anthropic
  • google
  • http (legacy generic JSON-over-HTTP provider)

Run with explicit API values:

ai-prompt-runner \
  --prompt "Hello world" \
  --api-endpoint "http://localhost:11434/api/generate" \
  --api-key "dummy" \
  --api-model "llama3.2"

python3 -m ai_prompt_runner.cli \
  --prompt "Hello world" \
  --api-endpoint "http://localhost:11434/api/generate" \
  --api-key "dummy" \
  --api-model "llama3.2"

Run with Anthropic defaults:

ai-prompt-runner \
  --provider anthropic \
  --api-key "$AI_API_KEY" \
  --prompt "Explain retry logic"

Run with OpenAI defaults:

ai-prompt-runner \
  --provider openai \
  --api-key "$AI_API_KEY" \
  --prompt "Explain configuration precedence"

Run with Google defaults:

ai-prompt-runner \
  --provider google \
  --api-key "$AI_API_KEY" \
  --prompt "Summarize this architecture"

Run with xAI defaults:

ai-prompt-runner \
  --provider xai \
  --api-key "$AI_API_KEY" \
  --prompt "Generate a concise changelog entry"

Run locally with Ollama defaults:

ai-prompt-runner \
  --provider ollama \
  --api-key "dummy" \
  --prompt "Hello from local Ollama"

System Prompt (--system)

Use --system to pass optional one-shot instruction context together with --prompt.

Example:

ai-prompt-runner \
  --provider openai \
  --api-key "$AI_API_KEY" \
  --system "You are a strict API architect." \
  --prompt "Explain retry logic in 5 bullets"

Behavior by provider type:

  • role-aware protocol providers (openai_compatible aliases, anthropic, google) map --system to native system fields
  • non role-aware providers (http, mock) use deterministic prompt composition:
    • SYSTEM: ...
    • USER: ...

--system does not introduce conversation state; execution remains stateless and single-shot.

Streaming (--stream)

Use --stream to print response chunks progressively when the selected provider supports streaming.

Streaming-capable providers:

  • openai_compatible and all OpenAI-compatible aliases (openai, openrouter, groq, together, fireworks, perplexity, inception, x, xai, lmstudio, ollama)
  • anthropic
  • google
  • mock (test-only deterministic stream)

Non-stream provider behavior:

  • http falls back to non-stream execution even if --stream is set.

Example:

ai-prompt-runner \
  --provider openai \
  --api-key "$AI_API_KEY" \
  --stream \
  --prompt "Explain retry strategy"

--stream changes console UX only. Final JSON and Markdown outputs still contain the complete final response payload.

Runtime Controls

Use optional runtime controls to tune generation behavior per execution.

Available flags:

  • --temperature (float >= 0)
  • --max-tokens (integer > 0)
  • --top-p (float > 0 and <= 1)

Example:

ai-prompt-runner \
  --provider openai \
  --api-key "$AI_API_KEY" \
  --temperature 0.2 \
  --max-tokens 512 \
  --top-p 0.9 \
  --prompt "Explain exponential backoff in one paragraph"

These controls are passed to protocol providers and mapped to provider-native request fields.

Safety Modes

Use safety/diagnostic flags to validate execution intent before runtime:

  • --strict-capabilities: fail when requested options are unsupported or unknown for the selected provider.
  • --dry-run: validate resolved config and capability checks, then exit without generation.
  • --print-effective-config: print resolved runtime configuration (with masked API key).

Capability states are registry-driven per provider:

  • supported
  • unsupported
  • unknown

Default mode is permissive:

  • capability mismatches produce warnings
  • execution continues (provider fallback or provider-side ignore may happen)

With --strict-capabilities, capability mismatches are treated as hard runtime errors.

Current capability matrix fields:

  • stream
  • system
  • usage
  • temperature
  • top_p
  • max_tokens

Example strict capability check:

ai-prompt-runner \
  --provider http \
  --temperature 0.2 \
  --strict-capabilities \
  --prompt "Hello"

Example dry-run preflight:

ai-prompt-runner \
  --provider openai \
  --api-key "$AI_API_KEY" \
  --dry-run \
  --print-effective-config

--dry-run does not call provider generation and does not write JSON/Markdown artifacts. In dry-run mode, prompt input is optional because execution is preflight-only.

Execution Logs (--log-run-dir)

Use --log-run-dir to persist deterministic per-run diagnostics without changing the final output contract.

Behavior:

  • creates one run directory per execution: run-YYYYmmddTHHMMSSffffffZ
  • writes sanitized request.json
  • writes response.json on success
  • writes error.json on runtime failure
  • never persists raw API keys

Example:

ai-prompt-runner \
  --provider openai \
  --api-key "$AI_API_KEY" \
  --log-run-dir logs \
  --prompt "Explain retry strategy"

Directory shape:

logs/
โ””โ”€โ”€ run-20260315T101530123456Z/
    โ”œโ”€โ”€ request.json
    โ””โ”€โ”€ response.json

On failure, response.json is replaced by error.json.

Structured Runtime Errors

Runtime failures are normalized to stable taxonomy codes:

  • auth_error
  • rate_limit
  • timeout
  • invalid_request
  • network_error
  • provider_error

These codes are used for diagnostics payloads (including error.json when --log-run-dir is enabled). Exit code behavior remains unchanged:

  • 0: success
  • 1: runtime error
  • 2: usage/validation error

Execution Metadata

Every successful JSON output includes stable execution metadata:

  • metadata.provider
  • metadata.timestamp_utc
  • metadata.execution_ms (integer, non-negative)
  • metadata.model (resolved model when available, otherwise requested model)
  • metadata.execution_context (execution provenance snapshot)

metadata.execution_context includes:

  • provider_protocol
  • api_endpoint
  • model_requested
  • model_resolved
  • runner_version
  • prompt_hash (sha256:<hex>)
  • runtime snapshot (stream, system_prompt_provided, runtime controls, timeout, retries)

Providers that expose usage counters also include optional:

  • metadata.usage.prompt_tokens
  • metadata.usage.completion_tokens
  • metadata.usage.total_tokens

metadata.usage remains optional and appears only when upstream provider usage is available.

Custom output paths:

ai-prompt-runner \
  --prompt "Hello world" \
  --out-json outputs/my_response.json \
  --out-md outputs/my_response.md

python3 -m ai_prompt_runner.cli \
  --prompt "Hello world" \
  --out-json outputs/my_response.json \
  --out-md outputs/my_response.md

Project Structure

Root/
โ”‚
โ”œโ”€โ”€ src/
โ”‚   โ””โ”€โ”€ ai_prompt_runner/
โ”‚       โ”œโ”€โ”€ api.py
โ”‚       โ”œโ”€โ”€ cli.py
โ”‚       โ”œโ”€โ”€ core/
โ”‚       โ”‚   โ”œโ”€โ”€ errors.py
โ”‚       โ”‚   โ”œโ”€โ”€ error_taxonomy.py
โ”‚       โ”‚   โ”œโ”€โ”€ models.py
โ”‚       โ”‚   โ”œโ”€โ”€ runner.py
โ”‚       โ”‚   โ””โ”€โ”€ validators.py
โ”‚       โ”œโ”€โ”€ services/
โ”‚       โ”‚   โ”œโ”€โ”€ anthropic_provider.py
โ”‚       โ”‚   โ”œโ”€โ”€ base.py
โ”‚       โ”‚   โ”œโ”€โ”€ google_provider.py
โ”‚       โ”‚   โ”œโ”€โ”€ http_provider.py
โ”‚       โ”‚   โ”œโ”€โ”€ mock_provider.py
โ”‚       โ”‚   โ”œโ”€โ”€ openai_compatible_provider.py
โ”‚       โ”‚   โ””โ”€โ”€ provider_factory.py
โ”‚       โ””โ”€โ”€ utils/
โ”‚           โ””โ”€โ”€ file_io.py
โ”‚
โ”œโ”€โ”€ schemas/
โ”‚   โ””โ”€โ”€ response.schema.json
โ”‚
โ”œโ”€โ”€ docs/
โ”‚   โ”œโ”€โ”€ architecture.md
โ”‚   โ”œโ”€โ”€ cli-reference.md
โ”‚   โ”œโ”€โ”€ configuration.md
โ”‚   โ”œโ”€โ”€ migration.md
โ”‚   โ”œโ”€โ”€ ops-runbook.md
โ”‚   โ”œโ”€โ”€ output-contract.md
โ”‚   โ”œโ”€โ”€ release-checklist.md
โ”‚   โ””โ”€โ”€ testing.md
โ”‚
โ”œโ”€โ”€ prompts/
โ”‚
โ”œโ”€โ”€ tests/
โ”‚   โ”œโ”€โ”€ fixtures/
โ”‚   โ”œโ”€โ”€ unit/
โ”‚   โ””โ”€โ”€ e2e/
โ”‚
โ”œโ”€โ”€ .github/workflows/ci.yml
โ”œโ”€โ”€ requirements.txt
โ”œโ”€โ”€ pyproject.toml
โ”œโ”€โ”€ uv.lock
โ”œโ”€โ”€ README.md
โ””โ”€โ”€ AGENT.md

Architecture Principles

Prompt Input
   โ†“
ai-prompt-runner (CLI)
   โ†“
Provider API Call
   โ†“
Structured Output (JSON + Markdown)
  • src/ai_prompt_runner/cli.py: argument parsing and process-level I/O only.
  • src/ai_prompt_runner/api.py: public Python facade (run_prompt) for one-shot library usage.
  • src/ai_prompt_runner/core/: business rules and payload validation.
  • src/ai_prompt_runner/services/: external integrations (AI provider implementations).
  • Provider layer follows an explicit contract enforced by reusable contract tests.
  • MockProvider ensures deterministic behavior and decouples validation from network dependencies.
  • src/ai_prompt_runner/utils/: file persistence helpers.

Technical Docs

Additional versioned technical documentation is available under docs/:

Output Contract

The normalized JSON response is treated as a stable contract and is formally defined by schemas/response.schema.json.

Detailed contract documentation is available in docs/output-contract.md.

Output Examples

Generated JSON (outputs/response.json):

{
  "prompt": "Explain retry strategy",
  "response": "Retry strategy improves resilience by handling transient failures with controlled backoff.",
  "metadata": {
    "provider": "openai",
    "timestamp_utc": "2026-03-15T10:21:11.236575+00:00",
    "execution_ms": 412,
    "model": "gpt-4o-mini",
    "execution_context": {
      "provider_protocol": "openai-compatible",
      "api_endpoint": "https://api.openai.com/v1",
      "model_requested": "gpt-4o-mini",
      "model_resolved": "gpt-4o-mini",
      "runner_version": "1.6.0",
      "prompt_hash": "sha256:3a5898f8f9a8c98ef08f2f77ec4d5ffbc5f5f7930fb4780f8114a2abf2ff03f7",
      "runtime": {
        "stream": false,
        "system_prompt_provided": false,
        "temperature": 0.2,
        "max_tokens": 512,
        "top_p": 0.9,
        "timeout_seconds": 30,
        "max_retries": 0
      }
    },
    "usage": {
      "prompt_tokens": 32,
      "completion_tokens": 41,
      "total_tokens": 73
    }
  }
}

Generated Markdown (outputs/response.md):

# AI Prompt Response

## Prompt

Explain retry strategy

## Response

Retry strategy improves resilience by handling transient failures with controlled backoff.

## Metadata

- Provider: openai
- Timestamp (UTC): 2026-03-15T10:21:11.236575+00:00

Testing

Run all tests:

python3 -m pytest

Using uv:

uv run pytest

Run unit tests only:

python3 -m pytest tests/unit

Run E2E tests only:

python3 -m pytest tests/e2e

Coverage (requires pytest-cov):

python3 -m pytest --cov=src --cov-report=term-missing -q

Using uv:

uv run pytest --cov=src --cov-report=term-missing -q

Generate an HTML coverage report:

python3 -m pytest --cov=src --cov-report=term-missing --cov-report=html -q

Using uv:

uv run pytest --cov=src --cov-report=term-missing --cov-report=html -q

Coverage gate used in CI:

python3 -m pytest --cov=src --cov-report=term-missing --cov-fail-under=95

Open htmlcov/index.html in a browser to inspect file-by-file coverage.

Mutation testing (non-blocking):

cosmic-ray baseline cosmic-ray.toml
cosmic-ray init cosmic-ray.toml cr-session.sqlite
cosmic-ray exec cosmic-ray.toml cr-session.sqlite
cosmic-ray dump cr-session.sqlite > cosmic-ray-results.ndjson

See docs/testing.md for scope and interpretation details.

Lint

ruff check .

Using uv:

uv run ruff check .

CI

GitHub Actions workflow runs on:

  • push
  • pull_request

Pipeline includes:

  • dependency installation
  • lint (ruff check .)
  • package build verification (python3 -m build)
  • build artifact verification
  • tests with coverage enforcement (--cov-fail-under=95)

uv is supported for local development workflows, while CI currently installs dependencies from requirements.txt.

Versioning

This project follows semantic versioning.

Create release tags such as:

  • v0.1.0
  • v1.0.0

Release history and notes should be published through GitHub Releases.

Release Notes

See CHANGELOG.md for version history.

Release Checklist

See docs/release-checklist.md for the standardized release preparation flow.

Ops Runbook

See docs/ops-runbook.md for quick CI/runtime incident triage and error-code action mapping.

Troubleshooting

  • ModuleNotFoundError: No module named 'ai_prompt_runner': run commands from repository root and use python3 -m ... syntax.
  • Connection refused on API call: verify AI_API_ENDPOINT, provider availability, and local network access.
  • requests/pytest not found: activate .venv and reinstall dependencies with python3 -m pip install -r requirements.txt.

Security

  • Never commit .env files containing secrets.
  • Never hardcode API keys in source code.
  • Prefer environment variables over CLI flags for sensitive values like AI_API_KEY.

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

ai_prompt_runner-1.9.0.tar.gz (39.3 kB view details)

Uploaded Source

Built Distribution

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

ai_prompt_runner-1.9.0-py3-none-any.whl (42.0 kB view details)

Uploaded Python 3

File details

Details for the file ai_prompt_runner-1.9.0.tar.gz.

File metadata

  • Download URL: ai_prompt_runner-1.9.0.tar.gz
  • Upload date:
  • Size: 39.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.14.0

File hashes

Hashes for ai_prompt_runner-1.9.0.tar.gz
Algorithm Hash digest
SHA256 0cd0ff258f3f6c12e8e5799be86f582a81f03cdfbfbb772322f9ca5d21fefcdd
MD5 b32f67e2007365f7c5170e0a32c188e3
BLAKE2b-256 09cd29ca5e5e62b8a841978f2b4de745c69c4669b8543b6766d9d3f8b939b285

See more details on using hashes here.

File details

Details for the file ai_prompt_runner-1.9.0-py3-none-any.whl.

File metadata

File hashes

Hashes for ai_prompt_runner-1.9.0-py3-none-any.whl
Algorithm Hash digest
SHA256 29fb857047488abf91138290cfad2560c57952e4f745219e61a6e78ab9211d02
MD5 985cfc6d6eba001d5c3f76951a17fc86
BLAKE2b-256 b3b6087ba063aa0d8c43b815df8293f9a5594567769d8992445a7d6b0c35b669

See more details on using hashes here.

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