Replace container singleton with request-scoped runtime DI; simplify error logging - #101
Merged
Merged
Conversation
…error logging Remove the process-global `SingletonMeta`/`Container`/`container` service locator in favor of explicit, request-scoped dependency injection, and reduce execution-error logging to a single concise, id-bearing line. Runtime DI (no component-interface change): - Add grafi/runtime: `ExecutionServices` (frozen, non-serializable bundle of event_store/tracer/error_reporter with in-process default_factory defaults), `GrafiRuntime` (composition root + invoke entry point), and the request-scoped `_current_services` ContextVar with `current_services()` / `bind_services()`. - `GrafiRuntime.invoke()` binds the services for the invocation; components resolve them via `current_services()`. No `invoke()` signature gains a `services` parameter -- tool/provider/node/command overrides are untouched. Relies on asyncio context propagation (grafi uses no thread offloads). - Switch the four container read sites (record_base, llm_command, event_driven_workflow, WorkflowRun) to `current_services()`. - Remove `container`, `SingletonMeta`, `register_event_store`/`register_tracer`, and `AssistantBaseBuilder.event_store`. Callers construct a `GrafiRuntime` (or `GrafiRuntime()` for in-process defaults) and invoke through it. Error diagnostics: - One concise log line per failure, emitted once at the layer closest to the failure, carrying error_id + conversation/invoke/assistant_request ids so the full structured record (cause chain, traceback, component fields) can be pulled from the event store. No traceback dumped to the log. - Add `error_id` correlation preserved across re-wrapped exceptions; stable wrapper messages (no embedded `str(cause)`); no double-wrapped NodeExecutionError; preserve the primary failure when failed-event persistence fails; EventStorePostgres raises EventPersistenceError with operation context consistently. - `ErrorReporter` is a single concrete loguru-backed class (subclass to customize); a misbehaving reporter can never mask the execution failure. Tests/examples: - conftest binds a default `ExecutionServices` per test (sync autouse fixture); migrate workflow/decorator tests off `container`; add tests/runtime suite (DI isolation, ContextVar propagation, serialization safety, error reporting). - Migrate tests_integration examples to run inside a bound runtime scope. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Follow-up to the runtime-DI change: make the agent convenience API a first-class runtime consumer, tidy the error path, and bring the docs in line. - ReActAgent.run()/a_run() now run through GrafiRuntime (optional `runtime=` param, defaulting to an in-process GrafiRuntime()), so callers no longer wrap them in bind_services(...). a_run() simply relays runtime.invoke()'s output. - _build_error_details walks the exception cause chain once (one _error_correlation pass returning error_id + root cause) instead of twice. - record_base: fix a stale comment describing the pre-simplification logging. - Docs: replace user-guide/containers.md with user-guide/runtime.md (request- scoped DI: ExecutionServices/GrafiRuntime/current_services/bind_services, with a migration table) and update mkdocs nav. Migrate the container/registration snippets in the event-store, OpenTelemetry, and MCP-server guides to GrafiRuntime/ExecutionServices; drop the removed `.event_store(...)` builder call from the builder-pattern and assistant guides; run direct workflow/tool/assistant examples inside a bound runtime scope. - gitignore root-level *_manifest.json (generate_manifest test artifact). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces the process-global
SingletonMeta/Container/containerservice locatorwith explicit, request-scoped dependency injection, and reduces execution-error
logging to a single concise, id-bearing line. No component
invoke()signaturechanges — tools, nodes, and commands are untouched.
Runtime DI
grafi/runtime:ExecutionServices(frozen, non-serializable bundle ofevent_store/tracer/error_reporter, each with an in-processdefault_factorydefault),GrafiRuntime(composition root +invokeentrypoint), and the request-scoped
_current_servicesContextVarwithcurrent_services()/bind_services().GrafiRuntime.invoke()binds the services for the invocation; componentsresolve them via
current_services(). Propagation relies onasynciocontext copying (Graphite uses no thread offloads), giving genuine
per-invocation isolation for concurrent invokes.
containerread sites (decorator,LLMCommand.get_tool_input,EventDrivenWorkflow,WorkflowRun) now readcurrent_services().ReActAgent.run()/a_run()run through aGrafiRuntime(optionalruntime=param), so the agent API needs no manual scope binding.Error diagnostics
carrying
error_id+ conversation/invoke/assistant_request ids — the fullstructured record (cause chain, traceback, component fields) is persisted to
the event store, so the log only points at it.
error_idcorrelation preserved across re-wrapped exceptions; stable wrappermessages (no embedded
str(cause)); no double-wrappedNodeExecutionError;the primary failure is preserved when failed-event persistence fails; a
misbehaving reporter can never mask the execution failure;
EventStorePostgresraises
EventPersistenceErrorwith operation context consistently.ErrorReporteris a single concrete loguru-backed class (subclass to customize).Breaking change
SingletonMeta,Container, the globalcontainer,register_event_store,register_tracer, andAssistantBaseBuilder.event_store(...)are removed. Codethat relied on the global container now constructs a
GrafiRuntime(orGrafiRuntime()for in-process defaults) and invokes through it;assistant.invoke(...)outside a bound scope raises a clear error instead ofsilently defaulting. Grafi is pre-1.0; release notes/migration are in the new
runtime guide.
Docs
user-guide/containers.md→user-guide/runtime.md(request-scoped DI guide +migration table); mkdocs nav updated.
GrafiRuntime/ExecutionServices; builder/assistant guides drop the removed.event_store(...)call; direct workflow/tool/assistant examples now run insidea bound runtime scope.
Testing
pytest tests/— 702 passed (incl. a newtests/runtime/suite: DIisolation, ContextVar propagation, serialization safety, error reporting).
mypy grafi— clean (88 source files).isort+black+flake8— clean acrossgrafi,tests,tests_integration.runtime binding and surfaces the normalized error chain
(
NodeExecutionError → LLMToolException → OpenAIError).🤖 Generated with Claude Code