Skip to content

[Deep Agent] Remove process-global request state (capability flags, connection singleton, frozen date) via runtime context #38

Open

Description

@stavp-star

Problem

The deep agent keeps request-scoped data in process-global mutable state. That is unsafe as soon as more than one user, connection, or concurrent request hits the same process:

  • _connection_capabilities (agent/src/deep_agent/tools.py:20) is a module-level dict that get_database_catalog mutates. If connection A's catalog has no knowledge pairs, then for every other user/connection in the process, search_knowledge_pairs returns "[] ... Do not call this tool again" until someone else's catalog call flips it back. Under concurrency this is a race. The flags also start as True, so behaviour depends on call order.
  • The connection is selected by mutating a singleton: run_deep_agent.py:207-209 sets os.environ[...] and the private client._connection_id. No tool accepts a connection, so the deep agent cannot serve two connections in one process at all.
  • today is formatted into the system prompt once, at agent construction (agent.py:239). A long-running server will report a stale date after midnight. That affects every relative-date question ("אתמול", "השבוע").

Proposed fix

  • Define a context_schema (LangGraph runtime context), e.g. DeepAgentContext(connection_id: int, user_id: str, locale: str = "he"), and pass it per invocation (agent.ainvoke(..., context=DeepAgentContext(...))).
  • Tools read the connection via ToolRuntime[DeepAgentContext] and call JeenMetadataClient methods with an explicit connection_id (most already accept connection_id=).
  • Store capability flags in graph state (returned via Command(update=...) from get_database_catalog), or compute them per connection in a cache keyed by connection_id.
  • Inject today's date dynamically with a @dynamic_prompt / before_model middleware (timezone Asia/Jerusalem), not at construction time.

Acceptance Criteria

  • No module-level mutable request state remains in agent/src/deep_agent/.
  • Two concurrent ainvoke calls with different connection_ids (one with knowledge pairs, one without) produce correct, independent tool behaviour. Covered by an asyncio test.
  • run_deep_agent.py no longer touches client._connection_id or os.environ to switch connections.
  • The date in the prompt reflects the day of the invocation in Asia/Jerusalem (test with a frozen clock across midnight).

Definition of Done

  • Concurrency test added and green.
  • Tool docstrings unchanged from the model's point of view (context is injected, not a model argument).
  • Merged.

Open questions


Found in an architecture/code review of feature/deep-agent-updated (commits 5cc8398, 897de37, b8e27c8) vs main.

🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions