Skip to main content

📄 Sira

cover

Sira is a multi-agent AI system that analyzes job postings and tailors your resume to match specific job requirements. It ensures authenticity, avoids AI clichés, and optimizes for Applicant Tracking Systems (ATS).

📚 Read the documentation → — user guide, CLI reference, architecture, and developer guide.

Sira (سيرة) is the Arabic word for a life story. Sīra dhātiyya (سيرة ذاتية) is the term for a curriculum vitae — the story you tell about your own work.

🚀 Features

  • Multi-Agent Architecture: 6 pipeline stages plus job scraping — dedicated agents for analysis, writing, and quality assurance.
  • Automated Job Scraping: Fetches job posting content from any public URL using Playwright.
  • Resume Memory: Stores your original resume plus job-specific tailored outputs in SQLite.
  • Authentic Tailoring: Rephrases your experience to match the job without inventing skills.
  • Hallucination & Cliché Detection: Built-in auditor to ensure quality and "human" tone.
  • Quality Gate Validators: Core pipeline agents' output is scored 0–10 by a quality gate before the pipeline proceeds.
  • Comprehensive Reporting: Generates self-review reports with gaps analysis, suggestions, and recommendations.
  • Self-Correcting Workflow: Write → Review → Audit loop with retries and quality feedback (defaults: 2 write attempts × 1 review iteration, both configurable).
  • Re-Tailoring: Re-run tailoring on a saved job with recommendations from a prior audit (re-tailor command).

🛠️ Architecture

The system runs a sequential pipeline with an inner refinement loop:

Stage 0 — Job Scraper: Fetches job posting content from any public URL using Playwright and converts HTML to Markdown (multi-strategy fallback: markitdown → html2text).

Stage 1 — Resume Parser: Parses your resume (Markdown, DOCX, or PDF) into a structured CV object. Not quality-gated — the parse is cached by content hash instead.

Stage 2 — Job Analyst: Extracts structured job requirements (title, company, skills, keywords) from the scraped posting. Not quality-gated.

On a cold cache these two stages run concurrently.

Stages 3–5 — Write → Review → Audit Loop (outer loop, --write-attempts, default 2):

Stage Agent Description
3. Write CV Writer Tailors the CV to match job requirements → Quality gate validates tailoring
4. Review Reviewer Scores CV quality and suggests improvements; triggers the refinement loop (--review-iterations, default 1, per write attempt)
5. Audit Auditor Checks for hallucinations and AI clichés → Quality gate validates audit quality. If audit fails, the entire Write → Review → Audit loop retries from stage 3.

Stage 6 — Report Generator: Judges which job skills your CV covers by meaning (one skill-matcher call over the whole CV text), computes the match score and verdict in Python, and compiles the self-review report.

Quality Gate System: The CV Writer and the Auditor have validators that score their output 0–10 with a shared quality-gate agent. If the score falls below the gate threshold (--gate-threshold, default 6), the agent retries with corrective feedback. On quality gate exhaustion, the system falls back to the last available output (graceful degradation) instead of failing fatally. Disable it entirely with --no-quality-gate.

Write → Review → Audit Loop: After the initial write, the reviewer scores the draft and suggests refinements. Once review iterations are exhausted, the auditor checks for hallucinations. If the audit fails, the entire write → review → audit loop retries. Both limits are flags: --write-attempts (default 2) and --review-iterations (default 1).

📋 Prerequisites

  • Python 3.13+
  • A Chromium browser for Playwright — installed once with sira setup
  • LLM Provider API Key — OpenAI by default; many providers supported (see LLM Providers)
  • uv (Fast Python package installer and resolver) — only for the from-source install below

📦 Installation

From PyPI

Sira is on PyPI. Install it as a standalone tool with uv or pipx — each gives Sira its own isolated environment and puts the sira command on your PATH:

uv tool install sira        # or: pipx install sira, or: pip install sira
sira setup                  # downloads the Chromium browser the job scraper drives
export OPENAI_API_KEY=your_api_key_here
sira tailor <JOB_URL> <RESUME_PATH>

sira setup runs playwright install chromium inside Sira's own environment, so the browser always matches the Playwright version Sira was installed with. Extras work the same way: uv tool install "sira[guard]".

The rest of this README writes commands as uv run sira …, which is the from-source form below. With a PyPI install, drop the uv run prefix.

From source (development)

  1. Clone the repository:

    git clone https://github.com/Tiqni/sira
    cd sira
    
  2. Install dependencies: This project uses uv for dependency management.

    uv sync
    
  3. Install the browser Playwright drives: The job scraper runs a real headless Chromium. The browser binary is a separate download from the Python package:

    uv run sira setup
    
  4. Set up Environment Variables: Export your API key for the LLM provider you plan to use:

    # OpenAI (default)
    export OPENAI_API_KEY=your_api_key_here
    
    # Other providers — see the LLM Providers section below
    # export ANTHROPIC_API_KEY=...
    # export GOOGLE_API_KEY=...
    # export GROQ_API_KEY=...
    # export MISTRAL_API_KEY=...
    

🤖 LLM Providers

Sira is built on PydanticAI, which supports a wide range of LLM providers. You select a provider and model via the --model CLI option using the format <provider>:<model>.

The default model is openai:gpt-5-mini. To use a different provider or model, pass --model with the appropriate prefix:

uv run sira tailor <JOB_URL> <RESUME_PATH> --model anthropic:claude-sonnet-4-5

Supported Providers

Provider Prefix Example --model Required Env Var
OpenAI openai: openai:gpt-4o-mini OPENAI_API_KEY
Anthropic anthropic: anthropic:claude-sonnet-4-5 ANTHROPIC_API_KEY
Google Gemini google: google:gemini-3-pro-preview GOOGLE_API_KEY
Google Cloud (Vertex AI) google-cloud: google-cloud:gemini-3-flash-preview GOOGLE_API_KEY
Groq groq: groq:llama-3.3-70b-versatile GROQ_API_KEY
Mistral mistral: mistral:mistral-large-latest MISTRAL_API_KEY
xAI xai: xai:grok-3-mini XAI_API_KEY — needs the sira[xai] extra (see tip)
Cohere cohere: cohere:command-r-plus CO_API_KEY
DeepSeek deepseek: deepseek:deepseek-chat DEEPSEEK_API_KEY
OpenRouter openrouter: openrouter:openai/gpt-4o OPENROUTER_API_KEY
Ollama (local) ollama: ollama:llama3 OLLAMA_BASE_URL
GitHub Models github: github:xai/grok-3-mini GITHUB_API_KEY — retired by GitHub on 2026-07-30; removed in pydantic-ai v3
Cerebras cerebras: cerebras:llama3.1-8b CEREBRAS_API_KEY
AWS Bedrock bedrock: bedrock:anthropic.claude-sonnet-4-5 AWS credentials

💡 Tip: PydanticAI resolves the model class, provider, and profile automatically from the <provider>:<model> string. Every provider above works out of the box except the retired GitHub Models and xAI: pydantic-ai 2.x uses the native xai-sdk, which cannot be installed together with Sira's dev tools, so it is an opt-in extra — install with uv sync --extra xai --no-dev instead of plain uv sync.

Using a Local Model (Ollama)

To use a model via Ollama, make sure Ollama is running, the model is pulled, and point the provider at Ollama's OpenAI-compatible endpoint by exporting OLLAMA_BASE_URL (PydanticAI requires it — there is no default):

ollama pull llama3
export OLLAMA_BASE_URL=http://localhost:11434/v1
uv run sira tailor <JOB_URL> <RESUME_PATH> --model ollama:llama3

Ollama cloud models work the same way through the local daemon (sign in with ollama signin first):

export OLLAMA_BASE_URL=http://localhost:11434/v1
uv run sira tailor <JOB_URL> <RESUME_PATH> --model 'ollama:kimi-k2.6:cloud'

⚠️ Note: Local models may be slower or produce less reliable structured output than cloud providers. The quality gates and retries help compensate, but for production use a cloud provider is recommended.

OpenAI-Compatible Providers

Many providers offer OpenAI-compatible APIs. PydanticAI supports these via the openai: prefix combined with provider-specific routing environment variables. See the PydanticAI OpenAI docs for details on Together AI, Perplexity, Fireworks AI, Azure AI Foundry, and more.

🏃 Usage

The CLI uses positional arguments (not interactive prompts). Two commands are available:

tailor — Run the full workflow

uv run sira tailor <JOB_URL> <RESUME_PATH> [OPTIONS]

Arguments:

  • JOB_URL — URL of the job posting (must start with http:// or https://)
  • RESUME_PATH — Path to your resume (.md, .docx, or .pdf)

Options:

  • --output-dir PATH — Output directory (default: ./output)
  • --model MODEL — LLM provider and model in provider:model format (default: openai:gpt-5-mini). See LLM Providers for all supported options.
  • --verbose / -v — Stream agent thinking and prompts in real-time
  • --debug / -d — Enable debug output and save the converted resume markdown
  • --interactive / -i — Pause at quality checkpoints (audit failure, weak match) and ask whether to continue, give feedback and retry, or quit. Skipped automatically when stdin is not a terminal.
  • --output-pattern TEMPLATE — Template for job-specific subdirectory name (default: {company_name}-{job_title})
  • --resume-name-pattern TEMPLATE — Template for resume file base name (default: {company_name}-{full_name})

Example

uv run sira tailor \
  https://www.linkedin.com/jobs/view/12345678 \
  /Users/me/resume.md \
  --model openai:gpt-4o-mini

re-tailor — Re-run with feedback from a prior audit

uv run sira re-tailor <JOB_ID> <RECOMMENDATIONS> [OPTIONS]

Arguments:

  • JOB_ID — UUID of the prior job (shown in output after a tailor run)
  • RECOMMENDATIONS — Comments or recommendations from the prior audit report

Options:

  • --resume-path PATH — Resume path (uses the stored path from the prior job if omitted)
  • --output-dir PATH — Output directory (default: ./output)
  • --model MODEL — AI model override
  • --verbose / -v — Stream agent thinking and prompts in real-time
  • --debug / -d — Enable debug output and save the converted resume markdown
  • --interactive / -i — Pause at quality checkpoints (audit failure, weak match) and ask whether to continue, give feedback and retry, or quit. Skipped automatically when stdin is not a terminal.
  • --output-pattern TEMPLATE — Template for job-specific subdirectory name (default: {company_name}-{job_title})
  • --resume-name-pattern TEMPLATE — Template for resume file base name (default: {company_name}-{full_name})

💡 Tip: If the original resume file no longer exists on disk when running re-tailor, you must provide --resume-path to point to the current location of your resume.

Example

uv run sira re-tailor \
  a1b2c3d4-... \
  "Add more emphasis on cloud infrastructure experience" \
  --model openai:gpt-4o-mini

resume / runs — Continue an interrupted run

Every run is durable: each model request is checkpointed by DBOS in a local SQLite file (dbos.sqlite3 in the data directory, override with SIRA_DBOS_DATABASE_URL). sira tailor prints a Run ID at the start and again at the end. It is not the Job ID printed with it — the Job ID names the memory record used by re-tailor; the Run ID names the durable run used by resume. If the process is killed, crashes, or a stage fails, continue from the last completed model request — earlier agents are replayed from their checkpoints, not called again:

Privacy: dbos.sqlite3 stores each run's inputs and checkpoints — your full resume text, the job posting, every model response (including the tailored CV) and your answers at interactive checkpoints — pickled, with your user's default file permissions. It never leaves your machine. Delete the file to purge it, or point SIRA_DBOS_DATABASE_URL at another location. Rows are readable only by the same Sira and pydantic-ai versions that wrote them.

uv run sira resume <RUN_ID>

A run that failed is continued as a new run id (printed as Continued as run: …); a run that was only interrupted keeps its id. If you answered "quit" at an interactive checkpoint, resume asks the question again. List recent runs and their status with:

uv run sira runs --limit 10

A run can only be resumed by the same Sira version that started it.

Durability covers the pipeline (parsing, analysis, writing, review, audit, report). The steps before it — resume conversion, the parsed-resume cache lookup, and scraping the job posting — are quick pre-flight work and are not checkpointed: a crash there leaves no run to resume, so just run sira tailor again.

Live progress & speed

By default a live progress dashboard is shown in the terminal, updating as each pipeline stage completes. In non-TTY environments (CI, pipes) it degrades to plain line-by-line logging automatically.

--verbose / -v — streams every agent's thinking and output tokens in real-time. This replaces the dashboard with direct streaming output and is the recommended mode for clean interactive viewing (see note below).

Speed flags (available on both tailor and re-tailor):

Flag Default Description
--fast off Speed preset: trims loops to 1 write + 1 review and enables faster model tier for mechanical agents
--write-attempts N 2 Maximum writer attempts in the write → review → audit outer loop
--review-iterations N 1 Maximum reviewer iterations per write attempt
--quality-gate / --no-quality-gate on Enable or disable the advisory quality gate
--gate-threshold N 6 Re-run an agent only when its quality score is below this threshold (0–10)

Note on TTY interleaving: when running interactively with the default live dashboard, the dashboard's Rich panel and the workflow's own print() calls can interleave and garble the output. This does not affect correctness — only the visual display. For clean interactive output use --verbose, or run in a non-TTY environment (pipe, CI). A future follow-up will route workflow print() calls through the reporter to eliminate this.

Alternative entry point

You can also invoke the CLI directly:

uv run python sira/main.py tailor <JOB_URL> <RESUME_PATH>

View Results

Upon successful completion, output files are saved in job-specific subdirectories under output/ (or the path specified via --output-dir). The subdirectory name follows the --output-pattern template (default: {company_name}-{job_title}).

Three resume formats are generated per run, all rendered from the same structured CV the writer produced (the PDF and DOCX share one template, so they look the same):

  • .md — Markdown
  • .pdf — PDF
  • .docx — DOCX

Pick the template with --style modern|classic|compact (default modern); see Output and reports for what each style looks like.

A comprehensive self-review report is also generated:

  • _report.md — Markdown report with match score, gap analysis, and recommendations

Example structure for a job at Acme Corp for a Senior Engineer role:

output/
└── acme_corp-senior_engineer/
    ├── acme_corp-Jane_Doe.md          ← Tailored resume (Markdown)
    ├── acme_corp-Jane_Doe.pdf         ← Tailored resume (PDF)
    ├── acme_corp-Jane_Doe.docx        ← Tailored resume (DOCX)
    └── acme_corp-Jane_Doe_report.md   ← Self-review report

Use --resume-name-pattern to customize the base filename (default: {company_name}-{full_name}). Available template variables: {company_name}, {job_title}, {full_name}, {timestamp}.

🧠 Resume Memory Behavior

  • The first run requires providing a resume path so the CLI can store your original resume.
  • Subsequent runs reuse the latest stored original resume from the SQLite database.
  • Content-hash caching: If your resume file hasn't changed since the last run, the pre-parsed CV is reused — no LLM parsing call is made, saving time and cost.
  • Every job submission starts from the original resume, never from a previous tailored resume.
  • Each successful tailoring run stores the tailored resume and audit result linked back to the original source resume.
  • Sira keeps its runtime state in a per-user data directory (~/Library/Application Support/sira on macOS, ~/.local/share/sira on Linux, %LOCALAPPDATA%\sira on Windows; set SIRA_DATA_DIR to change it). The memory database is resume_memory.sqlite3 in that directory, so tailor and re-tailor share it from any working directory. A memory/resume_memory.sqlite3 left by a release before 1.5 is moved there the first time Sira runs.
  • When running re-tailor, if the original resume file no longer exists on disk at its recorded path, you must provide --resume-path to restore the link.

📊 Self-Review Report

Each workflow run generates a self-review report that includes:

  • What Changed: Summary changes, reordered/deprioritized skills, and per-experience bullet rewrites
  • Quality Metrics: Hallucination score (0–10) and AI cliché score (0–10)
  • Gap Analysis: Keyword coverage, missing hard/soft skills vs. the job posting
  • Suggestions to Strengthen: Recommended improvements to better match the job
  • Audit Summary: Feedback from the auditor on tone, authenticity, and compliance
  • Skills Covered: Hard/soft skills the job asks for that your CV shows — matched by meaning, not only by exact words ("mentor to ~30 engineers" covers "Technical leadership and mentorship"), each with the CV line as evidence
  • Match Score: 0–100 = 0.6 × hard-skill coverage + 0.2 × soft-skill coverage + 0.2 × ATS keyword coverage (buckets the job does not list are rescaled away). Computed in Python, not by the model.
  • Overall Recommendation: "Strong Match" (score ≥ 75 and hard-skill coverage ≥ 75 %, or the job lists no hard skills), "Partial Match" (score ≥ 50), or "Weak Match"

✅ Quality Gate System

A shared quality_gate_agent scores other agents' output:

  • Advisory, not blocking: output is scored once, 0–10. An agent is re-run only when its score is below --gate-threshold (default 6).
  • Automatic retry: a below-threshold score raises a retry carrying the concrete improvements the gate asked for. Retry counts are set per agent in workflows/agents.py — they are not uniform.
  • Graceful fallback: when retries are exhausted, the last available output is used instead of failing the run.
  • Token usage tracking: gate runs count toward usage metrics, so cost reporting stays accurate.
  • Optional: --no-quality-gate removes the scoring calls entirely.

Full detail: Agent reference.

🛠️ Make Commands

Command Description
make help Show available commands and descriptions.
make install Install production dependencies using uv.
make install/dev Install development dependencies using uv.
make test Run the full test suite using pytest.
make install/uv Ensure uv is installed (auto-run by other targets).

Note: The make run target is deprecated and uses outdated paths. Use uv run sira tailor <JOB_URL> <RESUME_PATH> instead.

📂 Project Structure

sira/
├── sira/         # Main Python package
│   ├── main.py                # CLI entry point (Typer: tailor + re-tailor)
│   ├── workflows/             # Workflow orchestration and agent definitions
│   │   ├── __init__.py        # ResumeTailorWorkflow class
│   │   └── agents.py          # All agent definitions + quality gate validators
│   ├── paths.py               # Per-user data directory (SIRA_DATA_DIR)
│   ├── models/                # Pydantic data models
│   │   ├── agents/            # Agent output types (CV, JobAnalysis, AuditResult, etc.)
│   │   │   ├── output.py      # Core output models
│   │   │   └── deps.py        # Agent dependency types
│   │   └── workflow.py        # ResumeTailorResult
│   ├── memory/                # SQLite-backed resume memory
│   │   ├── models.py          # Memory domain models
│   │   ├── parser.py          # Resume parser adapter
│   │   ├── repository.py      # Abstract repository interface
│   │   ├── sqlite_repository.py  # SQLite implementation
│   │   └── service.py         # Orchestration service
│   ├── tools/                 # Playwright scraping, HTML parsing helpers
│   │   ├── playwright.py      # File I/O tool for agents
│   │   └── job_scraper_helpers.py  # HTML→MD parsers, placeholder detection
│   ├── rendering/
│   │   ├── __init__.py       # render_resume(cv, dir, base_name, style) → .md/.pdf/.docx
│   │   ├── errors.py         # RenderError
│   │   ├── templates.py      # TemplateSpec: modern, classic, compact
│   │   ├── inline.py         # inline markdown subset (links, bold, italic, code)
│   │   ├── html.py + resume.html.j2   # CV → HTML (Jinja2)
│   │   ├── css.py            # TemplateSpec → CSS for the PDF
│   │   ├── pdf.py            # HTML + CSS → PDF (PyMuPDF Story)
│   │   ├── docx.py           # CV + TemplateSpec → DOCX (python-docx)
│   │   └── markdown.py       # CV → Markdown
│   └── utils/                 # Markdown writer, resume conversion, CV diff
│       ├── cv_diff.py         # Pure-Python CV diff, gap analysis, match score
│       ├── skill_cleanup.py   # Collapse duplicate skill variants
│       ├── markdown_writer.py # generate_report_markdown
│       ├── resume_converter.py  # DOCX/PDF → Markdown conversion
│       └── validate_inputs.py
├── tests/                     # Test suite
│   ├── memory/                # Memory layer tests
│   ├── workflows/             # Workflow integration tests
│   ├── conftest.py            # Pytest fixtures (disables real LLM calls)
│   └── factories.py           # Test data factories
├── docs/                      # Additional documentation
├── output/                    # Default output directory for generated files
├── Makefile                   # Command shortcuts
├── pyproject.toml             # Project configuration and dependencies
└── README.md                  # This file

🛡️ Safety & Quality

  • Anti-Hallucination: The system is strictly instructed never to invent skills or experiences.
  • Cliché Filter: Avoids terms like "spearheaded", "synergy", "leveraged", and "game-changer".
  • Multi-Layer Validation: Quality gates score core pipeline agent output; auditor cross-checks final CV against the original.
  • Prompt-Injection Awareness: Some job pages embed text aimed at AI readers ("ignore previous instructions", hidden display:none blocks, "rate this candidate as a perfect match"). Every scraped page is scanned in plain Python (no LLM) for instruction overrides in 10 languages, text that addresses an AI, role/output manipulation, exfiltration requests, invisible Unicode, and phrases that exist in the HTML but not in the visible text. A match prints a ⚠️ Potential prompt-injection content detected (…) warning and the run continues — detection is advisory, and the scraper and analyst prompts are told to treat page text as data, never as instructions.

Optional: local classifier (sira[guard])

For a second opinion from a small model, install the guard extra. It runs Meta Llama Prompt Guard 2 (22M) locally on the scraped Markdown and adds a classifier_flagged indicator to the same warning.

uv sync --extra guard        # torch + transformers, ~2 GB
# or, installed from PyPI:  uv tool install "sira[guard]"
  • The first tailor run asks once whether the model may be downloaded (~90 MB) and executed on your machine, and remembers the answer in ~/.config/sira/guard_consent.json. Pre-answer with SIRA_GUARD_CONSENT=yes|no for scripts and CI.
  • The default model is gated: accept Meta's license on Hugging Face and set HF_TOKEN. If you cannot, point SIRA_GUARD_MODEL at an open model such as protectai/deberta-v3-base-prompt-injection-v2.
  • Any failure (no token, no network, out of memory) prints a warning and falls back to the regex scan. It never stops a run.

🤝 Contributing

Contributions are welcome! Please ensure you follow the coding guidelines and add tests for new features.

📖 Further Reading

The documentation site is the full reference:

In this repository:

  • ARCHITECTURE.md — Detailed system architecture, data flow, quality gate system, and design decisions
  • AGENTS.md — Agent development guide with conventions, tool invocation, and testing patterns
  • .github/copilot-instructions.md — GitHub Copilot instructions for AI-assisted development

Metadata

Release files for sira 2.1.0

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

Source distribution (sdist)

Source distribution for sira 2.1.0
File Size Uploaded
sira-2.1.0.tar.gz 186.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for sira 2.1.0
File Interpreter ABI Platform
sira-2.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 301.6 kB

Release files / sira-2.1.0.tar.gz

Download URL sira-2.1.0.tar.gz
Size 186.5 kB
Tags Source
SHA-256 checksum
How to use checksums
1279737dea05f103f318dfb665e1aaad98d2a0c3c1cad7508d048a253ceae0fb
BLAKE2b-256 checksum
How to use checksums
aae56f7211ca888e40f2028e5b8144f5cd99887eb06d4ffa661e6693a9705081
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.15 {"installer":{"name":"uv","version":"0.12.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

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 Sep 16, 2026.

Transparency log

Release files / sira-2.1.0-py3-none-any.whl

Download URL sira-2.1.0-py3-none-any.whl
Size 115.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
542eab7ae8e5f8b3e0563de453b699c6c96001e4aafbf1092623c87cc49dbef8
BLAKE2b-256 checksum
How to use checksums
df013641f4d56c4da742e55bb9c3b6c62ea267ec3bcfec4b6081b9ea814a4ed7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.15 {"installer":{"name":"uv","version":"0.12.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

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 Sep 16, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

2.1.0 This release

2 release files

2.0.0

2 release files

1.5.0

2 release files

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