Skip to main content

pythonllmhook

Русская версия

pythonllmhook converts explicitly allowed runtime failures into verified Python handlers or source patches. The import name and CLI command are llmhook.

The package is not a repository-wide coding agent. A decorator defines the activation point, runtime evidence, specification, editable files, checks, persistence, and Git policy.

Status: alpha. Use it in a controlled repository. Review generated code before deployment.

Requirements

  • Python 3.11 or later
  • Git for source evolution
  • An OpenAI API key for the OpenAI provider

Installation

python -m pip install pythonllmhook

Install provider and keyring support:

python -m pip install "pythonllmhook[openai,keyring]"

Development checkout:

git clone https://github.com/averagedigital/pythonllmhook.git
cd pythonllmhook
python -m venv .venv
. .venv/bin/activate
python -m pip install -e ".[dev]"

Initialization

Run this inside a Git repository:

llmhook init
llmhook doctor

init creates .llmhook/prompts, adds runtime paths to .gitignore, and appends [tool.llmhook] configuration when it is absent. It does not replace an existing section.

Credentials

Environment variables are the default for CI and containers:

export OPENAI_API_KEY="..."

Local keyring commands:

llmhook auth login openai
llmhook auth login openai --profile work
llmhook auth status
llmhook auth logout openai

Credential lookup order: explicit provider value, provider environment variable, LLMHOOK_API_KEY, OS keyring. The auth CLI writes keys only to the OS keyring. The library does not intentionally copy resolved credentials into project files, incidents, reports, or logs.

Runtime handler

from llmhook import llm_except


@llm_except(
    spec="""
    Accept integers from 0 to 100.
    Convert digit-only strings to int.
    Reject other values.
    """,
    exceptions=(TypeError, ValueError),
    returns=int,
    checks=["pytest tests/test_score.py -q"],
    execution="apply_handler",
    persistence="generated_module",
)
def score(value: object) -> int:
    if not 0 <= value <= 100:
        raise ValueError("invalid score")
    return value

On a supported failure, the provider returns matches and handle functions. llmhook parses the code, rejects denied imports and calls, replays the failing input in a subprocess, runs configured checks, and then activates the handler. Active handlers are ordinary Python modules under .llmhook/generated. A later matching input does not call the model.

Execution modes:

  • raise: record or generate, then raise the original exception.
  • apply_handler: return the handler result.
  • retry_function: require RetryInput, then call the original function once.

Persistence modes:

  • none: current call only.
  • memory: current process only.
  • generated_module: .llmhook/generated.
  • source_patch: use the evolution pipeline.

Source evolution

from llmhook import llm_evolve


@llm_evolve(
    spec="Convert digit-only score strings to int. Reject all other strings.",
    exceptions=(TypeError, ValueError),
    context=["tests/test_score.py"],
    editable=["src/app.py:10-30", "tests/test_score.py"],
    checks=["ruff check src tests", "pytest tests/test_score.py -q"],
    mutation="local_branch",
)
def normalize_score(payload: dict[str, object]) -> int:
    score = payload["score"]
    if not 0 <= score <= 100:
        raise ValueError("invalid score")
    return score

local_branch creates a detached worktree at the incident commit, validates the diff against editable, applies it, runs checks, creates llmhook/<hook>-<fingerprint>, and commits there. The caller's branch and working tree are not changed. The default does not push and does not create a pull request.

Mutation modes:

  • none: verify and report only.
  • local_source: apply a verified patch to the current working tree without a commit.
  • local_branch: create a local branch and commit in a worktree.
  • push_branch: also push; requires permissions.push_branch = true.
  • pull_request: also create a draft PR with gh; requires push and PR permissions.
  • live_source: apply to the running application's source tree. It requires both LLMHOOK_ALLOW_LIVE_MUTATION=1 and permissions.live_source_mutation = true.

Source changes take effect after process restart unless the application supplies its own reload mechanism.

Configuration

[tool.llmhook]
mode = "development"
max_attempts = 3
max_context_bytes = 150000

[tool.llmhook.runtime_model]
provider = "openai"
model = "MODEL_NAME"

[tool.llmhook.git]
mutation = "local_branch"
branch_prefix = "llmhook/"

[tool.llmhook.permissions]
call_runtime_model = true
generate_handler = true
generate_patch = true
run_commands = true
push_branch = false
create_pr = false
live_source_mutation = false

[tool.llmhook.checks]
commands = ["ruff check src tests", "pytest -q"]
timeout_seconds = 300

Modes:

  • off: call the original function only.
  • capture: store incidents and do not call a model.
  • development: allow configured generation and mutation.
  • production: follow explicit permissions.

LLMHOOK_DISABLE=1 disables capture, model calls, and mutation. Decorated functions still run.

Environment overrides: LLMHOOK_MODE, LLMHOOK_MODEL, LLMHOOK_PROVIDER, LLMHOOK_MUTATION, LLMHOOK_ALLOW_LIVE_MUTATION, and LLMHOOK_DISABLE.

CLI

llmhook incidents list
llmhook incidents show INCIDENT_ID
llmhook incidents ignore INCIDENT_ID
llmhook handlers list
llmhook handlers show HOOK_ID
llmhook handlers disable HOOK_ID VERSION
llmhook repair INCIDENT_ID
llmhook replay INCIDENT_ID
llmhook evolve HOOK_ID
llmhook config show

CI

Use environment credentials. Do not enable push, pull request, or live mutation permissions in a test job. Run:

python -m pytest -q
ruff check src tests
pyright src tests
python -m build

Security limits

Generated handlers and patches are untrusted code. AST checks, subprocess replay, path validation, and tests reduce risk. They do not provide a security sandbox. The verification process can access the current user account and files allowed by the operating system.

Redaction uses field names, length limits, and bounded serialization. It can miss secrets in free text, source files, custom objects, or encoded values. Do not include credentials in decorated function arguments or source context.

Line-range checks validate diff hunk positions against the original file. File boundaries are strict. Complex line movement can be rejected. Review every source patch.

live_source changes files used by a running process. It does not reload the process, guarantee correctness, or provide rollback.

Storage

.llmhook/
  incidents/    deduplicated runtime records
  generated/    handler modules and metadata
  reports/      evolution reports
  prompts/      local prompt additions
  worktrees/    isolated Git worktrees

Incident consistency and locks are local to one machine. There is no distributed coordination.

License

MIT. See LICENSE.

Download files

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

Source Distribution

pythonllmhook-0.1.0.tar.gz (34.6 kB view details)

Uploaded Source

Built Distribution

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

pythonllmhook-0.1.0-py3-none-any.whl (33.0 kB view details)

Uploaded Python 3

File details

Details for the file pythonllmhook-0.1.0.tar.gz.

File metadata

  • Download URL: pythonllmhook-0.1.0.tar.gz
  • Upload date:
  • Size: 34.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.11

File hashes

Hashes for pythonllmhook-0.1.0.tar.gz
Algorithm Hash digest
SHA256 9ad8ca1afb0961a6d093a4a62e04af6bd0867fbf7775832c3ec80058f7babf97
MD5 10dec181250ee4aef54908714107c8df
BLAKE2b-256 af9eefc22a90d19d9f7bea2960f4ca61500e7fe32139c42fb14e7e4f4f8bca18

See more details on using hashes here.

File details

Details for the file pythonllmhook-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: pythonllmhook-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 33.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.11

File hashes

Hashes for pythonllmhook-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9f733063085a0f0b753fcd9e2d9a6222444aa3ab8e3e0ff47212e772cfab385b
MD5 f27bd550db88ff3bbb502fa7276d15c3
BLAKE2b-256 34c8460f1e438ed8db2ef5caad3970224debfd5228b5ece27acb29bbcfe93253

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 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