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

Files changed (83) hide show
  1. rem/agentic/agents/__init__.py +16 -0
  2. rem/agentic/agents/agent_manager.py +311 -0
  3. rem/agentic/agents/sse_simulator.py +2 -0
  4. rem/agentic/context.py +103 -5
  5. rem/agentic/context_builder.py +36 -9
  6. rem/agentic/mcp/tool_wrapper.py +161 -18
  7. rem/agentic/otel/setup.py +1 -0
  8. rem/agentic/providers/phoenix.py +371 -108
  9. rem/agentic/providers/pydantic_ai.py +172 -30
  10. rem/agentic/schema.py +8 -4
  11. rem/api/deps.py +3 -5
  12. rem/api/main.py +26 -4
  13. rem/api/mcp_router/resources.py +15 -10
  14. rem/api/mcp_router/server.py +11 -3
  15. rem/api/mcp_router/tools.py +418 -4
  16. rem/api/middleware/tracking.py +5 -5
  17. rem/api/routers/admin.py +218 -1
  18. rem/api/routers/auth.py +349 -6
  19. rem/api/routers/chat/completions.py +255 -7
  20. rem/api/routers/chat/models.py +81 -7
  21. rem/api/routers/chat/otel_utils.py +33 -0
  22. rem/api/routers/chat/sse_events.py +17 -1
  23. rem/api/routers/chat/streaming.py +126 -19
  24. rem/api/routers/feedback.py +134 -14
  25. rem/api/routers/messages.py +24 -15
  26. rem/api/routers/query.py +6 -3
  27. rem/auth/__init__.py +13 -3
  28. rem/auth/jwt.py +352 -0
  29. rem/auth/middleware.py +115 -10
  30. rem/auth/providers/__init__.py +4 -1
  31. rem/auth/providers/email.py +215 -0
  32. rem/cli/commands/README.md +42 -0
  33. rem/cli/commands/cluster.py +617 -168
  34. rem/cli/commands/configure.py +4 -7
  35. rem/cli/commands/db.py +66 -22
  36. rem/cli/commands/experiments.py +468 -76
  37. rem/cli/commands/schema.py +6 -5
  38. rem/cli/commands/session.py +336 -0
  39. rem/cli/dreaming.py +2 -2
  40. rem/cli/main.py +2 -0
  41. rem/config.py +8 -1
  42. rem/models/core/experiment.py +58 -14
  43. rem/models/entities/__init__.py +4 -0
  44. rem/models/entities/ontology.py +1 -1
  45. rem/models/entities/ontology_config.py +1 -1
  46. rem/models/entities/subscriber.py +175 -0
  47. rem/models/entities/user.py +1 -0
  48. rem/schemas/agents/core/agent-builder.yaml +235 -0
  49. rem/schemas/agents/examples/contract-analyzer.yaml +1 -1
  50. rem/schemas/agents/examples/contract-extractor.yaml +1 -1
  51. rem/schemas/agents/examples/cv-parser.yaml +1 -1
  52. rem/services/__init__.py +3 -1
  53. rem/services/content/service.py +4 -3
  54. rem/services/email/__init__.py +10 -0
  55. rem/services/email/service.py +513 -0
  56. rem/services/email/templates.py +360 -0
  57. rem/services/phoenix/client.py +59 -18
  58. rem/services/postgres/README.md +38 -0
  59. rem/services/postgres/diff_service.py +127 -6
  60. rem/services/postgres/pydantic_to_sqlalchemy.py +45 -13
  61. rem/services/postgres/repository.py +5 -4
  62. rem/services/postgres/schema_generator.py +205 -4
  63. rem/services/session/compression.py +120 -50
  64. rem/services/session/reload.py +14 -7
  65. rem/services/user_service.py +41 -9
  66. rem/settings.py +442 -23
  67. rem/sql/migrations/001_install.sql +156 -0
  68. rem/sql/migrations/002_install_models.sql +1951 -88
  69. rem/sql/migrations/004_cache_system.sql +548 -0
  70. rem/sql/migrations/005_schema_update.sql +145 -0
  71. rem/utils/README.md +45 -0
  72. rem/utils/__init__.py +18 -0
  73. rem/utils/files.py +157 -1
  74. rem/utils/schema_loader.py +139 -10
  75. rem/utils/sql_paths.py +146 -0
  76. rem/utils/vision.py +1 -1
  77. rem/workers/__init__.py +3 -1
  78. rem/workers/db_listener.py +579 -0
  79. rem/workers/unlogged_maintainer.py +463 -0
  80. {remdb-0.3.114.dist-info → remdb-0.3.172.dist-info}/METADATA +218 -180
  81. {remdb-0.3.114.dist-info → remdb-0.3.172.dist-info}/RECORD +83 -68
  82. {remdb-0.3.114.dist-info → remdb-0.3.172.dist-info}/WHEEL +0 -0
  83. {remdb-0.3.114.dist-info → remdb-0.3.172.dist-info}/entry_points.txt +0 -0
@@ -28,7 +28,12 @@ def create_pydantic_tool(func: Callable[..., Any]) -> Tool:
28
28
  return Tool(func)
29
29
 
30
30
 
31
- def create_mcp_tool_wrapper(tool_name: str, mcp_tool: Any, user_id: str | None = None) -> Tool:
31
+ def create_mcp_tool_wrapper(
32
+ tool_name: str,
33
+ mcp_tool: Any,
34
+ user_id: str | None = None,
35
+ description_suffix: str | None = None,
36
+ ) -> Tool:
32
37
  """
33
38
  Create a Pydantic AI Tool from a FastMCP FunctionTool.
34
39
 
@@ -40,6 +45,8 @@ def create_mcp_tool_wrapper(tool_name: str, mcp_tool: Any, user_id: str | None =
40
45
  tool_name: Name of the MCP tool
41
46
  mcp_tool: The FastMCP FunctionTool object
42
47
  user_id: Optional user_id to inject into tool calls
48
+ description_suffix: Optional text to append to the tool's docstring.
49
+ Used to add schema-specific context (e.g., default table for search_rem).
43
50
 
44
51
  Returns:
45
52
  A Pydantic AI Tool instance
@@ -52,7 +59,11 @@ def create_mcp_tool_wrapper(tool_name: str, mcp_tool: Any, user_id: str | None =
52
59
  sig = inspect.signature(tool_func)
53
60
  has_user_id = "user_id" in sig.parameters
54
61
 
55
- # If we need to inject user_id, create a wrapper
62
+ # Build the docstring with optional suffix
63
+ base_doc = tool_func.__doc__ or ""
64
+ final_doc = base_doc + description_suffix if description_suffix else base_doc
65
+
66
+ # If we need to inject user_id or modify docstring, create a wrapper
56
67
  # Otherwise, use the function directly for better signature preservation
57
68
  if user_id and has_user_id:
58
69
  async def wrapped_tool(**kwargs) -> Any:
@@ -69,39 +80,171 @@ def create_mcp_tool_wrapper(tool_name: str, mcp_tool: Any, user_id: str | None =
69
80
 
70
81
  # Copy signature from original function for Pydantic AI inspection
71
82
  wrapped_tool.__name__ = tool_name
72
- wrapped_tool.__doc__ = tool_func.__doc__
83
+ wrapped_tool.__doc__ = final_doc
73
84
  wrapped_tool.__annotations__ = tool_func.__annotations__
74
85
  wrapped_tool.__signature__ = sig # Important: preserve full signature
75
86
 
76
87
  logger.debug(f"Creating MCP tool wrapper with user_id injection: {tool_name}")
77
88
  return Tool(wrapped_tool)
89
+ elif description_suffix:
90
+ # Need to wrap just for docstring modification
91
+ async def wrapped_tool(**kwargs) -> Any:
92
+ """Wrapper for docstring modification."""
93
+ valid_params = set(sig.parameters.keys())
94
+ filtered_kwargs = {k: v for k, v in kwargs.items() if k in valid_params}
95
+ return await tool_func(**filtered_kwargs)
96
+
97
+ wrapped_tool.__name__ = tool_name
98
+ wrapped_tool.__doc__ = final_doc
99
+ wrapped_tool.__annotations__ = tool_func.__annotations__
100
+ wrapped_tool.__signature__ = sig
101
+
102
+ logger.debug(f"Creating MCP tool wrapper with description suffix: {tool_name}")
103
+ return Tool(wrapped_tool)
78
104
  else:
79
105
  # No injection needed - use original function directly
80
106
  logger.debug(f"Creating MCP tool wrapper (no injection): {tool_name}")
81
107
  return Tool(tool_func)
82
108
 
83
109
 
84
- def create_resource_tool(uri: str, usage: str) -> Tool:
110
+ def create_resource_tool(uri: str, usage: str = "", mcp_server: Any = None) -> Tool:
85
111
  """
86
112
  Build a Tool instance from an MCP resource URI.
87
113
 
88
- This is a placeholder for now. A real implementation would create a
89
- tool that reads the content of the resource URI.
114
+ Creates a tool that fetches the resource content when called.
115
+ Resources declared in agent YAML become callable tools - this eliminates
116
+ the artificial MCP distinction between tools and resources.
117
+
118
+ Supports both:
119
+ - Concrete URIs: "rem://schemas" -> tool with no parameters
120
+ - Template URIs: "patient-profile://field/{field_key}" -> tool with field_key parameter
90
121
 
91
122
  Args:
92
- uri: The resource URI (e.g., "rem://resources/some-id").
93
- usage: The description of how to use the tool.
123
+ uri: The resource URI (concrete or template with {variable} placeholders).
124
+ usage: The description of what this resource provides.
125
+ mcp_server: Optional FastMCP server instance to resolve resources from.
126
+ If provided, resources are resolved from this server's registry.
127
+ If not provided, falls back to REM's built-in load_resource().
94
128
 
95
129
  Returns:
96
- A Pydantic AI Tool instance.
97
- """
98
- # Placeholder function that would read the resource
99
- def read_resource():
100
- """Reads content from a resource URI."""
101
- return f"Content of {uri}"
130
+ A Pydantic AI Tool instance that fetches the resource.
102
131
 
103
- read_resource.__name__ = f"read_{uri.replace('://', '_').replace('/', '_')}"
104
- read_resource.__doc__ = usage
132
+ Example:
133
+ # Concrete URI -> no-param tool
134
+ tool = create_resource_tool("rem://schemas", "List all agent schemas")
105
135
 
106
- logger.info(f"Built resource tool: {read_resource.__name__} (uri: {uri})")
107
- return Tool(read_resource)
136
+ # Template URI -> parameterized tool
137
+ tool = create_resource_tool("patient-profile://field/{field_key}", "Get field definition", mcp_server=mcp)
138
+ # Agent calls: get_patient_profile_field(field_key="safety.suicidality")
139
+ """
140
+ import json
141
+ import re
142
+
143
+ # Extract template variables from URI (e.g., {field_key}, {domain_name})
144
+ template_vars = re.findall(r'\{([^}]+)\}', uri)
145
+
146
+ # Parse URI to create function name (strip template vars for cleaner name)
147
+ clean_uri = re.sub(r'\{[^}]+\}', '', uri)
148
+ parts = clean_uri.replace("://", "_").replace("-", "_").replace("/", "_").replace(".", "_")
149
+ parts = re.sub(r'_+', '_', parts).strip('_') # Clean up multiple underscores
150
+ func_name = f"get_{parts}"
151
+
152
+ # For parameterized URIs, append _by_{params} to avoid naming conflicts
153
+ # e.g., rem://agents/{name} -> get_rem_agents_by_name (distinct from get_rem_agents)
154
+ if template_vars:
155
+ param_suffix = "_by_" + "_".join(template_vars)
156
+ func_name = f"{func_name}{param_suffix}"
157
+
158
+ # Build description including parameter info
159
+ description = usage or f"Fetch {uri} resource"
160
+ if template_vars:
161
+ param_desc = ", ".join(template_vars)
162
+ description = f"{description}\n\nParameters: {param_desc}"
163
+
164
+ if template_vars:
165
+ # Template URI -> create parameterized tool
166
+ async def wrapper(**kwargs: Any) -> str:
167
+ """Fetch MCP resource with substituted parameters."""
168
+ import asyncio
169
+ import inspect
170
+
171
+ # Try to resolve from MCP server's resource templates first
172
+ if mcp_server is not None:
173
+ try:
174
+ # Get resource templates from MCP server
175
+ templates = await mcp_server.get_resource_templates()
176
+ if uri in templates:
177
+ template = templates[uri]
178
+ # Call the template's underlying function directly
179
+ # The fn expects the template variables as kwargs
180
+ fn_result = template.fn(**kwargs)
181
+ # Handle both sync and async functions
182
+ if inspect.iscoroutine(fn_result):
183
+ fn_result = await fn_result
184
+ if isinstance(fn_result, str):
185
+ return fn_result
186
+ return json.dumps(fn_result, indent=2)
187
+ except Exception as e:
188
+ logger.warning(f"Failed to resolve resource {uri} from MCP server: {e}")
189
+
190
+ # Fallback: substitute template variables and use load_resource
191
+ resolved_uri = uri
192
+ for var in template_vars:
193
+ if var in kwargs:
194
+ resolved_uri = resolved_uri.replace(f"{{{var}}}", str(kwargs[var]))
195
+ else:
196
+ return json.dumps({"error": f"Missing required parameter: {var}"})
197
+
198
+ from rem.api.mcp_router.resources import load_resource
199
+ result = await load_resource(resolved_uri)
200
+ if isinstance(result, str):
201
+ return result
202
+ return json.dumps(result, indent=2)
203
+
204
+ # Build parameter annotations for Pydantic AI
205
+ wrapper.__name__ = func_name
206
+ wrapper.__doc__ = description
207
+ # Add type hints for parameters
208
+ wrapper.__annotations__ = {var: str for var in template_vars}
209
+ wrapper.__annotations__['return'] = str
210
+
211
+ logger.info(f"Built parameterized resource tool: {func_name} (uri: {uri}, params: {template_vars})")
212
+ else:
213
+ # Concrete URI -> no-param tool
214
+ async def wrapper(**kwargs: Any) -> str:
215
+ """Fetch MCP resource and return contents."""
216
+ import asyncio
217
+ import inspect
218
+
219
+ if kwargs:
220
+ logger.warning(f"Resource tool {func_name} called with unexpected kwargs: {list(kwargs.keys())}")
221
+
222
+ # Try to resolve from MCP server's resources first
223
+ if mcp_server is not None:
224
+ try:
225
+ resources = await mcp_server.get_resources()
226
+ if uri in resources:
227
+ resource = resources[uri]
228
+ # Call the resource's underlying function
229
+ fn_result = resource.fn()
230
+ if inspect.iscoroutine(fn_result):
231
+ fn_result = await fn_result
232
+ if isinstance(fn_result, str):
233
+ return fn_result
234
+ return json.dumps(fn_result, indent=2)
235
+ except Exception as e:
236
+ logger.warning(f"Failed to resolve resource {uri} from MCP server: {e}")
237
+
238
+ # Fallback to load_resource
239
+ from rem.api.mcp_router.resources import load_resource
240
+ result = await load_resource(uri)
241
+ if isinstance(result, str):
242
+ return result
243
+ return json.dumps(result, indent=2)
244
+
245
+ wrapper.__name__ = func_name
246
+ wrapper.__doc__ = description
247
+
248
+ logger.info(f"Built resource tool: {func_name} (uri: {uri})")
249
+
250
+ return Tool(wrapper)
rem/agentic/otel/setup.py CHANGED
@@ -158,6 +158,7 @@ def setup_instrumentation() -> None:
158
158
  base_exporter = GRPCExporter(
159
159
  endpoint=settings.otel.collector_endpoint,
160
160
  timeout=settings.otel.export_timeout,
161
+ insecure=settings.otel.insecure,
161
162
  )
162
163
  else: # http
163
164
  base_exporter = HTTPExporter(