返回资讯中心
外部精选
软件工程
#开发者实时流

Shadow-debugger – Background LLM error diagnosis for Python and FastAPI

Background error diagnosis for Python apps and web APIs.

Hacker News 最新GaspareDev2026/10/18 · 19 天前11 分钟阅读

以下正文同步自 Hacker News 最新,版权归原站所有,已转换为易读排版。

Background error diagnosis for Python apps and web APIs.

When an exception or HTTP 500 happens, shadow-debugger captures the stack frame from your code, strips secrets from local variables and request data, and sends the context to an LLM in a background worker thread. Your app returns its normal response right away; a few seconds later you get a Markdown postmortem in ./shadow_reports/.

It works offline with Ollama (zero external dependencies) or with Gemini, OpenAI (including OpenAI-compatible endpoints like Groq, vLLM, LM Studio), and Anthropic.

@app.get("/checkout/{order_id}") async def checkout(order_id: int, discount_items: int = 0): unit_discount = 50 / discount_items return {"order_id": order_id, "discount": unit_discount}

shadow-debugger writes shadow_reports/incident_20261018_103012_ZeroDivisionError_9f2a1c4b.md:

Shadow Debugger Incident Report

Timestamp: 2026-10-18T10:30:12.418290+00:00 Exception: ZeroDivisionError: division by zero Location: app/routes.py:42 in checkout Fingerprint: 9f2a1c4b8e1032d1 (occurrences so far: 1)


1. Root Cause Summary

checkout() divides 50 by discount_items without checking for 0. When the query parameter is omitted or set to 0, Python raises ZeroDivisionError.

2. Trigger Condition

GET /checkout/1042?discount_items=0 with local state {"order_id": 1042, "discount_items": 0}.

3. Proposed Fix

unit_discount = 50 / discount_items if discount_items > 0 else 0.0
- unit_discount = 50 / discount_items
+ unit_discount = 50 / discount_items if discount_items > 0 else 0.0

4. Prevention Advice

Validate numeric query parameters with Query(gt=0) in FastAPI or guard denominators before dividing.

Installation

Core package (stdlib only — works with local Ollama out of the box)

pip install shadow-debugger

Or install with the provider / framework you use:

pip install "shadow-debugger[gemini]" pip install "shadow-debugger[openai]" pip install "shadow-debugger[anthropic]" pip install "shadow-debugger[fastapi]" pip install "shadow-debugger[flask]" pip install "shadow-debugger[django]"

Quick start

Local with Ollama (no API key)

import shadow_debugger

shadow_debugger.init( provider="ollama", model="llama3.2", print_to_console=True, )

Cloud providers

import shadow_debugger

Uses GEMINI_API_KEY

shadow_debugger.init(provider="gemini", model="gemini-3.8-flash")

Uses OPENAI_API_KEY (or pass base_url=... for Groq / vLLM / LM Studio)

shadow_debugger.init(provider="openai", model="gpt-5.4-nano")

Uses ANTHROPIC_API_KEY

shadow_debugger.init(provider="anthropic", model="claude-haiku-5-5")

Web frameworks

FastAPI / Starlette

from fastapi import FastAPI import shadow_debugger

shadow_debugger.init(provider="ollama", model="llama3.2")

app = FastAPI() app.add_middleware(shadow_debugger.FastAPIWatcherMiddleware)

Flask

from flask import Flask import shadow_debugger

app = Flask(name) shadow_debugger.init(provider="ollama", model="llama3.2") shadow_debugger.init_flask(app)

init_flask hooks into got_request_exception, so normal 404/4xx responses are left untouched.

MIDDLEWARE = [ "shadow_debugger.integrations.DjangoWatcherMiddleware",

...

]

Decorator & manual capture

@watch works on both regular and async def functions:

import shadow_debugger

@shadow_debugger.watch(reraise=False) async def process_order(payload: dict): return payload["items"]["price"] / payload["items"]["quantity"]

Or inside an existing try/except block:

try: sync_inventory() except Exception as exc: shadow_debugger.capture_exception(exc, extra_metadata={"job": "inventory"})

Privacy & what gets sent

If you use a cloud provider, the prompt includes the traceback, the 5 lines of source around the error, and the local variables of the failing function. To keep sensitive data out of prompts:

All options can be passed to shadow_debugger.init(...):

import shadow_debugger from shadow_debugger import Provider, register_provider

@register_provider("my-gateway") class MyGatewayProvider(Provider): default_model = "my-model" env_key = "MY_GATEWAY_KEY"

def complete(self, system: str, prompt: str) -> str: self.check_configuration()

Call your LLM endpoint and return Markdown

return "..."

shadow_debugger.init(provider="my-gateway")

Roadmap & contributing

I'm actively looking for feedback and contributors. A few areas where PRs are very welcome:

See CONTRIBUTING.md for local setup instructions.

正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。

打开原文
去社区讨论 →