remdb 0.3.230__py3-none-any.whl → 0.3.258__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.
- rem/agentic/__init__.py +10 -1
- rem/agentic/context.py +13 -2
- rem/agentic/context_builder.py +45 -34
- rem/agentic/providers/pydantic_ai.py +302 -110
- rem/api/mcp_router/resources.py +223 -0
- rem/api/mcp_router/tools.py +76 -10
- rem/api/routers/auth.py +113 -10
- rem/api/routers/chat/child_streaming.py +22 -8
- rem/api/routers/chat/completions.py +3 -3
- rem/api/routers/chat/sse_events.py +3 -3
- rem/api/routers/chat/streaming.py +40 -45
- rem/api/routers/chat/streaming_utils.py +5 -7
- rem/api/routers/feedback.py +2 -2
- rem/api/routers/query.py +5 -14
- rem/cli/commands/ask.py +144 -33
- rem/cli/commands/experiments.py +1 -1
- rem/cli/commands/process.py +9 -1
- rem/cli/commands/query.py +109 -0
- rem/cli/commands/session.py +117 -0
- rem/cli/main.py +2 -0
- rem/models/core/experiment.py +1 -1
- rem/models/entities/session.py +1 -0
- rem/schemas/agents/core/agent-builder.yaml +1 -1
- rem/schemas/agents/test_orchestrator.yaml +42 -0
- rem/schemas/agents/test_structured_output.yaml +52 -0
- rem/services/content/providers.py +151 -49
- rem/services/postgres/repository.py +1 -0
- rem/services/rem/README.md +4 -3
- rem/services/rem/parser.py +7 -10
- rem/services/rem/service.py +47 -0
- rem/services/session/compression.py +7 -3
- rem/services/session/pydantic_messages.py +25 -7
- rem/services/session/reload.py +2 -1
- rem/settings.py +64 -7
- rem/sql/migrations/004_cache_system.sql +3 -1
- rem/utils/schema_loader.py +135 -103
- {remdb-0.3.230.dist-info → remdb-0.3.258.dist-info}/METADATA +6 -5
- {remdb-0.3.230.dist-info → remdb-0.3.258.dist-info}/RECORD +40 -37
- {remdb-0.3.230.dist-info → remdb-0.3.258.dist-info}/WHEEL +0 -0
- {remdb-0.3.230.dist-info → remdb-0.3.258.dist-info}/entry_points.txt +0 -0
rem/agentic/__init__.py
CHANGED
|
@@ -15,7 +15,13 @@ from .schema import (
|
|
|
15
15
|
validate_agent_schema,
|
|
16
16
|
create_agent_schema,
|
|
17
17
|
)
|
|
18
|
-
from .providers.pydantic_ai import
|
|
18
|
+
from .providers.pydantic_ai import (
|
|
19
|
+
create_agent_from_schema_file,
|
|
20
|
+
create_agent,
|
|
21
|
+
AgentRuntime,
|
|
22
|
+
clear_agent_cache,
|
|
23
|
+
get_agent_cache_stats,
|
|
24
|
+
)
|
|
19
25
|
from .query_helper import ask_rem, REMQueryOutput
|
|
20
26
|
from .llm_provider_models import (
|
|
21
27
|
ModelInfo,
|
|
@@ -41,6 +47,9 @@ __all__ = [
|
|
|
41
47
|
"create_agent_from_schema_file",
|
|
42
48
|
"create_agent",
|
|
43
49
|
"AgentRuntime",
|
|
50
|
+
# Agent Cache Management
|
|
51
|
+
"clear_agent_cache",
|
|
52
|
+
"get_agent_cache_stats",
|
|
44
53
|
# REM Query Helpers
|
|
45
54
|
"ask_rem",
|
|
46
55
|
"REMQueryOutput",
|
rem/agentic/context.py
CHANGED
|
@@ -16,6 +16,7 @@ Headers Mapping:
|
|
|
16
16
|
X-Agent-Schema → context.agent_schema_uri (default: "rem")
|
|
17
17
|
X-Model-Name → context.default_model
|
|
18
18
|
X-Is-Eval → context.is_eval (marks session as evaluation)
|
|
19
|
+
X-Client-Id → context.client_id (e.g., "web", "mobile", "cli")
|
|
19
20
|
|
|
20
21
|
Key Design Pattern:
|
|
21
22
|
- AgentContext is passed to agent factory, not stored in agents
|
|
@@ -222,6 +223,11 @@ class AgentContext(BaseModel):
|
|
|
222
223
|
description="Whether this is an evaluation session (set via X-Is-Eval header)",
|
|
223
224
|
)
|
|
224
225
|
|
|
226
|
+
client_id: str | None = Field(
|
|
227
|
+
default=None,
|
|
228
|
+
description="Client identifier (e.g., 'web', 'mobile', 'cli') set via X-Client-Id header",
|
|
229
|
+
)
|
|
230
|
+
|
|
225
231
|
model_config = {"populate_by_name": True}
|
|
226
232
|
|
|
227
233
|
def child_context(
|
|
@@ -232,7 +238,7 @@ class AgentContext(BaseModel):
|
|
|
232
238
|
"""
|
|
233
239
|
Create a child context for nested agent calls.
|
|
234
240
|
|
|
235
|
-
Inherits user_id, tenant_id, session_id, is_eval from parent.
|
|
241
|
+
Inherits user_id, tenant_id, session_id, is_eval, client_id from parent.
|
|
236
242
|
Allows overriding agent_schema_uri and default_model for the child.
|
|
237
243
|
|
|
238
244
|
Args:
|
|
@@ -256,6 +262,7 @@ class AgentContext(BaseModel):
|
|
|
256
262
|
default_model=model_override or self.default_model,
|
|
257
263
|
agent_schema_uri=agent_schema_uri or self.agent_schema_uri,
|
|
258
264
|
is_eval=self.is_eval,
|
|
265
|
+
client_id=self.client_id,
|
|
259
266
|
)
|
|
260
267
|
|
|
261
268
|
@staticmethod
|
|
@@ -374,6 +381,7 @@ class AgentContext(BaseModel):
|
|
|
374
381
|
default_model=normalized.get("x-model-name") or settings.llm.default_model,
|
|
375
382
|
agent_schema_uri=normalized.get("x-agent-schema"),
|
|
376
383
|
is_eval=is_eval,
|
|
384
|
+
client_id=normalized.get("x-client-id"),
|
|
377
385
|
)
|
|
378
386
|
|
|
379
387
|
@classmethod
|
|
@@ -391,6 +399,7 @@ class AgentContext(BaseModel):
|
|
|
391
399
|
- X-Model-Name: Model override
|
|
392
400
|
- X-Agent-Schema: Agent schema URI
|
|
393
401
|
- X-Is-Eval: Whether this is an evaluation session (true/false)
|
|
402
|
+
- X-Client-Id: Client identifier (e.g., "web", "mobile", "cli")
|
|
394
403
|
|
|
395
404
|
Args:
|
|
396
405
|
headers: Dictionary of HTTP headers (case-insensitive)
|
|
@@ -404,7 +413,8 @@ class AgentContext(BaseModel):
|
|
|
404
413
|
"X-Tenant-Id": "acme-corp",
|
|
405
414
|
"X-Session-Id": "sess-456",
|
|
406
415
|
"X-Model-Name": "anthropic:claude-opus-4-20250514",
|
|
407
|
-
"X-Is-Eval": "true"
|
|
416
|
+
"X-Is-Eval": "true",
|
|
417
|
+
"X-Client-Id": "web"
|
|
408
418
|
}
|
|
409
419
|
context = AgentContext.from_headers(headers)
|
|
410
420
|
"""
|
|
@@ -422,4 +432,5 @@ class AgentContext(BaseModel):
|
|
|
422
432
|
default_model=normalized.get("x-model-name") or settings.llm.default_model,
|
|
423
433
|
agent_schema_uri=normalized.get("x-agent-schema"),
|
|
424
434
|
is_eval=is_eval,
|
|
435
|
+
client_id=normalized.get("x-client-id"),
|
|
425
436
|
)
|
rem/agentic/context_builder.py
CHANGED
|
@@ -4,15 +4,12 @@ Centralized context builder for agent execution.
|
|
|
4
4
|
Session History (ALWAYS loaded with compression):
|
|
5
5
|
- Each chat request is a single message, so session history MUST be recovered
|
|
6
6
|
- Uses SessionMessageStore with compression to keep context efficient
|
|
7
|
-
- Long assistant responses include REM LOOKUP hints: "... [REM LOOKUP session-{id}-msg-{index}] ..."
|
|
8
|
-
- Agent can retrieve full content on-demand using REM LOOKUP
|
|
9
7
|
- Prevents context window bloat while maintaining conversation continuity
|
|
10
8
|
|
|
11
9
|
User Context (on-demand by default):
|
|
12
|
-
- System message includes
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
- Example: "User: sarah@example.com. To load user profile: Use REM LOOKUP \"sarah@example.com\""
|
|
10
|
+
- System message includes user email for context awareness
|
|
11
|
+
- Fails silently if user not found - agent proceeds without user context
|
|
12
|
+
- Example: "User: sarah@example.com"
|
|
16
13
|
|
|
17
14
|
User Context (auto-inject when enabled):
|
|
18
15
|
- Set CHAT__AUTO_INJECT_USER_CONTEXT=true
|
|
@@ -22,8 +19,8 @@ User Context (auto-inject when enabled):
|
|
|
22
19
|
Design Pattern:
|
|
23
20
|
1. Extract AgentContext from headers (user_id, tenant_id, session_id)
|
|
24
21
|
2. If auto-inject enabled: Load User/Session from database
|
|
25
|
-
3. If auto-inject disabled:
|
|
26
|
-
4. Construct system message with date + context
|
|
22
|
+
3. If auto-inject disabled: Show user email for context (fail silently if not found)
|
|
23
|
+
4. Construct system message with date + context
|
|
27
24
|
5. Return complete context ready for agent execution
|
|
28
25
|
|
|
29
26
|
Integration Points:
|
|
@@ -40,11 +37,10 @@ Usage (on-demand, default):
|
|
|
40
37
|
|
|
41
38
|
# Messages list structure (on-demand):
|
|
42
39
|
# [
|
|
43
|
-
# {"role": "system", "content": "Today's date: 2025-11-22\nUser: sarah@example.com
|
|
40
|
+
# {"role": "system", "content": "Today's date: 2025-11-22\n\nUser: sarah@example.com"},
|
|
44
41
|
# {"role": "user", "content": "What's next for the API migration?"}
|
|
45
42
|
# ]
|
|
46
43
|
|
|
47
|
-
# Agent receives hints and can decide to load context if needed
|
|
48
44
|
agent = await create_agent(context=context, ...)
|
|
49
45
|
prompt = "\n".join(msg.content for msg in messages)
|
|
50
46
|
result = await agent.run(prompt)
|
|
@@ -52,7 +48,7 @@ Usage (on-demand, default):
|
|
|
52
48
|
Usage (auto-inject, CHAT__AUTO_INJECT_USER_CONTEXT=true):
|
|
53
49
|
# Messages list structure (auto-inject):
|
|
54
50
|
# [
|
|
55
|
-
# {"role": "system", "content": "Today's date: 2025-11-22\n\nUser Context (auto-injected):\nSummary: ...\nInterests:
|
|
51
|
+
# {"role": "system", "content": "Today's date: 2025-11-22\n\nUser Context (auto-injected):\nSummary: ...\nInterests: ..."},
|
|
56
52
|
# {"role": "user", "content": "Previous message"},
|
|
57
53
|
# {"role": "assistant", "content": "Previous response"},
|
|
58
54
|
# {"role": "user", "content": "What's next for the API migration?"}
|
|
@@ -110,13 +106,11 @@ class ContextBuilder:
|
|
|
110
106
|
|
|
111
107
|
Session History (ALWAYS loaded with compression):
|
|
112
108
|
- If session_id provided, session history is ALWAYS loaded using SessionMessageStore
|
|
113
|
-
- Compression keeps
|
|
114
|
-
- Example: "... [Message truncated - REM LOOKUP session-{id}-msg-{index}] ..."
|
|
115
|
-
- Agent can retrieve full content on-demand using REM LOOKUP
|
|
109
|
+
- Compression keeps context efficient
|
|
116
110
|
|
|
117
111
|
User Context (on-demand by default):
|
|
118
|
-
- System message includes
|
|
119
|
-
-
|
|
112
|
+
- System message includes user email: "User: {email}"
|
|
113
|
+
- Fails silently if user not found - agent proceeds without user context
|
|
120
114
|
|
|
121
115
|
User Context (auto-inject when enabled):
|
|
122
116
|
- Set CHAT__AUTO_INJECT_USER_CONTEXT=true
|
|
@@ -137,9 +131,9 @@ class ContextBuilder:
|
|
|
137
131
|
|
|
138
132
|
# messages structure:
|
|
139
133
|
# [
|
|
140
|
-
# {"role": "system", "content": "Today's date: 2025-11-22\nUser: sarah@example.com
|
|
134
|
+
# {"role": "system", "content": "Today's date: 2025-11-22\n\nUser: sarah@example.com"},
|
|
141
135
|
# {"role": "user", "content": "Previous message"},
|
|
142
|
-
# {"role": "assistant", "content": "
|
|
136
|
+
# {"role": "assistant", "content": "Previous response"},
|
|
143
137
|
# {"role": "user", "content": "New message"}
|
|
144
138
|
# ]
|
|
145
139
|
"""
|
|
@@ -158,6 +152,7 @@ class ContextBuilder:
|
|
|
158
152
|
default_model=context.default_model,
|
|
159
153
|
agent_schema_uri=context.agent_schema_uri,
|
|
160
154
|
is_eval=context.is_eval,
|
|
155
|
+
client_id=context.client_id,
|
|
161
156
|
)
|
|
162
157
|
|
|
163
158
|
# Initialize DB if not provided and needed (for user context or session history)
|
|
@@ -177,6 +172,10 @@ class ContextBuilder:
|
|
|
177
172
|
today = datetime.now(timezone.utc).strftime("%Y-%m-%d")
|
|
178
173
|
context_hint = f"Today's date: {today}."
|
|
179
174
|
|
|
175
|
+
# Add client identifier if present
|
|
176
|
+
if context.client_id:
|
|
177
|
+
context_hint += f"\nClient: {context.client_id}"
|
|
178
|
+
|
|
180
179
|
# Add user context (auto-inject or on-demand hint)
|
|
181
180
|
if settings.chat.auto_inject_user_context and context.user_id and db:
|
|
182
181
|
# Auto-inject: Load and include user profile
|
|
@@ -189,18 +188,18 @@ class ContextBuilder:
|
|
|
189
188
|
context_hint += f"\n\nUser Context (auto-injected):\n{user_context_content}"
|
|
190
189
|
else:
|
|
191
190
|
context_hint += "\n\nNo user context available (anonymous or new user)."
|
|
192
|
-
elif context.user_id:
|
|
193
|
-
# On-demand:
|
|
194
|
-
#
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
191
|
+
elif context.user_id and db:
|
|
192
|
+
# On-demand: Show user email for context (no REM LOOKUP - it requires exact user_id match)
|
|
193
|
+
# Fail silently if user lookup fails - just proceed without user context
|
|
194
|
+
try:
|
|
195
|
+
user_repo = Repository(User, "users", db=db)
|
|
196
|
+
user = await user_repo.get_by_id(context.user_id, context.tenant_id)
|
|
197
|
+
if user and user.email:
|
|
198
|
+
context_hint += f"\n\nUser: {user.email}"
|
|
199
|
+
# If user not found, just proceed without adding user context
|
|
200
|
+
except Exception as e:
|
|
201
|
+
# Fail silently - don't block agent execution if user lookup fails
|
|
202
|
+
logger.debug(f"Could not load user context: {e}")
|
|
204
203
|
|
|
205
204
|
# Add system context hint
|
|
206
205
|
messages.append(ContextMessage(role="system", content=context_hint))
|
|
@@ -221,7 +220,12 @@ class ContextBuilder:
|
|
|
221
220
|
# can see previous tool results when the prompt is concatenated
|
|
222
221
|
for msg_dict in session_history:
|
|
223
222
|
role = msg_dict["role"]
|
|
224
|
-
content = msg_dict
|
|
223
|
+
content = msg_dict.get("content")
|
|
224
|
+
|
|
225
|
+
# Skip messages with null/empty content (common in tool messages)
|
|
226
|
+
if content is None or content == "":
|
|
227
|
+
logger.debug(f"Skipping {role} message with null/empty content")
|
|
228
|
+
continue
|
|
225
229
|
|
|
226
230
|
if role == "tool":
|
|
227
231
|
# Wrap tool results with clear markers for visibility
|
|
@@ -318,6 +322,7 @@ class ContextBuilder:
|
|
|
318
322
|
session_id: str | None = None,
|
|
319
323
|
message: str = "Hello",
|
|
320
324
|
model: str | None = None,
|
|
325
|
+
client_id: str | None = None,
|
|
321
326
|
) -> tuple[AgentContext, list[ContextMessage]]:
|
|
322
327
|
"""
|
|
323
328
|
Build context for testing (no database lookup).
|
|
@@ -325,7 +330,7 @@ class ContextBuilder:
|
|
|
325
330
|
Creates minimal context with:
|
|
326
331
|
- Test user (test@rem.ai)
|
|
327
332
|
- Test tenant
|
|
328
|
-
- Context hint with date
|
|
333
|
+
- Context hint with date and client
|
|
329
334
|
- Single user message
|
|
330
335
|
|
|
331
336
|
Args:
|
|
@@ -334,6 +339,7 @@ class ContextBuilder:
|
|
|
334
339
|
session_id: Optional session ID
|
|
335
340
|
message: User message content
|
|
336
341
|
model: Optional model override
|
|
342
|
+
client_id: Optional client identifier (e.g., "cli", "test")
|
|
337
343
|
|
|
338
344
|
Returns:
|
|
339
345
|
Tuple of (AgentContext, messages list)
|
|
@@ -341,7 +347,8 @@ class ContextBuilder:
|
|
|
341
347
|
Example:
|
|
342
348
|
context, messages = await ContextBuilder.build_from_test(
|
|
343
349
|
user_id="test@rem.ai",
|
|
344
|
-
message="What's the weather like?"
|
|
350
|
+
message="What's the weather like?",
|
|
351
|
+
client_id="cli"
|
|
345
352
|
)
|
|
346
353
|
"""
|
|
347
354
|
from ..settings import settings
|
|
@@ -352,11 +359,15 @@ class ContextBuilder:
|
|
|
352
359
|
tenant_id=tenant_id,
|
|
353
360
|
session_id=session_id,
|
|
354
361
|
default_model=model or settings.llm.default_model,
|
|
362
|
+
client_id=client_id,
|
|
355
363
|
)
|
|
356
364
|
|
|
357
365
|
# Build minimal messages
|
|
358
366
|
today = datetime.now(timezone.utc).strftime("%Y-%m-%d")
|
|
359
|
-
context_hint = f"Today's date: {today}
|
|
367
|
+
context_hint = f"Today's date: {today}."
|
|
368
|
+
if client_id:
|
|
369
|
+
context_hint += f"\nClient: {client_id}"
|
|
370
|
+
context_hint += f"\n\nTest user context: {user_id} (test mode, no profile loaded)."
|
|
360
371
|
|
|
361
372
|
messages = [
|
|
362
373
|
ContextMessage(role="system", content=context_hint),
|