jeevesagent.core.context¶
Per-run context propagation.
A single RunContext is built at the top of every
Agent.run() (or Agent.stream()) call and stored in a
contextvars.ContextVar for the duration of the run. Tools,
hooks, sub-agents, and architectures all read it through
get_run_context() rather than threading it through every
signature.
The framework treats user_id and session_id as first-class
typed primitives — not strings buried in a free-form configurable
dict. user_id partitions memory recall; session_id identifies
the conversation thread for replay and continuity. Application-
specific keys go in the metadata mapping, where the framework
makes no claim to understand them.
The contextvar is automatically propagated by anyio’s structured
concurrency primitives (create_task_group, start_soon), so
parallel tool dispatch, sub-agent spawning, and streaming consumers
all see the same context without any explicit plumbing.
Tests that call @tool functions directly (no active agent run)
get a default empty RunContext rather than an exception —
preserving direct-invocation ergonomics.
Exceptions¶
Emitted when a memory query is likely to silently miss data |
Classes¶
Typed, immutable context for one agent run. |
|
Context manager that installs a |
Functions¶
|
Return the |
Module Contents¶
- exception jeevesagent.core.context.IsolationWarning[source]¶
Bases:
UserWarningEmitted when a memory query is likely to silently miss data because the caller forgot to pass
user_id.Concrete trigger: a backend’s
recall/recall_factsruns withuser_id=Noneagainst a store whose persisted records include at least one non-Noneuser_id— the partition is safe (the anonymous bucket and named-user buckets are isolated), but the developer probably wired up multi-tenancy somewhere and forgot to passuser_idhere, so they will see suspiciously empty recall results.Subclass of
UserWarningso it goes through Python’s standardwarningsfilter machinery — apps can silence, promote-to-error, or log it however they want, e.g.:import warnings from jeevesagent import IsolationWarning warnings.simplefilter("error", IsolationWarning) # raise on hit
Initialize self. See help(type(self)) for accurate signature.
- class jeevesagent.core.context.RunContext[source]¶
Typed, immutable context for one agent run.
Set once at the start of
Agent.run()and propagated to every architecture, tool, hook, sub-agent, and memory operation via acontextvars.ContextVar. The framework treatsuser_idandsession_idas first-class fields (typed, namespaced);metadatais an opaque bag for app-specific keys the framework does not interpret.Construct one directly when you need to spawn work outside an active run with explicit scope:
ctx = RunContext(user_id="alice", session_id="conv_42") async with set_run_context(ctx): await my_tool(...)
Inside an agent run, prefer
get_run_context()over constructing a new one — that gives you the live context the framework set up.- with_overrides(*, user_id: str | None | _Sentinel = _Sentinel.UNSET, session_id: str | None | _Sentinel = _Sentinel.UNSET, run_id: str | _Sentinel = _Sentinel.UNSET, metadata: collections.abc.Mapping[str, Any] | _Sentinel = _Sentinel.UNSET) RunContext[source]¶
Return a new context with selected fields replaced.
Used by multi-agent architectures when spawning sub-agents that need to inherit most of the parent’s context but with a derived
session_idor augmentedmetadata. The sentinel makes “leave this field unchanged” distinguishable from “explicitly set this field toNone”.
- metadata: collections.abc.Mapping[str, Any]¶
Free-form application context. Use this for keys the framework does not need to understand — locale, request id, feature flags, tenant id beyond
user_id, etc. Read inside tools / hooks viaget_run_context().metadata.
- run_id: str = ''¶
Unique identifier for this single
Agent.run()invocation. Distinct fromsession_id(which identifies a conversation that may span many runs). Auto-set byAgent.run(); an explicit value passed in by the caller is overridden.
- session_id: str | None = None¶
Conversation thread identifier. Reusing the same
session_idacross calls signals “continue this conversation” — the framework will rehydrate prior session messages so the model sees real chat history, not just memory recall.Nonemeans “fresh conversation”; the framework auto-generates one insideAgent.run()if not supplied.
- user_id: str | None = None¶
Namespace for memory recall + persistence.
Noneis the “anonymous / single-tenant” bucket; episodes / facts stored withuser_id=Nonenever see episodes / facts stored with a non-Noneuser_idand vice versa. The framework treats this as a hard partition key, not a soft filter.
- class jeevesagent.core.context.set_run_context(context: RunContext)[source]¶
Context manager that installs a
RunContextfor the duration of anasync withblock.The framework uses this internally inside
Agent.run()to expose the live context to tools and hooks. Application code rarely needs it, but it is the supported way to invoke a tool outside an agent loop with explicit scope — for example in background workers that share tool implementations with the agent:async with set_run_context(RunContext(user_id="alice")): await some_tool(...)
Behaves correctly under structured concurrency: nested
async withblocks restore the prior context on exit, andanyiotask-group spawns inherit the active context automatically.
- jeevesagent.core.context.get_run_context() RunContext[source]¶
Return the
RunContextfor the currently-running agent.Inside an active
Agent.run()call this returns the live context withuser_id,session_id,run_id, andmetadatapopulated. Outside any active run (test code, direct@toolinvocation, REPL exploration) this returns the default emptyRunContext— never raises.Tools that need scope information call this rather than taking extra parameters:
@tool async def fetch_user_orders() -> str: ctx = get_run_context() return await db.query("orders", user_id=ctx.user_id)