remdb 0.3.172__py3-none-any.whl → 0.3.223__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/README.md +262 -2
- rem/agentic/context.py +173 -0
- rem/agentic/context_builder.py +12 -2
- rem/agentic/mcp/tool_wrapper.py +39 -16
- rem/agentic/providers/pydantic_ai.py +46 -43
- rem/agentic/schema.py +2 -2
- rem/agentic/tools/rem_tools.py +11 -0
- rem/api/main.py +1 -1
- rem/api/mcp_router/resources.py +64 -8
- rem/api/mcp_router/server.py +31 -24
- rem/api/mcp_router/tools.py +621 -166
- rem/api/routers/admin.py +30 -4
- rem/api/routers/auth.py +114 -15
- rem/api/routers/chat/completions.py +66 -18
- rem/api/routers/chat/sse_events.py +7 -3
- rem/api/routers/chat/streaming.py +254 -22
- rem/api/routers/common.py +18 -0
- rem/api/routers/dev.py +7 -1
- rem/api/routers/feedback.py +9 -1
- rem/api/routers/messages.py +176 -38
- rem/api/routers/models.py +9 -1
- rem/api/routers/query.py +12 -1
- rem/api/routers/shared_sessions.py +16 -0
- rem/auth/jwt.py +19 -4
- rem/auth/middleware.py +42 -28
- rem/cli/README.md +62 -0
- rem/cli/commands/ask.py +1 -1
- rem/cli/commands/db.py +148 -70
- rem/cli/commands/process.py +171 -43
- rem/models/entities/ontology.py +91 -101
- rem/schemas/agents/rem.yaml +1 -1
- rem/services/content/service.py +18 -5
- rem/services/email/service.py +11 -2
- rem/services/embeddings/worker.py +26 -12
- rem/services/postgres/__init__.py +28 -3
- rem/services/postgres/diff_service.py +57 -5
- rem/services/postgres/programmable_diff_service.py +635 -0
- rem/services/postgres/pydantic_to_sqlalchemy.py +2 -2
- rem/services/postgres/register_type.py +12 -11
- rem/services/postgres/repository.py +46 -25
- rem/services/postgres/schema_generator.py +5 -5
- rem/services/postgres/sql_builder.py +6 -5
- rem/services/session/__init__.py +8 -1
- rem/services/session/compression.py +40 -2
- rem/services/session/pydantic_messages.py +276 -0
- rem/settings.py +28 -0
- rem/sql/background_indexes.sql +5 -0
- rem/sql/migrations/001_install.sql +157 -10
- rem/sql/migrations/002_install_models.sql +160 -132
- rem/sql/migrations/004_cache_system.sql +7 -275
- rem/sql/migrations/migrate_session_id_to_uuid.sql +45 -0
- rem/utils/model_helpers.py +101 -0
- rem/utils/schema_loader.py +6 -6
- {remdb-0.3.172.dist-info → remdb-0.3.223.dist-info}/METADATA +1 -1
- {remdb-0.3.172.dist-info → remdb-0.3.223.dist-info}/RECORD +57 -53
- {remdb-0.3.172.dist-info → remdb-0.3.223.dist-info}/WHEEL +0 -0
- {remdb-0.3.172.dist-info → remdb-0.3.223.dist-info}/entry_points.txt +0 -0
|
@@ -579,60 +579,62 @@ async def create_agent(
|
|
|
579
579
|
# Extract schema fields using typed helpers
|
|
580
580
|
from ..schema import get_system_prompt, get_metadata
|
|
581
581
|
|
|
582
|
-
# Track whether mcp_servers was explicitly configured (even if empty)
|
|
583
|
-
mcp_servers_explicitly_set = False
|
|
584
|
-
|
|
585
582
|
if agent_schema:
|
|
586
583
|
system_prompt = get_system_prompt(agent_schema)
|
|
587
584
|
metadata = get_metadata(agent_schema)
|
|
588
|
-
# Check if mcp_servers was explicitly set (could be empty list to disable)
|
|
589
|
-
if hasattr(metadata, 'mcp_servers') and metadata.mcp_servers is not None:
|
|
590
|
-
mcp_server_configs = [s.model_dump() for s in metadata.mcp_servers]
|
|
591
|
-
mcp_servers_explicitly_set = True
|
|
592
|
-
else:
|
|
593
|
-
mcp_server_configs = []
|
|
594
585
|
resource_configs = metadata.resources if hasattr(metadata, 'resources') else []
|
|
595
586
|
|
|
587
|
+
# DEPRECATED: mcp_servers in agent schemas is ignored
|
|
588
|
+
# MCP servers are now always auto-detected at the application level
|
|
589
|
+
if hasattr(metadata, 'mcp_servers') and metadata.mcp_servers:
|
|
590
|
+
logger.warning(
|
|
591
|
+
"DEPRECATED: mcp_servers in agent schema is ignored. "
|
|
592
|
+
"MCP servers are auto-detected from tools.mcp_server module. "
|
|
593
|
+
"Remove mcp_servers from your agent schema."
|
|
594
|
+
)
|
|
595
|
+
|
|
596
596
|
if metadata.system_prompt:
|
|
597
597
|
logger.debug("Using custom system_prompt from json_schema_extra")
|
|
598
598
|
else:
|
|
599
599
|
system_prompt = ""
|
|
600
600
|
metadata = None
|
|
601
|
-
mcp_server_configs = []
|
|
602
601
|
resource_configs = []
|
|
603
602
|
|
|
604
|
-
# Auto-detect
|
|
605
|
-
#
|
|
606
|
-
#
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
603
|
+
# Auto-detect MCP server at application level
|
|
604
|
+
# Convention: tools/mcp_server.py exports `mcp` FastMCP instance
|
|
605
|
+
# Falls back to REM's built-in MCP server if no local server found
|
|
606
|
+
import importlib
|
|
607
|
+
import os
|
|
608
|
+
import sys
|
|
609
|
+
|
|
610
|
+
# Ensure current working directory is in sys.path for local imports
|
|
611
|
+
cwd = os.getcwd()
|
|
612
|
+
if cwd not in sys.path:
|
|
613
|
+
sys.path.insert(0, cwd)
|
|
614
|
+
|
|
615
|
+
mcp_server_configs = []
|
|
616
|
+
auto_detect_modules = [
|
|
617
|
+
"tools.mcp_server", # Convention: tools/mcp_server.py
|
|
618
|
+
"mcp_server", # Alternative: mcp_server.py in root
|
|
619
|
+
]
|
|
620
|
+
for module_path in auto_detect_modules:
|
|
621
|
+
try:
|
|
622
|
+
mcp_module = importlib.import_module(module_path)
|
|
623
|
+
if hasattr(mcp_module, "mcp"):
|
|
624
|
+
logger.info(f"Auto-detected local MCP server: {module_path}")
|
|
625
|
+
mcp_server_configs = [{"type": "local", "module": module_path, "id": "auto-detected"}]
|
|
626
|
+
break
|
|
627
|
+
except ImportError as e:
|
|
628
|
+
logger.debug(f"MCP server auto-detect: {module_path} not found ({e})")
|
|
629
|
+
continue
|
|
630
|
+
except Exception as e:
|
|
631
|
+
logger.warning(f"MCP server auto-detect: {module_path} failed to load: {e}")
|
|
632
|
+
continue
|
|
633
|
+
|
|
634
|
+
# Fall back to REM's default MCP server if no local server found
|
|
635
|
+
if not mcp_server_configs:
|
|
636
|
+
logger.info("No local MCP server found, using REM default (rem.mcp_server)")
|
|
637
|
+
mcp_server_configs = [{"type": "local", "module": "rem.mcp_server", "id": "rem"}]
|
|
636
638
|
|
|
637
639
|
# Extract temperature and max_iterations from schema metadata (with fallback to settings defaults)
|
|
638
640
|
if metadata:
|
|
@@ -730,7 +732,7 @@ async def create_agent(
|
|
|
730
732
|
# the artificial MCP distinction between tools and resources
|
|
731
733
|
#
|
|
732
734
|
# Supports both concrete and template URIs:
|
|
733
|
-
# - Concrete: "rem://
|
|
735
|
+
# - Concrete: "rem://agents" -> no-param tool
|
|
734
736
|
# - Template: "patient-profile://field/{field_key}" -> tool with field_key param
|
|
735
737
|
from ..mcp.tool_wrapper import create_resource_tool
|
|
736
738
|
|
|
@@ -766,6 +768,7 @@ async def create_agent(
|
|
|
766
768
|
|
|
767
769
|
# Create tools from collected resource URIs
|
|
768
770
|
# Pass the loaded MCP server so resources can be resolved from it
|
|
771
|
+
logger.info(f"Creating {len(resource_uris)} resource tools with mcp_server={'set' if loaded_mcp_server else 'None'}")
|
|
769
772
|
for uri, usage in resource_uris:
|
|
770
773
|
resource_tool = create_resource_tool(uri, usage, mcp_server=loaded_mcp_server)
|
|
771
774
|
tools.append(resource_tool)
|
rem/agentic/schema.py
CHANGED
|
@@ -79,7 +79,7 @@ class MCPResourceReference(BaseModel):
|
|
|
79
79
|
|
|
80
80
|
Example (exact URI):
|
|
81
81
|
{
|
|
82
|
-
"uri": "rem://
|
|
82
|
+
"uri": "rem://agents",
|
|
83
83
|
"name": "Agent Schemas",
|
|
84
84
|
"description": "List all available agent schemas"
|
|
85
85
|
}
|
|
@@ -96,7 +96,7 @@ class MCPResourceReference(BaseModel):
|
|
|
96
96
|
default=None,
|
|
97
97
|
description=(
|
|
98
98
|
"Exact resource URI or URI with query parameters. "
|
|
99
|
-
"Examples: 'rem://
|
|
99
|
+
"Examples: 'rem://agents', 'rem://resources?category=drug.*'"
|
|
100
100
|
)
|
|
101
101
|
)
|
|
102
102
|
|
rem/agentic/tools/rem_tools.py
CHANGED
|
@@ -3,6 +3,17 @@ REM tools for agent execution (CLI and API compatible).
|
|
|
3
3
|
|
|
4
4
|
These tools work in both CLI and API contexts by initializing services on-demand.
|
|
5
5
|
They wrap the service layer directly, not MCP tools.
|
|
6
|
+
|
|
7
|
+
Core tables (always available):
|
|
8
|
+
- resources: Documents, content chunks, artifacts
|
|
9
|
+
- moments: Temporal narratives extracted from resources (usually user-specific)
|
|
10
|
+
- ontologies: Domain entities with semantic links for further lookups (like a wiki)
|
|
11
|
+
|
|
12
|
+
Other tables (may vary by deployment):
|
|
13
|
+
- users, sessions, messages, files, schemas, feedbacks
|
|
14
|
+
|
|
15
|
+
Note: Not all tables are populated in all systems. Use FUZZY or SEARCH
|
|
16
|
+
to discover what data exists before assuming specific tables have content.
|
|
6
17
|
"""
|
|
7
18
|
|
|
8
19
|
from typing import Any, Literal, cast
|
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,
|
rem/api/mcp_router/resources.py
CHANGED
|
@@ -349,6 +349,53 @@ def register_agent_resources(mcp: FastMCP):
|
|
|
349
349
|
except Exception as e:
|
|
350
350
|
return f"# Available Agents\n\nError listing agents: {e}"
|
|
351
351
|
|
|
352
|
+
@mcp.resource("rem://agents/{agent_name}")
|
|
353
|
+
def get_agent_schema(agent_name: str) -> str:
|
|
354
|
+
"""
|
|
355
|
+
Get a specific agent schema by name.
|
|
356
|
+
|
|
357
|
+
Args:
|
|
358
|
+
agent_name: Name of the agent (e.g., "ask_rem", "agent-builder")
|
|
359
|
+
|
|
360
|
+
Returns:
|
|
361
|
+
Full agent schema as YAML string, or error message if not found.
|
|
362
|
+
"""
|
|
363
|
+
import importlib.resources
|
|
364
|
+
import yaml
|
|
365
|
+
from pathlib import Path
|
|
366
|
+
|
|
367
|
+
try:
|
|
368
|
+
# Find packaged agent schemas
|
|
369
|
+
agents_ref = importlib.resources.files("rem") / "schemas" / "agents"
|
|
370
|
+
agents_dir = Path(str(agents_ref))
|
|
371
|
+
|
|
372
|
+
if not agents_dir.exists():
|
|
373
|
+
return f"# Agent Not Found\n\nNo agent schemas directory found."
|
|
374
|
+
|
|
375
|
+
# Search for agent file (try multiple extensions)
|
|
376
|
+
for ext in [".yaml", ".yml", ".json"]:
|
|
377
|
+
# Try exact match first
|
|
378
|
+
agent_file = agents_dir / f"{agent_name}{ext}"
|
|
379
|
+
if agent_file.exists():
|
|
380
|
+
with open(agent_file, "r") as f:
|
|
381
|
+
content = f.read()
|
|
382
|
+
return f"# Agent Schema: {agent_name}\n\n```yaml\n{content}\n```"
|
|
383
|
+
|
|
384
|
+
# Try recursive search
|
|
385
|
+
matches = list(agents_dir.rglob(f"{agent_name}{ext}"))
|
|
386
|
+
if matches:
|
|
387
|
+
with open(matches[0], "r") as f:
|
|
388
|
+
content = f.read()
|
|
389
|
+
return f"# Agent Schema: {agent_name}\n\n```yaml\n{content}\n```"
|
|
390
|
+
|
|
391
|
+
# Not found - list available agents
|
|
392
|
+
available = [f.stem for f in agents_dir.rglob("*.yaml")] + \
|
|
393
|
+
[f.stem for f in agents_dir.rglob("*.yml")]
|
|
394
|
+
return f"# Agent Not Found\n\nAgent '{agent_name}' not found.\n\nAvailable agents: {', '.join(sorted(set(available)))}"
|
|
395
|
+
|
|
396
|
+
except Exception as e:
|
|
397
|
+
return f"# Error\n\nError loading agent '{agent_name}': {e}"
|
|
398
|
+
|
|
352
399
|
|
|
353
400
|
def register_file_resources(mcp: FastMCP):
|
|
354
401
|
"""
|
|
@@ -501,10 +548,11 @@ async def load_resource(uri: str) -> dict | str:
|
|
|
501
548
|
Load an MCP resource by URI.
|
|
502
549
|
|
|
503
550
|
This function is called by the read_resource tool to dispatch to
|
|
504
|
-
registered resource handlers.
|
|
551
|
+
registered resource handlers. Supports both regular resources and
|
|
552
|
+
parameterized resource templates (e.g., rem://agents/{agent_name}).
|
|
505
553
|
|
|
506
554
|
Args:
|
|
507
|
-
uri: Resource URI (e.g., "rem://
|
|
555
|
+
uri: Resource URI (e.g., "rem://agents", "rem://agents/ask_rem", "rem://status")
|
|
508
556
|
|
|
509
557
|
Returns:
|
|
510
558
|
Resource data (dict or string)
|
|
@@ -524,18 +572,26 @@ async def load_resource(uri: str) -> dict | str:
|
|
|
524
572
|
register_file_resources(mcp)
|
|
525
573
|
register_status_resources(mcp)
|
|
526
574
|
|
|
527
|
-
#
|
|
575
|
+
# 1. Try exact match in regular resources
|
|
528
576
|
resources = await mcp.get_resources()
|
|
529
|
-
|
|
530
577
|
if uri in resources:
|
|
531
578
|
resource = resources[uri]
|
|
532
|
-
# Call the underlying function
|
|
533
579
|
result = resource.fn()
|
|
534
|
-
# Handle async functions
|
|
535
580
|
if inspect.iscoroutine(result):
|
|
536
581
|
result = await result
|
|
537
582
|
return result if result else {"error": "Resource returned None"}
|
|
538
583
|
|
|
539
|
-
#
|
|
540
|
-
|
|
584
|
+
# 2. Try matching against parameterized resource templates
|
|
585
|
+
templates = await mcp.get_resource_templates()
|
|
586
|
+
for template_uri, template in templates.items():
|
|
587
|
+
params = template.matches(uri)
|
|
588
|
+
if params is not None:
|
|
589
|
+
# Template matched - call function with extracted parameters
|
|
590
|
+
result = template.fn(**params)
|
|
591
|
+
if inspect.iscoroutine(result):
|
|
592
|
+
result = await result
|
|
593
|
+
return result if result else {"error": "Resource returned None"}
|
|
594
|
+
|
|
595
|
+
# 3. Not found - include both resources and templates in error
|
|
596
|
+
available = list(resources.keys()) + list(templates.keys())
|
|
541
597
|
raise ValueError(f"Resource not found: {uri}. Available resources: {available}")
|
rem/api/mcp_router/server.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
MCP server creation and configuration for REM.
|
|
3
3
|
|
|
4
|
-
Design Pattern
|
|
4
|
+
Design Pattern
|
|
5
5
|
1. Create FastMCP server with tools and resources
|
|
6
6
|
2. Register tools using @mcp.tool() decorator
|
|
7
7
|
3. Register resources using resource registration functions
|
|
@@ -20,10 +20,31 @@ FastMCP Features:
|
|
|
20
20
|
"""
|
|
21
21
|
|
|
22
22
|
import importlib.metadata
|
|
23
|
+
from functools import wraps
|
|
23
24
|
|
|
24
25
|
from fastmcp import FastMCP
|
|
26
|
+
from loguru import logger
|
|
25
27
|
|
|
26
28
|
from ...settings import settings
|
|
29
|
+
from .prompts import register_prompts
|
|
30
|
+
from .resources import (
|
|
31
|
+
register_agent_resources,
|
|
32
|
+
register_file_resources,
|
|
33
|
+
register_schema_resources,
|
|
34
|
+
register_status_resources,
|
|
35
|
+
)
|
|
36
|
+
from .tools import (
|
|
37
|
+
ask_agent,
|
|
38
|
+
ask_rem_agent,
|
|
39
|
+
get_schema,
|
|
40
|
+
ingest_into_rem,
|
|
41
|
+
list_schema,
|
|
42
|
+
read_resource,
|
|
43
|
+
register_metadata,
|
|
44
|
+
save_agent,
|
|
45
|
+
search_rem,
|
|
46
|
+
test_error_handling,
|
|
47
|
+
)
|
|
27
48
|
|
|
28
49
|
# Get package version
|
|
29
50
|
try:
|
|
@@ -174,18 +195,7 @@ def create_mcp_server(is_local: bool = False) -> FastMCP:
|
|
|
174
195
|
),
|
|
175
196
|
)
|
|
176
197
|
|
|
177
|
-
# Register REM tools
|
|
178
|
-
from .tools import (
|
|
179
|
-
ask_rem_agent,
|
|
180
|
-
get_schema,
|
|
181
|
-
ingest_into_rem,
|
|
182
|
-
list_schema,
|
|
183
|
-
read_resource,
|
|
184
|
-
register_metadata,
|
|
185
|
-
save_agent,
|
|
186
|
-
search_rem,
|
|
187
|
-
)
|
|
188
|
-
|
|
198
|
+
# Register core REM tools
|
|
189
199
|
mcp.tool()(search_rem)
|
|
190
200
|
mcp.tool()(ask_rem_agent)
|
|
191
201
|
mcp.tool()(read_resource)
|
|
@@ -194,10 +204,16 @@ def create_mcp_server(is_local: bool = False) -> FastMCP:
|
|
|
194
204
|
mcp.tool()(get_schema)
|
|
195
205
|
mcp.tool()(save_agent)
|
|
196
206
|
|
|
207
|
+
# Register multi-agent tools
|
|
208
|
+
mcp.tool()(ask_agent)
|
|
209
|
+
|
|
210
|
+
# Register test tool only in development environment (not staging/production)
|
|
211
|
+
if settings.environment not in ("staging", "production"):
|
|
212
|
+
mcp.tool()(test_error_handling)
|
|
213
|
+
logger.debug("Registered test_error_handling tool (dev environment only)")
|
|
214
|
+
|
|
197
215
|
# File ingestion tool (with local path support for local servers)
|
|
198
216
|
# Wrap to inject is_local parameter
|
|
199
|
-
from functools import wraps
|
|
200
|
-
|
|
201
217
|
@wraps(ingest_into_rem)
|
|
202
218
|
async def ingest_into_rem_wrapper(
|
|
203
219
|
file_uri: str,
|
|
@@ -216,18 +232,9 @@ def create_mcp_server(is_local: bool = False) -> FastMCP:
|
|
|
216
232
|
mcp.tool()(ingest_into_rem_wrapper)
|
|
217
233
|
|
|
218
234
|
# Register prompts
|
|
219
|
-
from .prompts import register_prompts
|
|
220
|
-
|
|
221
235
|
register_prompts(mcp)
|
|
222
236
|
|
|
223
237
|
# Register schema resources
|
|
224
|
-
from .resources import (
|
|
225
|
-
register_agent_resources,
|
|
226
|
-
register_file_resources,
|
|
227
|
-
register_schema_resources,
|
|
228
|
-
register_status_resources,
|
|
229
|
-
)
|
|
230
|
-
|
|
231
238
|
register_schema_resources(mcp)
|
|
232
239
|
register_agent_resources(mcp)
|
|
233
240
|
register_file_resources(mcp)
|