remdb 0.3.181__py3-none-any.whl → 0.3.200__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.

Potentially problematic release.


This version of remdb might be problematic. Click here for more details.

rem/agentic/context.py CHANGED
@@ -22,14 +22,81 @@ Key Design Pattern:
22
22
  - Enables session tracking across API, CLI, and test execution
23
23
  - Supports header-based configuration override (model, schema URI)
24
24
  - Clean separation: context (who/what) vs agent (how)
25
+
26
+ Multi-Agent Context Propagation:
27
+ - ContextVar (_current_agent_context) threads context through nested agent calls
28
+ - Parent context is automatically available to child agents via get_current_context()
29
+ - Use agent_context_scope() context manager for scoped context setting
30
+ - Child agents inherit user_id, tenant_id, session_id, is_eval from parent
25
31
  """
26
32
 
33
+ from contextlib import contextmanager
34
+ from contextvars import ContextVar
35
+ from typing import Generator
36
+
27
37
  from loguru import logger
28
38
  from pydantic import BaseModel, Field
29
39
 
30
40
  from ..settings import settings
31
41
 
32
42
 
43
+ # Thread-local context for current agent execution
44
+ # This enables context propagation through nested agent calls (multi-agent)
45
+ _current_agent_context: ContextVar["AgentContext | None"] = ContextVar(
46
+ "current_agent_context", default=None
47
+ )
48
+
49
+
50
+ def get_current_context() -> "AgentContext | None":
51
+ """
52
+ Get the current agent context from context var.
53
+
54
+ Used by MCP tools (like ask_agent) to inherit context from parent agent.
55
+ Returns None if no context is set (e.g., direct CLI invocation without context).
56
+
57
+ Example:
58
+ # In an MCP tool
59
+ parent_context = get_current_context()
60
+ if parent_context:
61
+ # Inherit user_id, session_id, etc. from parent
62
+ child_context = parent_context.child_context(agent_schema_uri="child-agent")
63
+ """
64
+ return _current_agent_context.get()
65
+
66
+
67
+ def set_current_context(ctx: "AgentContext | None") -> None:
68
+ """
69
+ Set the current agent context.
70
+
71
+ Called by streaming layer before agent execution.
72
+ Should be cleared (set to None) after execution completes.
73
+ """
74
+ _current_agent_context.set(ctx)
75
+
76
+
77
+ @contextmanager
78
+ def agent_context_scope(ctx: "AgentContext") -> Generator["AgentContext", None, None]:
79
+ """
80
+ Context manager for scoped context setting.
81
+
82
+ Automatically restores previous context when exiting scope.
83
+ Safe for nested agent calls - each level preserves its parent's context.
84
+
85
+ Example:
86
+ context = AgentContext(user_id="user-123")
87
+ with agent_context_scope(context):
88
+ # Context is available via get_current_context()
89
+ result = await agent.run(...)
90
+ # Previous context (or None) is restored
91
+ """
92
+ previous = _current_agent_context.get()
93
+ _current_agent_context.set(ctx)
94
+ try:
95
+ yield ctx
96
+ finally:
97
+ _current_agent_context.set(previous)
98
+
99
+
33
100
  class AgentContext(BaseModel):
34
101
  """
35
102
  Session and configuration context for agent execution.
@@ -85,6 +152,40 @@ class AgentContext(BaseModel):
85
152
 
86
153
  model_config = {"populate_by_name": True}
87
154
 
155
+ def child_context(
156
+ self,
157
+ agent_schema_uri: str | None = None,
158
+ model_override: str | None = None,
159
+ ) -> "AgentContext":
160
+ """
161
+ Create a child context for nested agent calls.
162
+
163
+ Inherits user_id, tenant_id, session_id, is_eval from parent.
164
+ Allows overriding agent_schema_uri and default_model for the child.
165
+
166
+ Args:
167
+ agent_schema_uri: Agent schema for the child agent (required for lineage)
168
+ model_override: Optional model override for child agent
169
+
170
+ Returns:
171
+ New AgentContext for the child agent
172
+
173
+ Example:
174
+ parent_context = get_current_context()
175
+ child_context = parent_context.child_context(
176
+ agent_schema_uri="sentiment-analyzer"
177
+ )
178
+ agent = await create_agent(context=child_context)
179
+ """
180
+ return AgentContext(
181
+ user_id=self.user_id,
182
+ tenant_id=self.tenant_id,
183
+ session_id=self.session_id,
184
+ default_model=model_override or self.default_model,
185
+ agent_schema_uri=agent_schema_uri or self.agent_schema_uri,
186
+ is_eval=self.is_eval,
187
+ )
188
+
88
189
  @staticmethod
89
190
  def get_user_id_or_default(
90
191
  user_id: str | None,
@@ -217,11 +217,21 @@ class ContextBuilder:
217
217
  )
218
218
 
219
219
  # Convert to ContextMessage format
220
+ # For tool messages, wrap content with clear markers so the agent
221
+ # can see previous tool results when the prompt is concatenated
220
222
  for msg_dict in session_history:
223
+ role = msg_dict["role"]
224
+ content = msg_dict["content"]
225
+
226
+ if role == "tool":
227
+ # Wrap tool results with clear markers for visibility
228
+ tool_name = msg_dict.get("tool_name", "unknown")
229
+ content = f"[TOOL RESULT: {tool_name}]\n{content}\n[/TOOL RESULT]"
230
+
221
231
  messages.append(
222
232
  ContextMessage(
223
- role=msg_dict["role"],
224
- content=msg_dict["content"],
233
+ role=role,
234
+ content=content,
225
235
  )
226
236
  )
227
237
 
rem/api/main.py CHANGED
@@ -322,7 +322,7 @@ def create_app() -> FastAPI:
322
322
 
323
323
  app.add_middleware(
324
324
  AuthMiddleware,
325
- protected_paths=["/api/v1"],
325
+ protected_paths=["/api/v1", "/api/admin"],
326
326
  excluded_paths=["/api/auth", "/api/dev", "/api/v1/mcp/auth", "/api/v1/slack"],
327
327
  # Allow anonymous when auth is disabled, otherwise use setting
328
328
  allow_anonymous=(not settings.auth.enabled) or settings.auth.allow_anonymous,
@@ -34,6 +34,7 @@ from .resources import (
34
34
  register_status_resources,
35
35
  )
36
36
  from .tools import (
37
+ ask_agent,
37
38
  ask_rem_agent,
38
39
  get_schema,
39
40
  ingest_into_rem,
@@ -203,6 +204,9 @@ def create_mcp_server(is_local: bool = False) -> FastMCP:
203
204
  mcp.tool()(get_schema)
204
205
  mcp.tool()(save_agent)
205
206
 
207
+ # Register multi-agent tools
208
+ mcp.tool()(ask_agent)
209
+
206
210
  # Register test tool only in development environment (not staging/production)
207
211
  if settings.environment not in ("staging", "production"):
208
212
  mcp.tool()(test_error_handling)