shadow-debugger 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- shadow_debugger/__init__.py +293 -0
- shadow_debugger/_version.py +1 -0
- shadow_debugger/analyzer.py +108 -0
- shadow_debugger/config.py +56 -0
- shadow_debugger/core.py +244 -0
- shadow_debugger/inspector.py +176 -0
- shadow_debugger/integrations/__init__.py +224 -0
- shadow_debugger/patch_check.py +57 -0
- shadow_debugger/providers/__init__.py +23 -0
- shadow_debugger/providers/anthropic.py +54 -0
- shadow_debugger/providers/base.py +92 -0
- shadow_debugger/providers/gemini.py +66 -0
- shadow_debugger/providers/ollama.py +65 -0
- shadow_debugger/providers/openai.py +63 -0
- shadow_debugger/py.typed +1 -0
- shadow_debugger/redaction.py +190 -0
- shadow_debugger/reporter.py +128 -0
- shadow_debugger/throttler.py +64 -0
- shadow_debugger-0.1.0.dist-info/METADATA +292 -0
- shadow_debugger-0.1.0.dist-info/RECORD +23 -0
- shadow_debugger-0.1.0.dist-info/WHEEL +5 -0
- shadow_debugger-0.1.0.dist-info/licenses/LICENSE +21 -0
- shadow_debugger-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Deduplicates identical errors so the LLM is not called repeatedly for the same bug."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
import threading
|
|
7
|
+
import time
|
|
8
|
+
from collections import OrderedDict
|
|
9
|
+
from typing import Any, Callable, Dict, Tuple
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ErrorThrottler:
|
|
13
|
+
"""Thread-safe, memory-bounded (LRU) cooldown tracker keyed by error fingerprint."""
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
cooldown_seconds: float = 300,
|
|
18
|
+
max_entries: int = 1000,
|
|
19
|
+
clock: Callable[[], float] = time.monotonic,
|
|
20
|
+
) -> None:
|
|
21
|
+
self.cooldown_seconds = cooldown_seconds
|
|
22
|
+
self.max_entries = max_entries
|
|
23
|
+
self._clock = clock
|
|
24
|
+
self._lock = threading.Lock()
|
|
25
|
+
# fingerprint -> [last_analyzed_timestamp, occurrence_count]
|
|
26
|
+
self._seen: "OrderedDict[str, list]" = OrderedDict()
|
|
27
|
+
|
|
28
|
+
@staticmethod
|
|
29
|
+
def compute_fingerprint(error_context: Dict[str, Any]) -> str:
|
|
30
|
+
if error_context.get("fingerprint"):
|
|
31
|
+
return str(error_context["fingerprint"])
|
|
32
|
+
exc_type = str(error_context.get("exception_type", ""))
|
|
33
|
+
filepath = str(error_context.get("file", ""))
|
|
34
|
+
lineno = str(error_context.get("line", ""))
|
|
35
|
+
raw_key = f"{exc_type}:{filepath}:{lineno}"
|
|
36
|
+
return hashlib.sha256(raw_key.encode("utf-8")).hexdigest()[:16]
|
|
37
|
+
|
|
38
|
+
def hit(self, fingerprint: str) -> Tuple[bool, int]:
|
|
39
|
+
"""Record an occurrence. Returns ``(should_analyze, occurrence_count)``."""
|
|
40
|
+
now = self._clock()
|
|
41
|
+
with self._lock:
|
|
42
|
+
record = self._seen.get(fingerprint)
|
|
43
|
+
if record is None:
|
|
44
|
+
self._seen[fingerprint] = [now, 1]
|
|
45
|
+
if len(self._seen) > self.max_entries:
|
|
46
|
+
self._seen.popitem(last=False)
|
|
47
|
+
return True, 1
|
|
48
|
+
|
|
49
|
+
self._seen.move_to_end(fingerprint)
|
|
50
|
+
record[1] += 1
|
|
51
|
+
if now - record[0] >= self.cooldown_seconds:
|
|
52
|
+
record[0] = now
|
|
53
|
+
return True, record[1]
|
|
54
|
+
return False, record[1]
|
|
55
|
+
|
|
56
|
+
def should_analyze(self, error_context: Dict[str, Any]) -> Tuple[bool, str, int]:
|
|
57
|
+
"""Check and update cooldown state for ``error_context``."""
|
|
58
|
+
fingerprint = self.compute_fingerprint(error_context)
|
|
59
|
+
allowed, count = self.hit(fingerprint)
|
|
60
|
+
return allowed, fingerprint, count
|
|
61
|
+
|
|
62
|
+
def __len__(self) -> int:
|
|
63
|
+
with self._lock:
|
|
64
|
+
return len(self._seen)
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: shadow-debugger
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Non-blocking AI-powered background error diagnosis and root-cause analysis for Python applications and web APIs.
|
|
5
|
+
Author: GaspareDev
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/GaspareDev/shadow-debugger
|
|
8
|
+
Project-URL: Repository, https://github.com/GaspareDev/shadow-debugger
|
|
9
|
+
Project-URL: Issues, https://github.com/GaspareDev/shadow-debugger/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/GaspareDev/shadow-debugger/blob/main/CHANGELOG.md
|
|
11
|
+
Keywords: debugging,error-tracking,observability,llm,ai,gemini,ollama,openai,anthropic,fastapi,flask,django
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Software Development :: Bug Tracking
|
|
23
|
+
Classifier: Topic :: Software Development :: Debuggers
|
|
24
|
+
Classifier: Topic :: System :: Monitoring
|
|
25
|
+
Classifier: Typing :: Typed
|
|
26
|
+
Requires-Python: >=3.9
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Provides-Extra: gemini
|
|
30
|
+
Requires-Dist: google-genai>=2.22.0; extra == "gemini"
|
|
31
|
+
Provides-Extra: openai
|
|
32
|
+
Requires-Dist: openai>=1.50.0; extra == "openai"
|
|
33
|
+
Provides-Extra: anthropic
|
|
34
|
+
Requires-Dist: anthropic>=0.40.0; extra == "anthropic"
|
|
35
|
+
Provides-Extra: fastapi
|
|
36
|
+
Requires-Dist: fastapi>=0.100.0; extra == "fastapi"
|
|
37
|
+
Provides-Extra: flask
|
|
38
|
+
Requires-Dist: flask>=2.2.0; extra == "flask"
|
|
39
|
+
Requires-Dist: blinker>=1.6.0; extra == "flask"
|
|
40
|
+
Provides-Extra: django
|
|
41
|
+
Requires-Dist: django>=4.2; extra == "django"
|
|
42
|
+
Provides-Extra: all
|
|
43
|
+
Requires-Dist: google-genai>=2.22.0; extra == "all"
|
|
44
|
+
Requires-Dist: openai>=1.50.0; extra == "all"
|
|
45
|
+
Requires-Dist: anthropic>=0.40.0; extra == "all"
|
|
46
|
+
Requires-Dist: fastapi>=0.100.0; extra == "all"
|
|
47
|
+
Requires-Dist: flask>=2.2.0; extra == "all"
|
|
48
|
+
Requires-Dist: blinker>=1.6.0; extra == "all"
|
|
49
|
+
Provides-Extra: dev
|
|
50
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
51
|
+
Requires-Dist: httpx>=0.27.0; extra == "dev"
|
|
52
|
+
Requires-Dist: ruff>=0.6.0; extra == "dev"
|
|
53
|
+
Requires-Dist: mypy>=1.11.0; extra == "dev"
|
|
54
|
+
Dynamic: license-file
|
|
55
|
+
|
|
56
|
+
# shadow-debugger
|
|
57
|
+
|
|
58
|
+
[](LICENSE)
|
|
59
|
+
[](https://www.python.org/downloads/)
|
|
60
|
+
|
|
61
|
+
Background error diagnosis for Python apps and web APIs.
|
|
62
|
+
|
|
63
|
+
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/`.
|
|
64
|
+
|
|
65
|
+
It works offline with **Ollama** (zero external dependencies) or with **Gemini**, **OpenAI** (including OpenAI-compatible endpoints like Groq, vLLM, LM Studio), and **Anthropic**.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## What a report looks like
|
|
70
|
+
|
|
71
|
+
Given a crashing endpoint:
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
@app.get("/checkout/{order_id}")
|
|
75
|
+
async def checkout(order_id: int, discount_items: int = 0):
|
|
76
|
+
unit_discount = 50 / discount_items
|
|
77
|
+
return {"order_id": order_id, "discount": unit_discount}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
`shadow-debugger` writes `shadow_reports/incident_20261018_103012_ZeroDivisionError_9f2a1c4b.md`:
|
|
81
|
+
|
|
82
|
+
````markdown
|
|
83
|
+
# Shadow Debugger Incident Report
|
|
84
|
+
|
|
85
|
+
**Timestamp:** 2026-10-18T10:30:12.418290+00:00
|
|
86
|
+
**Exception:** `ZeroDivisionError`: division by zero
|
|
87
|
+
**Location:** `app/routes.py:42` in `checkout`
|
|
88
|
+
**Fingerprint:** `9f2a1c4b8e1032d1` (occurrences so far: 1)
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
### 1. Root Cause Summary
|
|
93
|
+
`checkout()` divides `50` by `discount_items` without checking for `0`. When the query parameter is omitted or set to `0`, Python raises `ZeroDivisionError`.
|
|
94
|
+
|
|
95
|
+
### 2. Trigger Condition
|
|
96
|
+
`GET /checkout/1042?discount_items=0` with local state `{"order_id": 1042, "discount_items": 0}`.
|
|
97
|
+
|
|
98
|
+
### 3. Proposed Fix
|
|
99
|
+
```python
|
|
100
|
+
unit_discount = 50 / discount_items if discount_items > 0 else 0.0
|
|
101
|
+
```
|
|
102
|
+
```diff
|
|
103
|
+
- unit_discount = 50 / discount_items
|
|
104
|
+
+ unit_discount = 50 / discount_items if discount_items > 0 else 0.0
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### 4. Prevention Advice
|
|
108
|
+
Validate numeric query parameters with `Query(gt=0)` in FastAPI or guard denominators before dividing.
|
|
109
|
+
````
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Installation
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
# Core package (stdlib only — works with local Ollama out of the box)
|
|
117
|
+
pip install shadow-debugger
|
|
118
|
+
|
|
119
|
+
# Or install with the provider / framework you use:
|
|
120
|
+
pip install "shadow-debugger[gemini]"
|
|
121
|
+
pip install "shadow-debugger[openai]"
|
|
122
|
+
pip install "shadow-debugger[anthropic]"
|
|
123
|
+
pip install "shadow-debugger[fastapi]"
|
|
124
|
+
pip install "shadow-debugger[flask]"
|
|
125
|
+
pip install "shadow-debugger[django]"
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Quick start
|
|
131
|
+
|
|
132
|
+
### Local with Ollama (no API key)
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
import shadow_debugger
|
|
136
|
+
|
|
137
|
+
shadow_debugger.init(
|
|
138
|
+
provider="ollama",
|
|
139
|
+
model="llama3.2",
|
|
140
|
+
print_to_console=True,
|
|
141
|
+
)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Cloud providers
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
import shadow_debugger
|
|
148
|
+
|
|
149
|
+
# Uses GEMINI_API_KEY
|
|
150
|
+
shadow_debugger.init(provider="gemini", model="gemini-3.8-flash")
|
|
151
|
+
|
|
152
|
+
# Uses OPENAI_API_KEY (or pass base_url=... for Groq / vLLM / LM Studio)
|
|
153
|
+
shadow_debugger.init(provider="openai", model="gpt-5.4-nano")
|
|
154
|
+
|
|
155
|
+
# Uses ANTHROPIC_API_KEY
|
|
156
|
+
shadow_debugger.init(provider="anthropic", model="claude-haiku-5-5")
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Web frameworks
|
|
162
|
+
|
|
163
|
+
### FastAPI / Starlette
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
from fastapi import FastAPI
|
|
167
|
+
import shadow_debugger
|
|
168
|
+
|
|
169
|
+
shadow_debugger.init(provider="ollama", model="llama3.2")
|
|
170
|
+
|
|
171
|
+
app = FastAPI()
|
|
172
|
+
app.add_middleware(shadow_debugger.FastAPIWatcherMiddleware)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Flask
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
from flask import Flask
|
|
179
|
+
import shadow_debugger
|
|
180
|
+
|
|
181
|
+
app = Flask(__name__)
|
|
182
|
+
shadow_debugger.init(provider="ollama", model="llama3.2")
|
|
183
|
+
shadow_debugger.init_flask(app)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
`init_flask` hooks into `got_request_exception`, so normal 404/4xx responses are left untouched.
|
|
187
|
+
|
|
188
|
+
### Django
|
|
189
|
+
|
|
190
|
+
In `settings.py`:
|
|
191
|
+
|
|
192
|
+
```python
|
|
193
|
+
MIDDLEWARE = [
|
|
194
|
+
"shadow_debugger.integrations.DjangoWatcherMiddleware",
|
|
195
|
+
# ...
|
|
196
|
+
]
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Decorator & manual capture
|
|
202
|
+
|
|
203
|
+
`@watch` works on both regular and `async def` functions:
|
|
204
|
+
|
|
205
|
+
```python
|
|
206
|
+
import shadow_debugger
|
|
207
|
+
|
|
208
|
+
@shadow_debugger.watch(reraise=False)
|
|
209
|
+
async def process_order(payload: dict):
|
|
210
|
+
return payload["items"]["price"] / payload["items"]["quantity"]
|
|
211
|
+
|
|
212
|
+
# Or inside an existing try/except block:
|
|
213
|
+
try:
|
|
214
|
+
sync_inventory()
|
|
215
|
+
except Exception as exc:
|
|
216
|
+
shadow_debugger.capture_exception(exc, extra_metadata={"job": "inventory"})
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Privacy & what gets sent
|
|
222
|
+
|
|
223
|
+
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:
|
|
224
|
+
|
|
225
|
+
- **Variable & header names** matching common secret names (`password`, `secret`, `token`, `api_key`, `authorization`, `cookie`, etc.) are replaced with `[REDACTED]`.
|
|
226
|
+
- **String values** matching JWTs, API keys (`sk-...`, `AIza...`, `AKIA...`, `ghp_...`), `Bearer` tokens, PEM private keys, and URL query tokens (`?access_token=...`) are scrubbed across variables, tracebacks, and exception messages.
|
|
227
|
+
- **Lazy objects** (like Django `QuerySet`s) are inspected via `__dict__` without calling `repr()`/`str()`, so capturing an error won't trigger extra database queries.
|
|
228
|
+
- You can turn off parts of the payload with `send_locals=False` or `send_source_code=False`, add your own regexes via `redact_keys=[...]` and `redact_patterns=[...]`, or run everything locally with `provider="ollama"`.
|
|
229
|
+
- Proposed code patches are only checked with `ast.parse()` — generated code is never executed.
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Configuration
|
|
234
|
+
|
|
235
|
+
All options can be passed to `shadow_debugger.init(...)`:
|
|
236
|
+
|
|
237
|
+
| Option | Default | Description |
|
|
238
|
+
| :--- | :--- | :--- |
|
|
239
|
+
| `provider` | `"gemini"` | `"gemini"`, `"ollama"`, `"openai"`, `"anthropic"`, or a `Provider` instance. |
|
|
240
|
+
| `model` | `None` | Model name. Uses the provider default if omitted. |
|
|
241
|
+
| `api_key` | `None` | API key (falls back to `GEMINI_API_KEY`, `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`). |
|
|
242
|
+
| `base_url` | `None` | Custom URL for Ollama (`OLLAMA_HOST`) or OpenAI-compatible servers. |
|
|
243
|
+
| `output_dir` | `"./shadow_reports"` | Folder for Markdown reports (`None` disables file output). |
|
|
244
|
+
| `print_to_console` | `False` | Print diagnoses to `stderr` (handy in local dev). |
|
|
245
|
+
| `cooldown_seconds` | `300.0` | Minimum seconds before re-analyzing the same error at the same line. |
|
|
246
|
+
| `send_source_code` | `True` | Include source lines around the crash. |
|
|
247
|
+
| `send_locals` | `True` | Include redacted local variables. |
|
|
248
|
+
| `redact_keys` | `()` | Extra regexes for variable/key names to redact. |
|
|
249
|
+
| `redact_patterns` | `()` | Extra regexes for secret value patterns to scrub. |
|
|
250
|
+
| `request_timeout` | `60.0` | Timeout in seconds for LLM calls. |
|
|
251
|
+
| `shutdown_timeout` | `10.0` | Seconds to wait on process exit for pending reports to finish. |
|
|
252
|
+
| `max_queue_size` | `100` | Max queued incidents before dropping new ones. |
|
|
253
|
+
| `catch_uncaught` | `True` | Hook `sys.excepthook` and `threading.excepthook`. |
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## Writing a custom provider
|
|
258
|
+
|
|
259
|
+
```python
|
|
260
|
+
import shadow_debugger
|
|
261
|
+
from shadow_debugger import Provider, register_provider
|
|
262
|
+
|
|
263
|
+
@register_provider("my-gateway")
|
|
264
|
+
class MyGatewayProvider(Provider):
|
|
265
|
+
default_model = "my-model"
|
|
266
|
+
env_key = "MY_GATEWAY_KEY"
|
|
267
|
+
|
|
268
|
+
def complete(self, system: str, prompt: str) -> str:
|
|
269
|
+
self.check_configuration()
|
|
270
|
+
# Call your LLM endpoint and return Markdown
|
|
271
|
+
return "..."
|
|
272
|
+
|
|
273
|
+
shadow_debugger.init(provider="my-gateway")
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## Roadmap & contributing
|
|
279
|
+
|
|
280
|
+
I'm actively looking for feedback and contributors. A few areas where PRs are very welcome:
|
|
281
|
+
|
|
282
|
+
- [ ] Webhook / notification sinks (Slack, Discord, Telegram, GitHub Issues)
|
|
283
|
+
- [ ] Additional providers (Mistral, LiteLLM, AWS Bedrock)
|
|
284
|
+
- [ ] Structured JSON output option alongside Markdown reports
|
|
285
|
+
- [ ] Celery / background worker task integration
|
|
286
|
+
- [ ] CLI command to browse and filter local reports in `./shadow_reports`
|
|
287
|
+
|
|
288
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for local setup instructions.
|
|
289
|
+
|
|
290
|
+
## License
|
|
291
|
+
|
|
292
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
shadow_debugger/__init__.py,sha256=VUITE75G6KBvXZ3RbIhnEMVnVNFF3Xj8582rwf0cDZ4,9366
|
|
2
|
+
shadow_debugger/_version.py,sha256=kUR5RAFc7HCeiqdlX36dZOHkUI5wI6V_43RpEcD8b-0,22
|
|
3
|
+
shadow_debugger/analyzer.py,sha256=0g9IBoTUORJ2yb-Gr9BmAR9MJYAD2wg7RZXACC39Kg4,4049
|
|
4
|
+
shadow_debugger/config.py,sha256=Osh37V4SPZ5u88c9T1PeZ-4dx04fL4YEiPOMBEubgCg,1847
|
|
5
|
+
shadow_debugger/core.py,sha256=99J79AsAVsEoQRshBhzWiNvvchC8OyVQJH99jWM2TlE,9439
|
|
6
|
+
shadow_debugger/inspector.py,sha256=8ixEwvG2FtQiMMv6x-Ny6Du9AWvLFenGLHv0CyBiBIY,6294
|
|
7
|
+
shadow_debugger/patch_check.py,sha256=sy6mtJk6pNWrtdDkR64SrrGvvAP_o04uOgADwF3pIsM,1837
|
|
8
|
+
shadow_debugger/py.typed,sha256=ZSegze1ahS5tlaVlEVAF0HG-jTkkScOwTjUnglYQOS8,22
|
|
9
|
+
shadow_debugger/redaction.py,sha256=CwQQVO65EJl2IcwiGbb1M2eW5QHojxoFZ0kv558SRsI,6555
|
|
10
|
+
shadow_debugger/reporter.py,sha256=TawWb-Vh7jV_6kKPpthqD0vRPhD_44pT0Yax6bU1rAU,5485
|
|
11
|
+
shadow_debugger/throttler.py,sha256=EOPC_Z1ikbFGRBf0sjSc7TojuKgtqs3IL1h-JGyTrYc,2403
|
|
12
|
+
shadow_debugger/integrations/__init__.py,sha256=0tv-_AU5v7PEmhqZbUvMESL7TmllNvhKRu__6v1hkNM,7565
|
|
13
|
+
shadow_debugger/providers/__init__.py,sha256=R7MPArkRWxcu-NyhGcgbq77f3Lgg9ow-nPDrGOiD30c,607
|
|
14
|
+
shadow_debugger/providers/anthropic.py,sha256=R79gClpjwHz6j6w5ELzHfZtTUf2G0NzINYoRfP1mq_s,2031
|
|
15
|
+
shadow_debugger/providers/base.py,sha256=W66umF4drjd_MX9GAhzi3UPCNUWSFtFBUNlzLNlD67I,3071
|
|
16
|
+
shadow_debugger/providers/gemini.py,sha256=CUNJspEQsTZQ6rQ9SP4TmU3euABbXEBpchECKY7_9wk,2573
|
|
17
|
+
shadow_debugger/providers/ollama.py,sha256=Kn037q78PreDqUBYMIoFhxZkDQhxRXHGP-Zg_7-rvoQ,2378
|
|
18
|
+
shadow_debugger/providers/openai.py,sha256=Qqakyt9__nTmE1qG6oDr6ITa9c3xiBwegUDQkfxu6LA,2223
|
|
19
|
+
shadow_debugger-0.1.0.dist-info/licenses/LICENSE,sha256=DTl2sA7c-JVs06AF-1nxjdmE4Xycbe4GlcdpO-HP_o4,1067
|
|
20
|
+
shadow_debugger-0.1.0.dist-info/METADATA,sha256=wbD7bncP3VchW6a8WXXf9I8hsQ1Hp6UsZI2qcw3-tj8,10161
|
|
21
|
+
shadow_debugger-0.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
22
|
+
shadow_debugger-0.1.0.dist-info/top_level.txt,sha256=uydI_u61yAjtl6_x4rkzQ9Z-RV5QyH0Jn24bq5Rc680,16
|
|
23
|
+
shadow_debugger-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 GaspareDev
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
shadow_debugger
|