Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

92 Commits

Repository files navigation

🔧 pfix

PyPI versionPyPI downloadsPython 3.10+Code style: blackRuffpre-commitTests: 56 examplesDocsLicense: Apache-2.0

AI Cost Tracking

PyPIVersionPythonLicenseAI CostHuman TimeModel

  • 🤖 LLM usage: $2.9533 (86 commits)
  • 👤 Human dev: ~$2235 (22.4h @ $100/h, 30min dedup)

Generated on 2026-07-05 using openrouter/deep/deep-v4-pro


Self-healing Python — catches runtime errors and fixes source code + dependencies via LLM + MCP.

The strategy of using a small tool for detecting errors in a single library or file enables quick bug fixes before writing a prompt, since all context is contained in the error file. This offloads large and expensive models that should be used where smaller ones are insufficient—allowing them to plan and create strategy for the entire library instead of handling individual errors, like in test files.

This automation can also be leveraged via CI/GitOps with provided API keys to the LLM provider for fixing encountered errors during testing across the entire ecosystem.

💡 New in 0.1.5: Zero-configuration mode! Just import pfix with PFIX_AUTO_APPLY=true in .env and any exception triggers automatic repair.

Features

  • Zero-config modeimport pfix + .env = auto-healing for entire project
  • @pfix decorator — wrap any function; errors trigger automatic repair
  • Fast dep fixModuleNotFoundError → instant pip/uv install (no LLM call)
  • pipreqs scanning — project-wide import analysis for missing dependencies
  • LLM code repair — sends error context to LLM (OpenRouter/LiteLLM) for intelligent fixes
  • pip + uv — auto-detects uv for faster installs, falls back to pip
  • MCP server@mcp.tool() via FastMCP for IDE integration (Claude Code, Cursor, VS Code)
  • Git auto-commit — optional auto-commit of fixes with configurable prefix
  • Auto-restartos.execv process restart after fix applied
  • Interactive diff — unified diff with confirmation before applying
  • Backup system — timestamped backups in .pfix_backups/ (can be disabled)
  • Async support@apfix for async functions

From PyPI (Users)

pip install pfix
# With MCP server support
pip install pfix[mcp]
# With git auto-commit
pip install pfix[git]
# Everything
pip install pfix[all]

From Source (Developers)

Clone and install in editable mode:

git clone https://github.com/softreck/pfix.git
cd pfix
pip install -e .# Or with all optional dependencies
pip install -e ".[all]"

Editable mode (-e) allows you to modify source code without reinstalling. Changes take effect immediately.

Running Examples

After installation, examples can be run from any directory:

# From project rootcd /path/to/pfix/examples
# Run a single example categorycd types && python main.py
cd data && python main.py
# The .env file in project root is automatically found

Example workflow:

cd examples
python run_all.py # Run all 12 categories, auto-reset at end
python run_all.py --dry-run # Preview what would run
python run_all.py --no-reset # Keep fixed versions for inspection
python reset.py # Manual reset when needed

Option 1: Zero Configuration (Recommended)

Just import pfix with PFIX_AUTO_APPLY=true in your .env:

# .env
OPENROUTER_API_KEY=sk-or-v1-...
PFIX_AUTO_APPLY=true
# your_script.pyimportpfix# Auto-activates global exception hookdefbuggy_function(x):
return1/x# Division by zero? Auto-fixed!buggy_function(0) # pfix catches, analyzes, fixes, and retries

What happens:

  1. Exception is caught by global hook
  2. LLM analyzes the error context
  3. Fix is applied to source file
  4. Process restarts (if PFIX_AUTO_RESTART=true)

Option 2: Explicit Session Control

Use pfix_session for fine-grained control:

frompfiximportconfigure, pfix_sessionconfigure(auto_apply=True, dry_run=False)
defprocess_data(data):
returndata[0] /data[1] # Might failwithpfix_session(__file__, auto_apply=True):
result=process_data([1, 0]) # Auto-fixed on errorprint(f"Result: {result}")

Option 3: Decorator (Per-Function)

Use @pfix for function-level control:

frompfiximportpfix@pfix(retries=3, hint="Processes CSV files")defanalyze_csv(path):
importpandasaspd# Auto-installed if missingdf=pd.read_csv(path)
returndf.groupby("category").sum()
@pfix(deps=["requests", "python-dateutil"])deffetch_events(url: str):
importrequestsfromdateutil.parserimportparsereturn [parse(e["ts"]) foreinrequests.get(url).json()["events"]]

Pattern A: Development Mode (Interactive)

frompfiximportconfigure# Ask before applying fixesconfigure(auto_apply=False)
importpfix# Hook installeddefrisky_operation():
returnundefined_variable# NameErrorrisky_operation() # Shows diff, asks for confirmation

Pattern B: CI/CD Mode (Non-Interactive)

frompfiximportconfigure# Auto-apply everything, dry run for safetyconfigure(auto_apply=True, dry_run=True, create_backups=False)
importpfix

Pattern C: Library Mode (Specific Functions)

frompfiximportpfix, pfix_session# Only protect specific functions@pfix(auto_apply=True)defunstable_api_call():
...
# Or specific code blockswithpfix_session(__file__):
untrusted_code()

How It Works

┌─────────────────────────────────────────────────────────────────┐
│ Your Code │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────┐ │
│ │ Zero Config │ or │ Session Block │ or │ @pfix Decor │ │
│ │ import pfix │ │ with pfix_session│ │ @pfix │ │
│ └────────┬────────┘ └────────┬────────┘ └──────┬────┘ │
│ │ │ │ │
│ └────────────────────────┴────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Exception Occurs │ │
│ │ ┌────────────────────────────────────────────────────┐ │ │
│ │ │ 1. ModuleNotFoundError? │ │ │
│ │ │ → pip/uv install → retry │ │ │
│ │ └────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌────────────────────────────────────────────────────┐ │ │
│ │ │ 2. Build ErrorContext │ │ │
│ │ │ - Traceback │ │ │
│ │ │ - Source code │ │ │
│ │ │ - Local variables │ │ │
│ │ │ - File imports │ │ │
│ │ │ - pipreqs scan │ │ │
│ │ └────────────────┬───────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌────────────────────────────────────────────────────┐ │ │
│ │ │ 3. LLM Analysis (LiteLLM → OpenRouter) │ │ │
│ │ │ - Diagnosis │ │ │
│ │ │ - Fix proposal │ │ │
│ │ │ - Confidence score │ │ │
│ │ └────────────────┬───────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌────────────────────────────────────────────────────┐ │ │
│ │ │ 4. Apply Fix (if confidence > 0.1) │ │ │
│ │ │ - Show diff (or auto-apply) │ │ │
│ │ │ - Create backup (if create_backups=True) │ │ │
│ │ │ - Write fixed code │ │ │
│ │ │ - Git commit (if git_auto_commit=True) │ │ │
│ │ └────────────────┬───────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌────────────────────────────────────────────────────┐ │ │
│ │ │ 5. Recovery │ │ │
│ │ │ - Reload module → retry │ │ │
│ │ │ - or os.execv restart │ │ │
│ │ └────────────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘

Error Types & Fixes

Error TypeAuto-Fix StrategyExample Fix
ModuleNotFoundErrorpip/uv installpip install requests
NameErrorAdd importimport os at top
TypeErrorType conversionstr(age) instead of age
AttributeErrorFix attribute accessobj.get() instead of obj.attr
ZeroDivisionErrorAdd guard clauseif x == 0: return 0
IndexErrorBounds checkingif i < len(list):
KeyErrorSafe dict accessdict.get(key, default)
ValueErrorInput validationTry/except or validation
FileNotFoundErrorCheck path existenceif os.path.exists():

Confidence Thresholds

  • > 90%: High confidence fixes (type conversions, simple guards)
  • 50-90%: Medium confidence (logic changes, API adjustments)
  • 10-50%: Low confidence (complex refactorings)
  • < 10%: Skipped (manual review recommended)

CLI

pfix run script.py # Run with global exception hook
pfix run script.py --auto # Auto-apply fixes
pfix run script.py --restart # Restart process after fix
pfix check # Show config status
pfix diagnose # Run environment diagnostics
pfix diagnose --category memory,filesystem # Filter categories
pfix diagnose --fix # Auto-fix what can be fixed
pfix diagnose --output TODO.md # Save to file
pfix deps scan # Scan for missing deps (pipreqs)
pfix deps install # Install all missing deps
pfix deps generate # Generate requirements.txt
pfix server # Start MCP server (stdio)
pfix server --http 3001 # Start MCP server (HTTP)

MCP Integration

pfix exposes tools via FastMCP for IDE integration:

ToolDescription
pfix_analyzeAnalyze error → diagnosis + fix proposal
pfix_fixAnalyze + apply fix (with backup)
pfix_diagnoseRun environment diagnostics
pfix_deps_scanScan for missing deps
pfix_deps_installInstall a package
pfix_deps_generateGenerate requirements.txt
pfix_edit_fileWrite file content

Claude Code / VS Code setup

Add to your MCP config (.claude/mcp.json or VS Code settings):

{
"mcpServers": {
"pfix": {
"command": "python",
"args": ["-m", "pfix.mcp_server"]
}
}
}

Configuration

pfix supports multiple configuration methods (in order of priority):

  1. Environment variables (override everything)
  2. .env file in project root
  3. pyproject.toml[tool.pfix] section
  4. setup.cfg[pfix] section
  5. setup.py keyword arguments
  6. Programmatic configure()

Configuration Priority

Higher numbers win (environment variables have highest priority):

[6] Environment variables (PFIX_*)
[5] .env file
[4] pyproject.toml [tool.pfix]
[3] setup.cfg [pfix]
[2] setup.py setup()
[1] configure() programmatic

Method 1: .env (Recommended for Development)

Create a .env file in your project root:

# Required
OPENROUTER_API_KEY=sk-or-v1-...
# Behavior
PFIX_AUTO_APPLY=true # Auto-apply fixes without confirmation
PFIX_AUTO_INSTALL_DEPS=true # Auto-install missing dependencies
PFIX_AUTO_RESTART=true # Restart process after fix
PFIX_MAX_RETRIES=3
PFIX_CREATE_BACKUPS=false # Disable backups# Optional
PFIX_MODEL=openrouter/qwen/qwen3-coder-next
PFIX_PKG_MANAGER=uv # pip or uv
PFIX_GIT_COMMIT=false # Auto-commit fixes
PFIX_GIT_PREFIX="pfix: "

Note:.env is searched from current working directory upward, so it works from any subdirectory (e.g., examples/).

Method 2: pyproject.toml (Recommended for Projects)

Add to your pyproject.toml:

[tool.pfix]
model = "openrouter/qwen/qwen3-coder-next"auto_apply = trueauto_install_deps = trueauto_restart = truemax_retries = 3create_backups = falsegit_auto_commit = falsegit_commit_prefix = "pfix: "enabled = truedry_run = falsepkg_manager = "uv"# auto, pip, or uvmcp_enabled = falsemcp_transport = "stdio"mcp_server_url = "http://localhost:3001"

Benefits:

  • Version controlled with your project
  • Works with any Python packaging tool
  • No external files needed

Method 3: setup.cfg (Legacy Projects)

For projects using setup.cfg:

[metadata]name = myproject
version = 1.0.0
...
[pfix]model = openrouter/qwen/qwen3-coder-next
auto_apply = true
auto_install_deps = true
auto_restart = false
max_retries = 3
create_backups = true

Method 4: setup.py (Legacy Projects)

For projects using setup.py:

fromsetuptoolsimportsetupsetup(
name="myproject",
version="1.0.0",
# ... other setup args# pfix configurationpfix_model="openrouter/qwen/qwen3-coder-next",
pfix_auto_apply=True,
pfix_auto_install_deps=True,
pfix_auto_restart=False,
pfix_max_retries=3,
pfix_create_backups=True,
pfix_enabled=True,
)

Note:setup.py config requires pfix to be installed in the same environment.

Method 5: Programmatic Configuration

Configure at runtime in your Python code:

frompfiximportconfigure# Before importing pfix or using the hookconfigure(
# LLM settingsllm_model="openrouter/qwen/qwen3-coder-next",
llm_api_key="sk-or-v1-...",
llm_temperature=0.2,
llm_max_tokens=4096,
# Behaviorauto_apply=True,
auto_install_deps=True,
auto_restart=True,
max_retries=3,
enabled=True,
dry_run=False,
# Projectpkg_manager="uv",
create_backups=False,
project_root="/path/to/project",
# Gitgit_auto_commit=False,
git_commit_prefix="pfix: ",
# MCPmcp_enabled=False,
mcp_transport="stdio",
)
### Configuration Reference|Variable|Type|Default|Description||----------|------|---------|-------------||`OPENROUTER_API_KEY`|`str`||**Required**OpenRouterAPIkey||`PFIX_MODEL`|`str`|`openrouter/qwen/qwen3-coder-next`|LLMmodeltouse||`PFIX_API_BASE`|`str`|`https://openrouter.ai/api/v1`|APIbaseURL||`PFIX_AUTO_APPLY`|`bool`|`false`|Auto-applyfixeswithoutconfirmation||`PFIX_AUTO_INSTALL_DEPS`|`bool`|`true`|Auto-installmissingdependencies||`PFIX_AUTO_RESTART`|`bool`|`false`|Restartprocessafterfixapplied||`PFIX_MAX_RETRIES`|`int`|`3`|Maxfixattemptspererror||`PFIX_DRY_RUN`|`bool`|`false`|Showproposedfixeswithoutapplying||`PFIX_CREATE_BACKUPS`|`bool`|`true`|Create`.pfix_backups/`beforefixing||`PFIX_ENABLED`|`bool`|`true`|Masterswitchtodisablepfix||`PFIX_PKG_MANAGER`|`str`|auto|`pip`, `uv`, orauto-detected||`PFIX_GIT_COMMIT`|`bool`|`false`|Auto-commitfixestogit||`PFIX_GIT_PREFIX`|`str`|`pfix: `|Gitcommitmessageprefix||`PFIX_MCP_ENABLED`|`bool`|`false`|EnableMCPserver||`PFIX_MCP_TRANSPORT`|`str`|`stdio`|`stdio`or`http`||`PFIX_PROJECT_ROOT`|`str`|`.`|Projectrootforrelativepaths|## LLM Models & Providerspfixuses [LiteLLM](https://litellm.ai) tosupportmultipleLLMproviders. YoucanusecloudAPIsorrunmodelslocally.
### OpenRouter (Cloud - Recommended)OpenRouterprovidesaccesstomultiplemodelswithasingleAPIkey.
```bash# .envOPENROUTER_API_KEY=sk-or-v1-...
PFIX_MODEL=openrouter/qwen/qwen3-coder-next

Recommended models:

ModelDescriptionBest For
openrouter/qwen/qwen3-coder-nextClaude 4 SonnetBalanced quality/speed
openrouter/anthropic/claude-opus-4Claude 4 OpusComplex fixes
openrouter/anthropic/claude-haiku-4Claude 4 HaikuFast, cheap fixes
openrouter/qwen/qwen3-235b-a22b-2507Qwen3 235BCode-heavy tasks
openrouter/qwen/qwen3.5-flash-02-23Qwen3.5 FlashFast responses
openrouter/nvidia/nemotron-3-super-120b-a12b:freeNemotron 3 SuperFree tier
openrouter/deepseek/deepseek-coder-v2DeepSeek CoderCode-specific

Ollama (Local - Free, Private)

Run models locally for zero cost and complete privacy.

Setup:

# Pull a code-capable model
ollama pull codellama:7b
ollama pull qwen2.5-coder:7b
ollama pull deepseek-coder:6.7b

Configure pfix:

# .env
PFIX_MODEL=ollama/codellama:7b
PFIX_API_BASE=http://localhost:11434
# No API key needed for local models

Recommended local models:

ModelSizeSpeedQuality
ollama/codellama:7b7BFastGood
ollama/qwen2.5-coder:7b7BFastVery Good
ollama/deepseek-coder:6.7b6.7BFastVery Good
ollama/codellama:13b13BMediumExcellent
ollama/qwen2.5-coder:14b14BMediumExcellent

.env

PFIX_MODEL=gpt-4o PFIX_API_KEY=sk-... PFIX_API_BASE=https://api.openai.com/v1


**Models:** `gpt-4o`, `gpt-5.4-mini`, `gpt-4-turbo`, `gpt-3.5-turbo`
# .env
PFIX_MODEL=anthropic/claude-3-sonnet-20241022
PFIX_API_KEY=sk-ant-...

Models:claude-3-opus, claude-3-sonnet, claude-3-haiku

.env

PFIX_MODEL=azure/ PFIX_API_KEY=... PFIX_API_BASE=https://.openai.azure.com


# .env
PFIX_MODEL=vertex_ai/gemini-1.5-pro
# or
PFIX_MODEL=gemini/gemini-1.5-pro

Choosing a Model

For beginners: Start with openrouter/qwen/qwen3-coder-next (good balance)

For cost savings: Use Ollama locally or OpenRouter free models (:free suffix)

For complex fixes: Use larger models (Claude Opus, GPT-4, Qwen 235B)

For speed: Use smaller models (Haiku, GPT-4o-mini, local 7B models)

Runtime Error Tracking

pfix can automatically capture runtime errors to TODO.md for production monitoring:

[tool.pfix.runtime_todo]
enabled = truetodo_file = "TODO.md"min_severity = "medium"max_entries = 500deduplicate = trueinclude_local_vars = falseinclude_traceback_depth = 5

Features:

  • Absolute paths — errors tracked with full file paths
  • Deduplication — same error 1000x = one entry with counter
  • Full traceback — complete call stack, not just last frame
  • Environment context — Python version, hostname, PID, venv path
  • Thread-safe — file locking for multi-worker setups (gunicorn/uvicorn)
  • Append-only — never loses history

Enable via environment:

PFIX_RUNTIME_TODO=true
PFIX_TODO_FILE=TODO.md

Environment Diagnostics

Comprehensive environment checking with 14 diagnostic categories:

pfix diagnose # Run all diagnostics
pfix diagnose --category venv,memory,network
pfix diagnose --json --check # CI mode (exit 1 on errors)
pfix diagnose --fix # Auto-fix what can be fixed

Categories:

CategoryChecks
import_dependencyMissing imports, circular deps, version conflicts, stdlib shadowing
filesystemDisk space, permissions, broken symlinks, large files
venvActivation, integrity, global leaks, requirements sync
python_versionpyproject.toml requires-python, deprecated features
memoryAvailable RAM, swap, recursion limit, GC pressure
networkDNS, connectivity, SSL certs, proxy config
processulimits, signals, zombie processes
encodingUTF-8 BOM, line endings, locale
pathssys.path issues, PYTHONPATH, long paths
config_env.env security, required vars, gitignore
concurrencyThread count, asyncio loop issues
serializationPickle protocol, corrupt cache files
hardwareGPU availability, CPU count, Docker limits
third_partyAPI rate limits, auth expiration, schema changes

Auto-fixable issues:

  • Stale .pyc files → find . -name '*.pyc' -delete
  • UTF-8 BOM → remove BOM markers
  • Mixed line endings → convert to LF
  • Missing .env → copy from .env.example
  • .env not gitignored → add to .gitignore
  • Large log files → truncate to last N lines

Usage in CI/CD:

- run: pfix diagnose --check --jsoncontinue-on-error: true

Custom Error Handlers

frompfiximportpfixdeflog_error(exc):
withopen("errors.log", "a") asf:
f.write(f"{type(exc).__name__}: {exc}\n")
@pfix(on_error=log_error, retries=3)defrisky_operation():
...

Async Support

frompfiximportapfix@apfix(auto_apply=True)asyncdeffetch_data(url):
importaiohttpasyncwithaiohttp.ClientSession() assession:
asyncwithsession.get(url) asresponse:
returnawaitresponse.json()

Session with Custom Config

frompfiximportpfix_session, get_configconfig=get_config()
config.llm_model="openrouter/anthropic/claude-opus-4"config.llm_temperature=0.1withpfix_session(__file__, auto_apply=True, restart=True):
main()

Examples

See examples/ directory for working examples:

Best Practices

  1. Start with PFIX_AUTO_APPLY=false to review fixes before applying
  2. Enable backups (PFIX_CREATE_BACKUPS=true) in development
  3. Use dry-run mode in CI/CD to preview fixes without applying
  4. Set PFIX_AUTO_RESTART=true for long-running processes
  5. Add hints to decorators for better LLM context: @pfix(hint="Processes CSV files")

"LLM confidence too low"

  • Increase context with @pfix(hint="...")
  • Check your API key is valid
  • Try a different model in PFIX_MODEL

"Could not locate function"

  • Ensure the error is in a named function (not lambda or exec)
  • Use pfix_session for module-level code

Backups filling up disk

  • Set PFIX_CREATE_BACKUPS=false
  • Clean .pfix_backups/ periodically: rm -rf .pfix_backups/

Too many restarts

  • Set PFIX_MAX_RETRIES=1 to limit attempts
  • Use PFIX_AUTO_RESTART=false to disable restarts

Dependencies

PackageRole
litellmLLM proxy — OpenRouter, OpenAI, Anthropic, Ollama
python-dotenvLoad .env configuration
richTerminal UI (diffs, panels, tables)
pipreqsProject import scanning
pathspecGitignore-aware file filtering
mcpFastMCP server (optional)
gitpythonGit auto-commit (optional)
watchdogFile change watching (optional)

License

Licensed under Apache-2.0.

Status

Last updated by taskill at 2026-04-25 13:42 UTC

MetricValue
HEAD6ddd51f
Coverage
Failing tests
Commits in last cycle50

Introduced a deep code analysis engine and a configuration management system, plus a number of documentation/refactor updates and CLI/config fixes across the project.

About

Self-healing Python — catches runtime errors, fixes code & dependencies via LLM + MCP

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages