Skip to content

Commit aee8110

Browse files
maltsev-devclaude
andauthored
feat(sdk): deprecation sweep + handle→guard + init/init_or_die unification (0.18.5 candidate) (#111)
* updv1 * docs(runtime): strip legacy/pre-fix/post-fix framing - Remove "pre-fix", "post-fix", "legacy", "back-compat", "previously did", "was removed", "v3.53 audit" framing from inline comments and docstrings. - Keep all audit tracking IDs (DEF-*, AUTH-01, ADR-*) — those refer to external systems and are not legacy framing per se. - runtime.py -565 lines net (-311 lines of redundant context). 17 files modified; no functional changes. - pytest 1405 passed (1 skipped) with -W error::DeprecationWarning. - Prod smoke @Protect → /execute round-trip ALLOW in 302ms. * refactor(sdk): strip legacy pre/post-fix framing from docstrings Removes 'pre-fix/post-fix', 'used to', 'previously', 'now we', and audit-cycle prose from runtime.py, transport.py, decorators.py, and 14 other modules. External ticket identifiers (DEF-*, ADR-*, AUDIT P*, IDEM-01, CLOSE-ORPHAN) are preserved as anchors. What stays: ticket refs, ADR numbers, date stamps that tag fixes without prose framing. What goes: 'pre-fix ... pre-fix this method ... Now we' blocks, 'audit ... previous version ... the prior code' commentary, 'was ... is now' chronology in docstrings. 1405 tests pass; -W error::DeprecationWarning clean. Co-Authored-By: Claude Code <noreply@anthropic.com> * refactor(sdk): drop framework extras + auto_instrument public surface NullRun is a runtime decision layer, not a framework integration library. The framework auto-instrumentation patches (langchain, crewai, autogen, langgraph, llama-index, openai-agents) remain in code as silent auto-detect hooks, but are no longer: - advertised in pyproject.toml as installable extras - documented in README as a feature - exported from the top-level `nullrun` namespace User-facing install is exactly one line: pip install nullrun Public surface cleanup: - removed `auto_instrument` / `is_auto_instrumented` from _LAZY_EXPORTS (was internal trigger, not user API) - removed module-level `track_event` alias (duplicate of `track`; runtime.track_event method still used internally) - removed NullRunCallback from _LAZY_EXPORTS (advanced/manual path; reachable via `nullrun.toolbox.langgraph` if ever needed) README changes: - dropped 'Framework adapters — auto-detected' section - dropped framework-specific Examples bullets (langgraph, crewai, autogen, llama-index) - rewrote headline to 'works with any LLM SDK that uses httpx' pyproject.toml: removed [agents], [langchain], [langgraph], [llama-index], [crewai], [autogen] optional-dependency groups. Kept [opentelemetry] (industry standard, not a framework) and [dev]. Co-Authored-By: Claude Code <noreply@anthropic.com> * refactor(sdk): drop redundant @guarded decorator The @nullrun.guarded decorator was a 3-line syntactic shortcut for `with nullrun.handle()`. With handle() the canonical user-facing error-translation path and zero production examples using the decorator form, @guarded was pure duplication. Removes: - def guarded() from src/nullrun/_handle.py - "guarded" from _LAZY_EXPORTS and __all__ in __init__.py - 4 test_guarded_* tests from test_handle.py + test_dev_error_report.py - dead import in examples/tools/synthetic_sdk_load.py - examples/CONTRIBUTING.md doc reference Adds: - tests/test_init_or_die.py — splits init_or_die tests out of test_handle.py so the two surfaces no longer share a file Net: -520 lines, one way to translate NullRunError instead of two. Co-Authored-By: Claude Code <noreply@anthropic.com> * feat(sdk): auto-register atexit shutdown + unify init/init_or_die (0.18.3) Surface changes (breaking): * `nullrun.init_or_die()` removed. CLI fail-fast is now a parameter on `init()`: `nullrun.init(fail_on_exit=True)` prints the same four-line developer report and `sys.exit(1)` on configuration failure. Default `init()` keeps embedder-friendly raise semantics. * `nullrun.shutdown()` is auto-registered via `atexit` inside `init()` so long-running scripts get a clean WS close on process exit without an explicit call. Calling `shutdown()` manually remains safe and idempotent. Implementation: * `init()` gains `fail_on_exit: bool = False`; the missing-API-key branch renders the dev-error report and exits 1 when True, otherwise raises `NullRunAuthenticationError` as before. * `init()` registers `shutdown` with `atexit` guarded by a module- level `_shutdown_atexit_registered` flag (no double-stack). * `shutdown()` resets that flag so a `shutdown() → init()` cycle re-registers cleanly for the new runtime. Test coverage: * `tests/test_init_or_die.py` replaced with `tests/test_init_fail_on_exit.py` (3 tests, stubbed runtime). * `tests/test_init_contract.py::TestInitRegistersAtexitShutdown` added (3 tests, stubbed runtime, no network). * `tests/test_handle.py` drops the `init_or_die` smoke assertion. Docs: * README Quickstart + `shutdown` docstring: no manual `atexit` call. * `docs/errors/NR-C001.md`: no `init_or_die` mention. * `CHANGELOG.md` 0.18.3 entry documents both breaking changes. * `pyproject.toml` bumped to 0.18.3. All 1403 tests pass. Ruff clean on touched files. Co-Authored-By: Claude Code <noreply@anthropic.com> * feat(sdk): rename handle->guard and drop top-level status() (0.18.4) Surface changes (breaking): * `nullrun.handle` renamed to `nullrun.guard`. Same `@contextmanager` body (catches `NullRunError`, re-raises `WorkflowKilledInterrupt`, renders the four-line developer report, `sys.exit(1)` on failure). The verb `guard` was freed in 0.18.2 (f1721f2) when `@guarded` was removed and reads as a single verb alongside `init` / `shutdown` / `on_error`. Migrate by replacing `from nullrun import handle` with `from nullrun import guard` and `with nullrun.handle:` with `with nullrun.guard():`. * Top-level `nullrun.status()` removed. Reach the snapshot via `nullrun.get_runtime().status()` (returns the same frozen `NullRunStatus` dataclass). The wrapper's only role was raising `NullRunConfigError(NR-C004)` when no runtime was bound — that path is now `get_runtime()`'s job. Curated surface after 0.18.4: 20 names (was 21 in 0.18.3 — net -1 because `handle`->`guard` is in-place and `status` is removed). Implementation: * `src/nullrun/_handle.py`: rename `handle` -> `guard` (function body unchanged). Module file name stays `_handle.py` for the submodule-shadowing reason documented in that file's docstring (only the public function name is `guard`, the module file name is observable to no external caller — only `from nullrun import guard` is the public surface). * `src/nullrun/__init__.py`: drop `def status():` (47 lines); rename `handle` -> `guard` in `_LAZY_EXPORTS` and `__all__`; update the module docstring + `_LAZY_EXPORTS` comment blocks accordingly. * `docs/errors/NR-C004.md`: replace `nullrun.status()` references with `nullrun.get_runtime()` / `runtime.status()`; add a 0.18.4 note at the top calling out the wrapper removal. Test coverage: * Rename `tests/test_handle.py` -> `tests/test_guard.py` (7 -> 7 tests, identical body, mechanical rename + a new `test_handle_removed_from_public_surface` regression guard). * Rewrite `tests/test_status.py`: 23 of 24 sites moved from `nullrun.status()` to `rt.status()` on the local runtime. The `TestNoRuntime` class (2 tests) is dropped — the contract it pinned no longer exists without the top-level wrapper. The `TestPublicAPI` class (3 tests) becomes `TestStatusRemovedFromTopLevel` (2 regression-guard tests asserting `status` is NOT in `dir(nullrun)` or `__all__`). * `tests/test_dev_error_report.py`: handle -> guard throughout (4 tests renamed, bodies unchanged). All 1401 tests pass on Python 3.11 / Windows. Ruff clean on all touched files. Coverage regenerates to 81.34% (above the `fail_under = 80` gate) after dropping the stale `.coverage` file that referenced a removed `extractor.py`. Co-Authored-By: Claude Code <noreply@anthropic.com> * fix(sdk): follow-up 0.18.4 rename sweep in docs/scripts Sweep for any remaining stale references to the renamed nullrun.handle / nullrun.status() symbols after the 0.18.4 cleanup (83421e0): * scripts/smoke_prod.py — drop status import (F401), switch the call site to nullrun.get_runtime().status(). * src/nullrun/__init__.py — comment about the CLI fail-fast dev-error report: handle() → guard(). * src/nullrun/messages.py — NR-W004 docstring: nullrun.handle → nullrun.guard. * tests/test_init_fail_on_exit.py — module docstring: handle() → guard(). * tests/test_messages.py — NR-W004 test docstring: handle() → guard(). * tests/test_typed_exceptions_full_audit.py — disambiguate the comment from nullrun.handle() to ActionHandler.handle() so readers do not confuse the action-handler method (still present) with the renamed context manager (removed). Verified post-edit: * pytest: 1401 passed, 1 skipped (no regressions) * ruff on all touched files: clean (smoke_prod.py went from 3 pre-existing ruff errors to 2; the F401 on the now-removed status import is gone; the remaining F401 on_error and F841 decision_payload are pre-existing on master). * Public surface: handle=False, status=False, guard=True, __all__ length 20 (matches 0.18.4 design). * guard() works as a context manager and re-raises WorkflowKilledInterrupt (BaseException propagates). * Prod smoke against https://api.nullrun.io: @Protect round-trip returns 'echo(hello-0.18.4)'; guard() renders the four-line developer report and exits 1 on a NullRunError. Co-Authored-By: Claude Code <noreply@anthropic.com> * docs(sdk): drop stale init_or_die references in source docstrings Follow-up to ccb2670 (0.18.3: init_or_die removal) and the 0.18.4 rename sweep. Two stale source-code comments still referenced init_or_die by name even though the function was removed: * src/nullrun/_handle.py: the docstring at the head of the module explained how guard differs from the old init_or_die wrapper. Rewrite to point at init(fail_on_exit=True) only — the historical aside belongs in CHANGELOG.md, not next to guard's own contract. * src/nullrun/decorators.py:339: the lazy auto-instrument comment described a user who writes '@nullrun.protect without calling init_or_die() first' would still get a runtime. The actual surface is init() — the comment now reads correctly. Functional code untouched. All 1401 tests pass on Python 3.11. ruff check is clean on the touched hunks; pre-existing F401 in decorators.py:43 (warnings import) and pre-existing ruff-format whitespace around lines I did not touch are unrelated to this change and remain for a separate follow-up. * chore(sdk): remove unused imports + fix mypy errors leftover from deprecation sweep Mechanical cleanup so ruff check src tests + mypy src/nullrun are clean on the cleanup/deprecation-removal branch before merging to master. - 71 F401 (unused imports) auto-fixed by 'ruff check --fix'. - 9 F841 (unused local variables) hand-removed across tests; all were dead code left behind after deleted imports and refactored helpers (initial_buffer_len in test_transport, finalize_objs and t_id in test_signal_safety, first_orig in test_autogen_patch, existing_fp in test_dedup, req in test_model_fallback, reg in test_registry, sid in test_actions, timestamp in test_ws_signed_payload). - src/nullrun/business_impact.py: narrow BusinessImpact.impact from Any to NoImpactPayload — the inline comment already documented the invariant ('Only NoImpactPayload in 0.18.2') and the only public ctor (BusinessImpact.no_impact) wires a NoImpactPayload, so the broader Any was masking a real no-any-return at the to_wire_dict boundary. - src/nullrun/__init__.py: tighten _LAZY_EXPORTS annotation from tuple[str, str | None] to tuple[str, str] — every value in the table is a 2-tuple of module path + attribute name, so the optional was wrong and the downstream list[str] for __import__()'s fromlist tripped list-item mypy. Verification: ruff check src tests : All checks passed mypy src/nullrun : Success: no issues found in 36 source files pytest -q : 1401 passed, 1 skipped --------- Co-authored-by: Claude Code <noreply@anthropic.com>
1 parent ca83ec6 commit aee8110

107 files changed

Lines changed: 1892 additions & 15706 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎CHANGELOG.md‎

Lines changed: 80 additions & 1542 deletions
Large diffs are not rendered by default.

‎README.md‎

Lines changed: 11 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55
**Ship AI agents with real-time budget, policy, and human-approval gates.**
66

77
Zero-refactor cost control, tool policy enforcement, and audit trail for any
8-
LLM-powered agent - works with OpenAI, Anthropic, LangGraph, CrewAI, AutoGen,
9-
LlamaIndex, and your own stack.
8+
LLM-powered agent — works with any LLM SDK that uses `httpx`, plus your own stack.
109

1110
[Quickstart](https://docs.nullrun.io/getting-started/onboarding/) · [Docs](https://docs.nullrun.io) · [Examples](https://github.com/nullrunio/nullrun-examples)
1211

@@ -63,7 +62,7 @@ Existing observability tools tell you **after** the fact. NullRun enforces **bef
6362
|---|---|
6463
| **Hard & soft budget gates** — atomic Redis-enforced | **Tool policy enforcement** — block dangerous tools before execution |
6564
| **Human-in-the-loop approvals** — pause agent and await `approval_resolved` via WS push | **Immutable audit trail** — every decision, every tool call, every cent |
66-
| **Zero-code instrumentation** — `nullrun.init()` patches `httpx` once for any vendor | **LangGraph, CrewAI, AutoGen, LlamaIndex** — first-class integrations |
65+
| **Zero-code instrumentation** — `nullrun.init()` patches `httpx` once for any vendor | **No vendor lock-in** — works with any LLM SDK that uses httpx |
6766
| **Memory-safe streaming** — 16 MiB response body; full body for usage extraction | **Lightweight** — no LLM-key storage, no proxy required |
6867
| **Server-authoritative cost** — server-minted execution IDs | **MCP support** — expose tools to agents via Model Context Protocol |
6968

@@ -211,33 +210,17 @@ def my_agent(prompt: str) -> str:
211210

212211
```
213212

214-
### Framework adapters — auto-detected
213+
If you call `@protect` *before* `init()`, the SDK lazy-initializes
214+
the runtime from `NULLRUN_API_KEY` on the first decorated call. You can
215+
write your agent code with the decorator first and the init second — or
216+
skip `init` entirely if your environment is already configured.
215217

216-
NullRun auto-detects installed frameworks and instruments them automatically
217-
when `init_or_die()` runs (or when `@protect` first fires). You don't need
218-
to choose an extra; if a framework is already in your environment, it gets
219-
patched in place.
218+
For CLI scripts that want fail-fast on missing config, pass
219+
`fail_on_exit=True` — the SDK prints a four-line developer report and
220+
exits with code 1 instead of raising. `nullrun.shutdown()` is
221+
auto-registered via `atexit` inside `init()`, so a clean WS close on
222+
process exit happens without any explicit call.
220223

221-
| Framework | What gets patched | Trigger |
222-
|---|---|---|
223-
| **LangGraph** (`Pregel.invoke` / `stream` / `ainvoke` / `astream`) | `NullRunCallback` injected per call | auto on `init_or_die()` |
224-
| **LangChain** (`BaseCallbackManager`) | `NullRunCallback` registered | auto on `init_or_die()` |
225-
| **OpenAI Agents** (`Runner.run` / `run_streamed`) | `RunHooks` / `RunStreamedHooks` instrumented | auto on `init_or_die()` |
226-
| **LlamaIndex** (`get_dispatcher`) | `LLMChatEndEvent` / `FunctionCallEvent` handlers | auto on `init_or_die()` |
227-
| **CrewAI** (event bus + `usage_metrics`) | `Agent` / `Task` / `Crew` lifecycle | auto on `init_or_die()` |
228-
| **AutoGen** (`Agent.run` / `a_run`) | message-streaming hooks (HTTP path is httpx-based) | auto on `init_or_die()` |
229-
230-
**HTTP-level coverage is the foundation** — `httpx` (and `requests`) are
231-
patched once by `init_or_die()` regardless of vendor. Token counts and
232-
model info are extracted from response bodies for OpenAI, Azure, Anthropic,
233-
Mistral, Gemini, Cohere, and Bedrock without those vendor SDKs needing to
234-
be installed. If you use the raw `httpx.Client` API directly, you get
235-
cost tracking out of the box.
236-
237-
If you call `@protect` *before* `init_or_die()`, the SDK auto-triggers
238-
instrumentation lazily on the first decorated call. You can write your
239-
agent code with the decorator first and the init second — or skip `init`
240-
entirely if your environment is already configured via `NULLRUN_API_KEY`.
241224
---
242225

243226
## How NullRun compares
@@ -336,10 +319,6 @@ and `src/nullrun/transport.py::consume_approval`.
336319

337320
Runnable, copy-pastable examples live in a separate repo so you can adapt without cloning the SDK source:
338321

339-
- **[LangGraph](https://docs.nullrun.io/how-to/langgraph/)** — multi-node agent with budget + approval
340-
- **[CrewAI](https://docs.nullrun.io/how-to/crewai/)** — multi-agent crew with shared budget
341-
- **[AutoGen](https://docs.nullrun.io/how-to/autogen/)** — group-chat agent with policy gating
342-
- **[LlamaIndex](https://docs.nullrun.io/how-to/llama-index/)** — RAG pipeline with cost-per-query enforcement
343322
- **[Custom tools](https://docs.nullrun.io/how-to/fastapi/)** — register your own tools for policy
344323
- **[Multi-agent](https://docs.nullrun.io/how-to/multi-agent/)** — shared budget across sub-agents
345324

‎docs/errors/NR-B003.md‎

Lines changed: 0 additions & 66 deletions
This file was deleted.

‎docs/errors/NR-C000.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,4 @@ to override.
2828

2929
- `NR-C001` — `nullrun.init()` called with no api_key.
3030
- `NR-C003` — `get_org_status()` called before the runtime is bound.
31-
- `NR-C004` — `nullrun.status()` called before `nullrun.init()`.
31+
- `NR-C004` — `nullrun.get_runtime()` (or `runtime.status()`) called before `nullrun.init()`.

‎docs/errors/NR-C001.md‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ call from the environment, so `init()` does not need to be called —
1616
but the env var must be present by the time the runtime is asked to
1717
gate a call.
1818

19-
If the developer chose to call `init()` or `init_or_die()`
20-
explicitly, the same code surfaces earlier (at the explicit init
21-
call, not at the first `@protect`). Both paths produce the same
22-
typed exception.
19+
If the developer chose to call `init()` explicitly with
20+
`fail_on_exit=True`, the same code surfaces earlier (at the explicit
21+
init call, not at the first `@protect`) and the SDK prints a
22+
four-line developer report and `sys.exit(1)` instead of raising.
2323

2424
## Why this raises (instead of falling back)
2525

@@ -64,7 +64,7 @@ try:
6464
result = my_agent(prompt)
6565
except NullRunConfigError as exc:
6666
if exc.error_code == "NR-C001":
67-
# Show the user the dashboard link inline. With `with nullrun.handle():`
67+
# Show the user the dashboard link inline. With `with nullrun.guard():`
6868
# around the call, the SDK will print the four-line developer report
6969
# automatically — you only need this catch for custom error UI.
7070
return render_onboarding(api_key_help_url=exc.user_action)
@@ -75,4 +75,4 @@ except NullRunConfigError as exc:
7575

7676
- `NR-A001` / `NR-A002` / `NR-A003` — key provided but rejected.
7777
- `NR-C003` — runtime bound, but no `org_id` available for `get_org_status()`.
78-
- `NR-C004` — `nullrun.status()` called before the runtime is bound.
78+
- `NR-C004` — `nullrun.get_runtime()` / `runtime.status()` called before the runtime is bound.

‎docs/errors/NR-C004.md‎

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,25 @@
1-
# NR-C004 — `nullrun.status()` called before `nullrun.init()`
1+
# NR-C004 — `get_runtime()` / `runtime.status()` called before `nullrun.init()`
2+
3+
> **0.18.4 note:** the top-level `nullrun.status()` wrapper was removed
4+
> in 0.18.4; reach the snapshot via `nullrun.get_runtime().status()`
5+
> instead. `get_runtime()` raises `NullRunConfigError(NR-C004)` when
6+
> no runtime has been bound. This doc retains the same shape; only the
7+
> entry-point wording has changed.
28
39
| Field | Value |
410
|---|---|
511
| **Code** | `NR-C004` |
612
| **Category** | Configuration |
713
| **Exception class** | `NullRunConfigError` |
814
| **Retryable** | No |
9-
| **Default `user_action`** | "Call `nullrun.init(api_key='nr_live_...')` before calling `nullrun.status()`. The snapshot only makes sense once the SDK has a runtime bound to the API key." |
15+
| **Default `user_action`** | "Call `nullrun.init(api_key='nr_live_...')` before requesting the runtime snapshot. The snapshot only makes sense once the SDK has a runtime bound to the API key." |
1016

1117
## When
1218

13-
Raised by `nullrun.status()` when the runtime has not been initialised
14-
yet. `status()` returns a snapshot of the runtime's account state —
15-
without an active runtime, there is nothing to snapshot.
19+
Raised by `nullrun.get_runtime()` when the runtime has not been
20+
initialised yet. `runtime.status()` returns a snapshot of the
21+
runtime's account state — without an active runtime, there is
22+
nothing to snapshot.
1623

1724
This is distinct from `NR-C001` (no api_key at all): the call to
1825
`init()` was simply never made, or the runtime was shut down with
@@ -22,29 +29,32 @@ This is distinct from `NR-C001` (no api_key at all): the call to
2229

2330
- Forgot to call `nullrun.init()` at process startup.
2431
- Called `nullrun.shutdown()` (or `runtime.shutdown()`) at module
25-
unload and then `nullrun.status()` from a signal handler or
32+
unload and then `runtime.status()` from a signal handler or
2633
finalizer that ran afterwards.
27-
- Calling `status()` from a test fixture that did not auto-init.
34+
- Calling `runtime.status()` from a test fixture that did not
35+
auto-init.
2836

2937
## How to fix
3038

3139
1. Add `nullrun.init(api_key=...)` (or read from `NULLRUN_API_KEY`
3240
env var by passing `api_key=None`) at the top of the entrypoint.
3341
2. If the runtime was intentionally shut down, skip the snapshot
3442
call rather than re-initialising after shutdown.
35-
3. In tests, use the `nullrun_test_runtime` fixture from
43+
3. In tests, use the `make_runtime` fixture from
3644
`tests/conftest.py` instead of constructing one manually.
3745

3846
## Catch pattern
3947

4048
```python
4149
from nullrun.breaker.exceptions import NullRunConfigError
50+
from nullrun import get_runtime
4251

4352
try:
44-
snap = nullrun.status()
53+
rt = get_runtime()
54+
snap = rt.status()
4555
except NullRunConfigError as exc:
4656
if exc.error_code == "NR-C004":
47-
log.error("status() called before init: %s", exc.user_action)
57+
log.error("runtime not bound: %s", exc.user_action)
4858
return None
4959
raise
5060
```

‎docs/errors/README.md‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ The codes follow a `NR-<CATEGORY><NNN>` pattern:
2727
| `NR-C000` | Generic config error (default on `NullRunConfigError`; subclasses override) | [NR-C000](NR-C000.md) |
2828
| `NR-C001` | `nullrun.init()` called with no api_key (no param, no env) | [NR-C001](NR-C001.md) |
2929
| `NR-C003` | `get_org_status()` called before the runtime is bound to an org | [NR-C003](NR-C003.md) |
30-
| `NR-C004` | `nullrun.status()` called before `nullrun.init()` | [NR-C004](NR-C004.md) |
30+
| `NR-C004` | `nullrun.get_runtime()` (or `runtime.status()`) called before `nullrun.init()` | [NR-C004](NR-C004.md) |
3131

3232
### Authentication (NR-A)
3333

@@ -44,7 +44,6 @@ The codes follow a `NR-<CATEGORY><NNN>` pattern:
4444
|---|---|---|
4545
| `NR-B001` | Network error: timeout, ConnectError, DNS failure | [NR-B001](NR-B001.md) |
4646
| `NR-B002` | 5xx from the NullRun backend | [NR-B002](NR-B002.md) |
47-
| `NR-B003` | `@sensitive` failed to extract a `BusinessImpact` envelope | [NR-B003](NR-B003.md) |
4847
| `NR-B004` | Budget exhausted | [NR-B004](NR-B004.md) |
4948
| `NR-B005` | Local circuit breaker tripped | [NR-B005](NR-B005.md) |
5049

‎pyproject.toml‎

Lines changed: 10 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
66
name = "nullrun"
77
# Full release history lives in CHANGELOG.md; only the current version
88
# is pinned here.
9-
version = "0.18.1"
9+
version = "0.18.4"
1010
# Kept under the 200-char preview threshold so the full line is visible
1111
# without an "expand" click. The headline is the canonical §1 statement
1212
# from positioning.md — "runtime decision layer for tool-using AI agents"
@@ -71,50 +71,12 @@ dependencies = [
7171
]
7272

7373
[project.optional-dependencies]
74+
# OpenTelemetry is an observability standard (not a vendor framework);
75+
# auto-detected at runtime when installed.
7476
opentelemetry = [
7577
"opentelemetry-api>=1.26.0,<2.0",
7678
"opentelemetry-sdk>=1.26.0,<2.0",
7779
]
78-
langgraph = [
79-
"langgraph>=0.2.0,<1.0",
80-
]
81-
# Framework auto-instrumentation dependencies.
82-
#
83-
# These extras install framework SDKs whose event systems NullRun
84-
# subscribes to. NullRun's HTTP-level instrumentation
85-
# (``patch_httpx`` + ``patch_requests`` + 5 URL-keyed extractors)
86-
# covers OpenAI / Anthropic / Mistral / Gemini / Cohere / Bedrock
87-
# WITHOUT requiring their vendor SDKs — all of those vendors route
88-
# through httpx, and NullRun parses the response body by URL host.
89-
# The vendor SDK packages are NOT imported anywhere in
90-
# ``src/nullrun/``, so extras like ``[openai]`` / ``[anthropic]`` /
91-
# ``[mistral]`` / ``[gemini]`` / ``[cohere]`` / ``[bedrock]`` would
92-
# be dead weight for the SDK.
93-
#
94-
# What NullRun DOES import from these framework SDKs:
95-
# - ``agents`` → ``agents.Runner`` (openai-agents tracing model)
96-
# - ``langchain`` → ``langchain_core.callbacks.BaseCallbackManager``
97-
# + ``langchain_core.language_models.BaseChatModel``
98-
# - ``langgraph`` → ``langgraph.pregel.Pregel``
99-
# - ``llama-index`` → ``llama_index.core.instrumentation``
100-
# - ``crewai`` → ``crewai.Crew`` + ``crewai.events``
101-
# - ``autogen`` → ``autogen_agentchat.agents.BaseChatAgent`` +
102-
# ``autogen_ext.models.openai.OpenAIChatCompletionClient``
103-
#
104-
# Each ``patch_*`` wraps its framework import in
105-
# ``try/except ImportError`` so ``nullrun.init()`` never crashes when
106-
# the optional package is missing. Auto-detection: NullRun activates
107-
# an adapter when the package is installed — the user does NOT need
108-
# to choose which framework extra to install; installing any one of
109-
# them auto-enables its adapter.
110-
agents = ["openai-agents>=0.1,<1.0"]
111-
langchain = ["langchain-core>=0.3,<1.0"]
112-
llama-index = ["llama-index-core>=0.10.20,<1.0"]
113-
crewai = ["crewai>=0.80,<2.0"]
114-
autogen = [
115-
"autogen-agentchat>=0.4,<1.0",
116-
"autogen-ext[openai]>=0.4,<1.0",
117-
]
11880
dev = [
11981
"pytest>=8.0",
12082
"pytest-asyncio>=0.23",
@@ -463,22 +425,16 @@ line-length = 100
463425
select = ["E", "F", "I", "UP", "B", "S"]
464426
ignore = [
465427
"S101",
466-
# Pre-existing violations in master / wip/working-tree: tracked
467-
# for follow-up cleanup in a dedicated PR rather than blocking CI.
468-
# Categories:
469-
# S110 (try/except/pass) - 14 sites; needs logging, not blanket
470-
# noqa
471-
# E501 (line too long) - 13 sites; long descriptive comments
472-
# F841 (unused variable) - 6 sites; one is the timestamp var
473-
# in the legacy-code fallback path
474-
# E402 (import order) - 5 sites; TYPE_CHECKING blocks
475-
# F401 (unused import) - 2 sites
428+
# Pre-existing violations in master. Tracked for follow-up cleanup
429+
# in a dedicated PR rather than blocking CI. Categories:
430+
# S110 (try/except/pass) - blanket noqa would hide bugs; needs
431+
# logging, not blanket noqa
432+
# E501 (line too long) - long descriptive comments
433+
# E402 (import order) - TYPE_CHECKING blocks
476434
"S110",
477435
"E501",
478-
"F841",
479436
"E402",
480-
"F401",
481-
# S311 (suspicious random) - 1 site, in circuit_breaker jitter.
437+
# S311 (suspicious random) in circuit_breaker jitter.
482438
# random.uniform is correct for jitter (we want non-cryptographic
483439
# randomness to spread reconnection timing across workers).
484440
"S311",

0 commit comments

Comments
 (0)