remdb 0.3.0__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/__init__.py +2 -0
- rem/agentic/README.md +650 -0
- rem/agentic/__init__.py +39 -0
- rem/agentic/agents/README.md +155 -0
- rem/agentic/agents/__init__.py +8 -0
- rem/agentic/context.py +148 -0
- rem/agentic/context_builder.py +329 -0
- rem/agentic/mcp/__init__.py +0 -0
- rem/agentic/mcp/tool_wrapper.py +107 -0
- rem/agentic/otel/__init__.py +5 -0
- rem/agentic/otel/setup.py +151 -0
- rem/agentic/providers/phoenix.py +674 -0
- rem/agentic/providers/pydantic_ai.py +572 -0
- rem/agentic/query.py +117 -0
- rem/agentic/query_helper.py +89 -0
- rem/agentic/schema.py +396 -0
- rem/agentic/serialization.py +245 -0
- rem/agentic/tools/__init__.py +5 -0
- rem/agentic/tools/rem_tools.py +231 -0
- rem/api/README.md +420 -0
- rem/api/main.py +324 -0
- rem/api/mcp_router/prompts.py +182 -0
- rem/api/mcp_router/resources.py +536 -0
- rem/api/mcp_router/server.py +213 -0
- rem/api/mcp_router/tools.py +584 -0
- rem/api/routers/auth.py +229 -0
- rem/api/routers/chat/__init__.py +5 -0
- rem/api/routers/chat/completions.py +281 -0
- rem/api/routers/chat/json_utils.py +76 -0
- rem/api/routers/chat/models.py +124 -0
- rem/api/routers/chat/streaming.py +185 -0
- rem/auth/README.md +258 -0
- rem/auth/__init__.py +26 -0
- rem/auth/middleware.py +100 -0
- rem/auth/providers/__init__.py +13 -0
- rem/auth/providers/base.py +376 -0
- rem/auth/providers/google.py +163 -0
- rem/auth/providers/microsoft.py +237 -0
- rem/cli/README.md +455 -0
- rem/cli/__init__.py +8 -0
- rem/cli/commands/README.md +126 -0
- rem/cli/commands/__init__.py +3 -0
- rem/cli/commands/ask.py +566 -0
- rem/cli/commands/configure.py +497 -0
- rem/cli/commands/db.py +493 -0
- rem/cli/commands/dreaming.py +324 -0
- rem/cli/commands/experiments.py +1302 -0
- rem/cli/commands/mcp.py +66 -0
- rem/cli/commands/process.py +245 -0
- rem/cli/commands/schema.py +183 -0
- rem/cli/commands/serve.py +106 -0
- rem/cli/dreaming.py +363 -0
- rem/cli/main.py +96 -0
- rem/config.py +237 -0
- rem/mcp_server.py +41 -0
- rem/models/core/__init__.py +49 -0
- rem/models/core/core_model.py +64 -0
- rem/models/core/engram.py +333 -0
- rem/models/core/experiment.py +628 -0
- rem/models/core/inline_edge.py +132 -0
- rem/models/core/rem_query.py +243 -0
- rem/models/entities/__init__.py +43 -0
- rem/models/entities/file.py +57 -0
- rem/models/entities/image_resource.py +88 -0
- rem/models/entities/message.py +35 -0
- rem/models/entities/moment.py +123 -0
- rem/models/entities/ontology.py +191 -0
- rem/models/entities/ontology_config.py +131 -0
- rem/models/entities/resource.py +95 -0
- rem/models/entities/schema.py +87 -0
- rem/models/entities/user.py +85 -0
- rem/py.typed +0 -0
- rem/schemas/README.md +507 -0
- rem/schemas/__init__.py +6 -0
- rem/schemas/agents/README.md +92 -0
- rem/schemas/agents/core/moment-builder.yaml +178 -0
- rem/schemas/agents/core/rem-query-agent.yaml +226 -0
- rem/schemas/agents/core/resource-affinity-assessor.yaml +99 -0
- rem/schemas/agents/core/simple-assistant.yaml +19 -0
- rem/schemas/agents/core/user-profile-builder.yaml +163 -0
- rem/schemas/agents/examples/contract-analyzer.yaml +317 -0
- rem/schemas/agents/examples/contract-extractor.yaml +134 -0
- rem/schemas/agents/examples/cv-parser.yaml +263 -0
- rem/schemas/agents/examples/hello-world.yaml +37 -0
- rem/schemas/agents/examples/query.yaml +54 -0
- rem/schemas/agents/examples/simple.yaml +21 -0
- rem/schemas/agents/examples/test.yaml +29 -0
- rem/schemas/agents/rem.yaml +128 -0
- rem/schemas/evaluators/hello-world/default.yaml +77 -0
- rem/schemas/evaluators/rem/faithfulness.yaml +219 -0
- rem/schemas/evaluators/rem/lookup-correctness.yaml +182 -0
- rem/schemas/evaluators/rem/retrieval-precision.yaml +199 -0
- rem/schemas/evaluators/rem/retrieval-recall.yaml +211 -0
- rem/schemas/evaluators/rem/search-correctness.yaml +192 -0
- rem/services/__init__.py +16 -0
- rem/services/audio/INTEGRATION.md +308 -0
- rem/services/audio/README.md +376 -0
- rem/services/audio/__init__.py +15 -0
- rem/services/audio/chunker.py +354 -0
- rem/services/audio/transcriber.py +259 -0
- rem/services/content/README.md +1269 -0
- rem/services/content/__init__.py +5 -0
- rem/services/content/providers.py +806 -0
- rem/services/content/service.py +676 -0
- rem/services/dreaming/README.md +230 -0
- rem/services/dreaming/__init__.py +53 -0
- rem/services/dreaming/affinity_service.py +336 -0
- rem/services/dreaming/moment_service.py +264 -0
- rem/services/dreaming/ontology_service.py +54 -0
- rem/services/dreaming/user_model_service.py +297 -0
- rem/services/dreaming/utils.py +39 -0
- rem/services/embeddings/__init__.py +11 -0
- rem/services/embeddings/api.py +120 -0
- rem/services/embeddings/worker.py +421 -0
- rem/services/fs/README.md +662 -0
- rem/services/fs/__init__.py +62 -0
- rem/services/fs/examples.py +206 -0
- rem/services/fs/examples_paths.py +204 -0
- rem/services/fs/git_provider.py +935 -0
- rem/services/fs/local_provider.py +760 -0
- rem/services/fs/parsing-hooks-examples.md +172 -0
- rem/services/fs/paths.py +276 -0
- rem/services/fs/provider.py +460 -0
- rem/services/fs/s3_provider.py +1042 -0
- rem/services/fs/service.py +186 -0
- rem/services/git/README.md +1075 -0
- rem/services/git/__init__.py +17 -0
- rem/services/git/service.py +469 -0
- rem/services/phoenix/EXPERIMENT_DESIGN.md +1146 -0
- rem/services/phoenix/README.md +453 -0
- rem/services/phoenix/__init__.py +46 -0
- rem/services/phoenix/client.py +686 -0
- rem/services/phoenix/config.py +88 -0
- rem/services/phoenix/prompt_labels.py +477 -0
- rem/services/postgres/README.md +575 -0
- rem/services/postgres/__init__.py +23 -0
- rem/services/postgres/migration_service.py +427 -0
- rem/services/postgres/pydantic_to_sqlalchemy.py +232 -0
- rem/services/postgres/register_type.py +352 -0
- rem/services/postgres/repository.py +337 -0
- rem/services/postgres/schema_generator.py +379 -0
- rem/services/postgres/service.py +802 -0
- rem/services/postgres/sql_builder.py +354 -0
- rem/services/rem/README.md +304 -0
- rem/services/rem/__init__.py +23 -0
- rem/services/rem/exceptions.py +71 -0
- rem/services/rem/executor.py +293 -0
- rem/services/rem/parser.py +145 -0
- rem/services/rem/queries.py +196 -0
- rem/services/rem/query.py +371 -0
- rem/services/rem/service.py +527 -0
- rem/services/session/README.md +374 -0
- rem/services/session/__init__.py +6 -0
- rem/services/session/compression.py +360 -0
- rem/services/session/reload.py +77 -0
- rem/settings.py +1235 -0
- rem/sql/002_install_models.sql +1068 -0
- rem/sql/background_indexes.sql +42 -0
- rem/sql/install_models.sql +1038 -0
- rem/sql/migrations/001_install.sql +503 -0
- rem/sql/migrations/002_install_models.sql +1202 -0
- rem/utils/AGENTIC_CHUNKING.md +597 -0
- rem/utils/README.md +583 -0
- rem/utils/__init__.py +43 -0
- rem/utils/agentic_chunking.py +622 -0
- rem/utils/batch_ops.py +343 -0
- rem/utils/chunking.py +108 -0
- rem/utils/clip_embeddings.py +276 -0
- rem/utils/dict_utils.py +98 -0
- rem/utils/embeddings.py +423 -0
- rem/utils/examples/embeddings_example.py +305 -0
- rem/utils/examples/sql_types_example.py +202 -0
- rem/utils/markdown.py +16 -0
- rem/utils/model_helpers.py +236 -0
- rem/utils/schema_loader.py +336 -0
- rem/utils/sql_types.py +348 -0
- rem/utils/user_id.py +81 -0
- rem/utils/vision.py +330 -0
- rem/workers/README.md +506 -0
- rem/workers/__init__.py +5 -0
- rem/workers/dreaming.py +502 -0
- rem/workers/engram_processor.py +312 -0
- rem/workers/sqs_file_processor.py +193 -0
- remdb-0.3.0.dist-info/METADATA +1455 -0
- remdb-0.3.0.dist-info/RECORD +187 -0
- remdb-0.3.0.dist-info/WHEEL +4 -0
- remdb-0.3.0.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,536 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP Resources for REM system information.
|
|
3
|
+
|
|
4
|
+
Resources are read-only data sources that LLMs can access for context.
|
|
5
|
+
They provide schema information, documentation, and system status.
|
|
6
|
+
|
|
7
|
+
Design Pattern:
|
|
8
|
+
- Resources are registered with the FastMCP server
|
|
9
|
+
- Resources return structured data (typically as strings or JSON)
|
|
10
|
+
- Resources don't modify system state (read-only)
|
|
11
|
+
- Resources help LLMs understand available operations
|
|
12
|
+
|
|
13
|
+
Available Resources:
|
|
14
|
+
- rem://schema/entities - Entity schemas documentation
|
|
15
|
+
- rem://schema/query-types - REM query types documentation
|
|
16
|
+
- rem://status - System health and statistics
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from fastmcp import FastMCP
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def register_schema_resources(mcp: FastMCP):
|
|
23
|
+
"""
|
|
24
|
+
Register schema documentation resources.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
mcp: FastMCP server instance
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
@mcp.resource("rem://schema/entities")
|
|
31
|
+
def get_entity_schemas() -> str:
|
|
32
|
+
"""
|
|
33
|
+
Get REM entity schemas documentation.
|
|
34
|
+
|
|
35
|
+
Returns complete schema information for all entity types:
|
|
36
|
+
- Resource: Chunked, embedded content
|
|
37
|
+
- Entity: Domain knowledge nodes
|
|
38
|
+
- Moment: Temporal narratives
|
|
39
|
+
- Message: Conversation messages
|
|
40
|
+
- User: System users
|
|
41
|
+
- File: File uploads
|
|
42
|
+
"""
|
|
43
|
+
return """
|
|
44
|
+
# REM Entity Schemas
|
|
45
|
+
|
|
46
|
+
## Resource
|
|
47
|
+
Chunked, embedded content from documents, files, conversations.
|
|
48
|
+
|
|
49
|
+
Fields:
|
|
50
|
+
- id: UUID (auto-generated)
|
|
51
|
+
- user_id: User identifier (primary data scoping field)
|
|
52
|
+
- tenant_id: Tenant identifier (legacy, set to user_id)
|
|
53
|
+
- name: Resource name/label (used for LOOKUP)
|
|
54
|
+
- content: Main text content
|
|
55
|
+
- category: Optional category (document, conversation, etc.)
|
|
56
|
+
- related_entities: JSONB array of extracted entity references
|
|
57
|
+
- graph_paths: JSONB array of InlineEdge objects
|
|
58
|
+
- resource_timestamp: Timestamp of resource creation
|
|
59
|
+
- metadata: JSONB flexible metadata dict
|
|
60
|
+
- created_at, updated_at, deleted_at: Temporal tracking
|
|
61
|
+
|
|
62
|
+
## Entity
|
|
63
|
+
Domain knowledge nodes with properties and relationships.
|
|
64
|
+
|
|
65
|
+
NOTE: Entities are stored within resources/moments, not in a separate table.
|
|
66
|
+
Entity IDs are human-readable labels (e.g., "sarah-chen", "api-design-v2").
|
|
67
|
+
|
|
68
|
+
## Moment
|
|
69
|
+
Temporal narratives and time-bound events.
|
|
70
|
+
|
|
71
|
+
Fields:
|
|
72
|
+
- id: UUID (auto-generated)
|
|
73
|
+
- user_id: User identifier (primary data scoping field)
|
|
74
|
+
- tenant_id: Tenant identifier (legacy, set to user_id)
|
|
75
|
+
- name: Moment name/label (used for LOOKUP)
|
|
76
|
+
- moment_type: Type (meeting, coding_session, conversation, etc.)
|
|
77
|
+
- resource_timestamp: Start time
|
|
78
|
+
- resource_ends_timestamp: End time
|
|
79
|
+
- present_persons: JSONB array of Person objects
|
|
80
|
+
- speakers: JSONB array of Speaker objects
|
|
81
|
+
- emotion_tags: Array of emotion tags
|
|
82
|
+
- topic_tags: Array of topic tags
|
|
83
|
+
- summary: Natural language summary
|
|
84
|
+
- source_resource_ids: Array of referenced resource UUIDs
|
|
85
|
+
- created_at, updated_at, deleted_at: Temporal tracking
|
|
86
|
+
|
|
87
|
+
## Message
|
|
88
|
+
Conversation messages with agents.
|
|
89
|
+
|
|
90
|
+
Fields:
|
|
91
|
+
- id: UUID (auto-generated)
|
|
92
|
+
- user_id: User identifier (primary data scoping field)
|
|
93
|
+
- tenant_id: Tenant identifier (legacy, set to user_id)
|
|
94
|
+
- role: Message role (user, assistant, system)
|
|
95
|
+
- content: Message text
|
|
96
|
+
- session_id: Conversation session identifier
|
|
97
|
+
- metadata: JSONB flexible metadata dict
|
|
98
|
+
- created_at, updated_at, deleted_at: Temporal tracking
|
|
99
|
+
|
|
100
|
+
## User
|
|
101
|
+
System users with authentication.
|
|
102
|
+
|
|
103
|
+
Fields:
|
|
104
|
+
- id: UUID (auto-generated)
|
|
105
|
+
- user_id: User identifier (primary data scoping field)
|
|
106
|
+
- tenant_id: Tenant identifier (legacy, set to user_id)
|
|
107
|
+
- name: User name
|
|
108
|
+
- email: User email
|
|
109
|
+
- metadata: JSONB flexible metadata dict
|
|
110
|
+
- created_at, updated_at, deleted_at: Temporal tracking
|
|
111
|
+
|
|
112
|
+
## File
|
|
113
|
+
File uploads with S3 storage.
|
|
114
|
+
|
|
115
|
+
Fields:
|
|
116
|
+
- id: UUID (auto-generated)
|
|
117
|
+
- user_id: User identifier (primary data scoping field)
|
|
118
|
+
- tenant_id: Tenant identifier (legacy, set to user_id)
|
|
119
|
+
- name: File name
|
|
120
|
+
- s3_key: S3 object key
|
|
121
|
+
- s3_bucket: S3 bucket name
|
|
122
|
+
- content_type: MIME type
|
|
123
|
+
- size_bytes: File size
|
|
124
|
+
- metadata: JSONB flexible metadata dict
|
|
125
|
+
- created_at, updated_at, deleted_at: Temporal tracking
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
@mcp.resource("rem://schema/query-types")
|
|
129
|
+
def get_query_types() -> str:
|
|
130
|
+
"""
|
|
131
|
+
Get REM query types documentation.
|
|
132
|
+
|
|
133
|
+
Returns comprehensive documentation for all REM query types
|
|
134
|
+
with examples and parameter specifications.
|
|
135
|
+
"""
|
|
136
|
+
return """
|
|
137
|
+
# REM Query Types
|
|
138
|
+
|
|
139
|
+
## LOOKUP
|
|
140
|
+
O(1) entity resolution across ALL tables using KV_STORE.
|
|
141
|
+
|
|
142
|
+
Parameters:
|
|
143
|
+
- entity_key (required): Entity label/name (e.g., "sarah-chen", "api-design-v2")
|
|
144
|
+
- user_id (optional): User scoping for private entities
|
|
145
|
+
|
|
146
|
+
Example:
|
|
147
|
+
```
|
|
148
|
+
rem_query(query_type="lookup", entity_key="Sarah Chen", user_id="user-123")
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
- entity_key: The looked-up key
|
|
153
|
+
- entity_type: Entity type (person, document, etc.)
|
|
154
|
+
- entity_id: UUID of the entity
|
|
155
|
+
- content_summary: Summary of entity content
|
|
156
|
+
- metadata: Additional metadata
|
|
157
|
+
|
|
158
|
+
## FUZZY
|
|
159
|
+
Fuzzy text matching using pg_trgm similarity.
|
|
160
|
+
|
|
161
|
+
Parameters:
|
|
162
|
+
- query_text (required): Query string
|
|
163
|
+
- threshold (optional): Similarity threshold 0.0-1.0 (default: 0.7)
|
|
164
|
+
- limit (optional): Max results (default: 10)
|
|
165
|
+
- user_id (optional): User scoping
|
|
166
|
+
|
|
167
|
+
Example:
|
|
168
|
+
```
|
|
169
|
+
rem_query(query_type="fuzzy", query_text="sara", threshold=0.7, user_id="user-123")
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
- Entities matching query with similarity scores
|
|
174
|
+
- Ordered by similarity (highest first)
|
|
175
|
+
|
|
176
|
+
## SEARCH
|
|
177
|
+
Semantic vector search using embeddings (table-specific).
|
|
178
|
+
|
|
179
|
+
Parameters:
|
|
180
|
+
- query_text (required): Natural language query
|
|
181
|
+
- table_name (required): Table to search (resources, moments, etc.)
|
|
182
|
+
- field_name (optional): Field to search (defaults to "content")
|
|
183
|
+
- provider (optional): Embedding provider (default: from LLM__EMBEDDING_PROVIDER setting)
|
|
184
|
+
- min_similarity (optional): Minimum similarity 0.0-1.0 (default: 0.7)
|
|
185
|
+
- limit (optional): Max results (default: 10)
|
|
186
|
+
- user_id (optional): User scoping
|
|
187
|
+
|
|
188
|
+
Example:
|
|
189
|
+
```
|
|
190
|
+
rem_query(
|
|
191
|
+
query_type="search",
|
|
192
|
+
query_text="database migration",
|
|
193
|
+
table_name="resources",
|
|
194
|
+
user_id="user-123"
|
|
195
|
+
)
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
- Semantically similar entities
|
|
200
|
+
- Ordered by similarity score
|
|
201
|
+
|
|
202
|
+
## SQL
|
|
203
|
+
Direct SQL queries with WHERE clauses (tenant-scoped).
|
|
204
|
+
|
|
205
|
+
Parameters:
|
|
206
|
+
- table_name (required): Table to query
|
|
207
|
+
- where_clause (optional): SQL WHERE condition
|
|
208
|
+
- limit (optional): Max results
|
|
209
|
+
|
|
210
|
+
Example:
|
|
211
|
+
```
|
|
212
|
+
rem_query(
|
|
213
|
+
query_type="sql",
|
|
214
|
+
table_name="moments",
|
|
215
|
+
where_clause="moment_type='meeting' AND resource_timestamp > '2025-01-01'",
|
|
216
|
+
user_id="user-123"
|
|
217
|
+
)
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
- Matching rows from table
|
|
222
|
+
- Automatically scoped to user_id
|
|
223
|
+
|
|
224
|
+
## TRAVERSE
|
|
225
|
+
Multi-hop graph traversal with depth control.
|
|
226
|
+
|
|
227
|
+
Parameters:
|
|
228
|
+
- start_key (required): Starting entity key
|
|
229
|
+
- max_depth (optional): Maximum traversal depth (default: 1)
|
|
230
|
+
- depth=0: PLAN mode (analyze edges without traversal)
|
|
231
|
+
- depth=1+: Full traversal with cycle detection
|
|
232
|
+
- rel_type (optional): Filter by relationship type (e.g., "manages", "authored_by")
|
|
233
|
+
- user_id (optional): User scoping
|
|
234
|
+
|
|
235
|
+
Example:
|
|
236
|
+
```
|
|
237
|
+
rem_query(
|
|
238
|
+
query_type="traverse",
|
|
239
|
+
start_key="Sarah Chen",
|
|
240
|
+
max_depth=2,
|
|
241
|
+
rel_type="manages",
|
|
242
|
+
user_id="user-123"
|
|
243
|
+
)
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Returns:
|
|
247
|
+
- Traversed entities with depth info
|
|
248
|
+
- Relationship types and weights
|
|
249
|
+
- Path information for each node
|
|
250
|
+
|
|
251
|
+
## Multi-Turn Exploration
|
|
252
|
+
|
|
253
|
+
REM supports iterated retrieval where LLMs conduct multi-turn conversations
|
|
254
|
+
with the database:
|
|
255
|
+
|
|
256
|
+
Turn 1: Find entry point
|
|
257
|
+
```
|
|
258
|
+
LOOKUP "Sarah Chen"
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Turn 2: Analyze neighborhood (PLAN mode)
|
|
262
|
+
```
|
|
263
|
+
TRAVERSE start_key="Sarah Chen" max_depth=0
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Turn 3: Selective traversal
|
|
267
|
+
```
|
|
268
|
+
TRAVERSE start_key="Sarah Chen" rel_type="manages" max_depth=2
|
|
269
|
+
```
|
|
270
|
+
"""
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def register_agent_resources(mcp: FastMCP):
|
|
274
|
+
"""
|
|
275
|
+
Register agent schema resources.
|
|
276
|
+
|
|
277
|
+
Args:
|
|
278
|
+
mcp: FastMCP server instance
|
|
279
|
+
"""
|
|
280
|
+
|
|
281
|
+
@mcp.resource("rem://agents")
|
|
282
|
+
def list_available_agents() -> str:
|
|
283
|
+
"""
|
|
284
|
+
List all available agent schemas.
|
|
285
|
+
|
|
286
|
+
Returns a list of agent schemas packaged with REM, including:
|
|
287
|
+
- Agent name
|
|
288
|
+
- Description
|
|
289
|
+
- Available tools
|
|
290
|
+
- Version information
|
|
291
|
+
|
|
292
|
+
TODO: Add pagination support if agent count grows large (not needed for now)
|
|
293
|
+
"""
|
|
294
|
+
import importlib.resources
|
|
295
|
+
import yaml
|
|
296
|
+
from pathlib import Path
|
|
297
|
+
|
|
298
|
+
try:
|
|
299
|
+
# Find packaged agent schemas
|
|
300
|
+
agents_ref = importlib.resources.files("rem") / "schemas" / "agents"
|
|
301
|
+
agents_dir = Path(str(agents_ref))
|
|
302
|
+
|
|
303
|
+
if not agents_dir.exists():
|
|
304
|
+
return "# Available Agents\n\nNo agent schemas found in package."
|
|
305
|
+
|
|
306
|
+
# Discover all agent schemas recursively
|
|
307
|
+
agent_files = sorted(agents_dir.rglob("*.yaml")) + sorted(agents_dir.rglob("*.yml")) + sorted(agents_dir.rglob("*.json"))
|
|
308
|
+
|
|
309
|
+
if not agent_files:
|
|
310
|
+
return "# Available Agents\n\nNo agent schemas found."
|
|
311
|
+
|
|
312
|
+
output = ["# Available Agent Schemas\n"]
|
|
313
|
+
output.append("Packaged agent schemas available for use:\n")
|
|
314
|
+
|
|
315
|
+
for agent_file in agent_files:
|
|
316
|
+
try:
|
|
317
|
+
with open(agent_file, "r") as f:
|
|
318
|
+
schema = yaml.safe_load(f)
|
|
319
|
+
|
|
320
|
+
agent_name = agent_file.stem
|
|
321
|
+
description = schema.get("description", "No description")
|
|
322
|
+
# Get first 200 characters of description
|
|
323
|
+
desc_snippet = description[:200] + "..." if len(description) > 200 else description
|
|
324
|
+
|
|
325
|
+
# Get additional metadata
|
|
326
|
+
extra = schema.get("json_schema_extra", {})
|
|
327
|
+
version = extra.get("version", "unknown")
|
|
328
|
+
tools = extra.get("tools", [])
|
|
329
|
+
|
|
330
|
+
output.append(f"\n## {agent_name}")
|
|
331
|
+
output.append(f"**Path:** `agents/{agent_file.name}`")
|
|
332
|
+
output.append(f"**Version:** {version}")
|
|
333
|
+
output.append(f"**Description:** {desc_snippet}")
|
|
334
|
+
if tools:
|
|
335
|
+
output.append(f"**Tools:** {', '.join(tools[:5])}" + (" ..." if len(tools) > 5 else ""))
|
|
336
|
+
|
|
337
|
+
# Usage example
|
|
338
|
+
output.append(f"\n**Usage:**")
|
|
339
|
+
output.append(f"```python")
|
|
340
|
+
output.append(f'rem ask agents/{agent_file.name} "Your query here"')
|
|
341
|
+
output.append(f"```")
|
|
342
|
+
|
|
343
|
+
except Exception as e:
|
|
344
|
+
output.append(f"\n## {agent_file.stem}")
|
|
345
|
+
output.append(f"⚠️ Error loading schema: {e}")
|
|
346
|
+
|
|
347
|
+
return "\n".join(output)
|
|
348
|
+
|
|
349
|
+
except Exception as e:
|
|
350
|
+
return f"# Available Agents\n\nError listing agents: {e}"
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
def register_file_resources(mcp: FastMCP):
|
|
354
|
+
"""
|
|
355
|
+
Register file operation resources.
|
|
356
|
+
|
|
357
|
+
Args:
|
|
358
|
+
mcp: FastMCP server instance
|
|
359
|
+
"""
|
|
360
|
+
|
|
361
|
+
@mcp.resource("rem://files/presigned-url/{s3_key}")
|
|
362
|
+
def get_presigned_url(s3_key: str, expiration: int = 3600) -> str:
|
|
363
|
+
"""
|
|
364
|
+
Generate presigned URL for S3 object download.
|
|
365
|
+
|
|
366
|
+
Args:
|
|
367
|
+
s3_key: S3 object key (e.g., "tenant/files/uuid/file.pdf")
|
|
368
|
+
expiration: URL expiration time in seconds (default: 3600 = 1 hour)
|
|
369
|
+
|
|
370
|
+
Returns:
|
|
371
|
+
Presigned URL for downloading the file
|
|
372
|
+
|
|
373
|
+
Raises:
|
|
374
|
+
RuntimeError: If S3 is not configured
|
|
375
|
+
|
|
376
|
+
Example:
|
|
377
|
+
>>> url = get_presigned_url("acme/files/123/document.pdf")
|
|
378
|
+
>>> # Returns: https://s3.amazonaws.com/bucket/acme/files/123/document.pdf?signature=...
|
|
379
|
+
"""
|
|
380
|
+
from ...settings import settings
|
|
381
|
+
|
|
382
|
+
# Check if S3 is configured
|
|
383
|
+
if not settings.s3.bucket_name:
|
|
384
|
+
raise RuntimeError(
|
|
385
|
+
"S3 is not configured. Cannot generate presigned URLs.\n"
|
|
386
|
+
"Configure S3 settings in ~/.rem/config.yaml or environment variables."
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
import aioboto3
|
|
390
|
+
import asyncio
|
|
391
|
+
from botocore.exceptions import ClientError
|
|
392
|
+
|
|
393
|
+
async def _generate_url():
|
|
394
|
+
session = aioboto3.Session()
|
|
395
|
+
async with session.client(
|
|
396
|
+
"s3",
|
|
397
|
+
endpoint_url=settings.s3.endpoint_url,
|
|
398
|
+
aws_access_key_id=settings.s3.access_key_id,
|
|
399
|
+
aws_secret_access_key=settings.s3.secret_access_key,
|
|
400
|
+
region_name=settings.s3.region,
|
|
401
|
+
) as s3_client:
|
|
402
|
+
try:
|
|
403
|
+
url = await s3_client.generate_presigned_url(
|
|
404
|
+
"get_object",
|
|
405
|
+
Params={
|
|
406
|
+
"Bucket": settings.s3.bucket_name,
|
|
407
|
+
"Key": s3_key,
|
|
408
|
+
},
|
|
409
|
+
ExpiresIn=expiration,
|
|
410
|
+
)
|
|
411
|
+
return url
|
|
412
|
+
except ClientError as e:
|
|
413
|
+
raise RuntimeError(f"Failed to generate presigned URL: {e}")
|
|
414
|
+
|
|
415
|
+
# Run async function
|
|
416
|
+
loop = asyncio.get_event_loop()
|
|
417
|
+
if loop.is_running():
|
|
418
|
+
# If already in async context, create new loop
|
|
419
|
+
import nest_asyncio
|
|
420
|
+
nest_asyncio.apply()
|
|
421
|
+
url = loop.run_until_complete(_generate_url())
|
|
422
|
+
else:
|
|
423
|
+
url = asyncio.run(_generate_url())
|
|
424
|
+
|
|
425
|
+
return url
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
def register_status_resources(mcp: FastMCP):
|
|
429
|
+
"""
|
|
430
|
+
Register system status resources.
|
|
431
|
+
|
|
432
|
+
Args:
|
|
433
|
+
mcp: FastMCP server instance
|
|
434
|
+
"""
|
|
435
|
+
|
|
436
|
+
@mcp.resource("rem://status")
|
|
437
|
+
def get_system_status() -> str:
|
|
438
|
+
"""
|
|
439
|
+
Get REM system health and statistics.
|
|
440
|
+
|
|
441
|
+
Returns system information including:
|
|
442
|
+
- Service health
|
|
443
|
+
- Database connection status
|
|
444
|
+
- Environment configuration
|
|
445
|
+
- Available query types
|
|
446
|
+
"""
|
|
447
|
+
from ...settings import settings
|
|
448
|
+
|
|
449
|
+
return f"""
|
|
450
|
+
# REM System Status
|
|
451
|
+
|
|
452
|
+
## Environment
|
|
453
|
+
- Environment: {settings.environment}
|
|
454
|
+
- Team: {settings.team}
|
|
455
|
+
- Root Path: {settings.root_path or '/'}
|
|
456
|
+
|
|
457
|
+
## LLM Configuration
|
|
458
|
+
- Default Model: {settings.llm.default_model}
|
|
459
|
+
- Default Temperature: {settings.llm.default_temperature}
|
|
460
|
+
- Embedding Provider: {settings.llm.embedding_provider}
|
|
461
|
+
- Embedding Model: {settings.llm.embedding_model}
|
|
462
|
+
- OpenAI API Key: {"✓ Configured" if settings.llm.openai_api_key else "✗ Not configured"}
|
|
463
|
+
- Anthropic API Key: {"✓ Configured" if settings.llm.anthropic_api_key else "✗ Not configured"}
|
|
464
|
+
|
|
465
|
+
## Database
|
|
466
|
+
- PostgreSQL: {settings.postgres.connection_string}
|
|
467
|
+
|
|
468
|
+
## S3 Storage
|
|
469
|
+
- Bucket: {settings.s3.bucket_name}
|
|
470
|
+
- Region: {settings.s3.region}
|
|
471
|
+
|
|
472
|
+
## Observability
|
|
473
|
+
- OTEL Enabled: {settings.otel.enabled}
|
|
474
|
+
- Phoenix Enabled: {settings.phoenix.enabled}
|
|
475
|
+
|
|
476
|
+
## Authentication
|
|
477
|
+
- Auth Enabled: {settings.auth.enabled}
|
|
478
|
+
|
|
479
|
+
## Available Query Types
|
|
480
|
+
- LOOKUP: O(1) entity resolution
|
|
481
|
+
- FUZZY: Fuzzy text matching
|
|
482
|
+
- SEARCH: Semantic vector search
|
|
483
|
+
- SQL: Direct SQL queries
|
|
484
|
+
- TRAVERSE: Multi-hop graph traversal
|
|
485
|
+
|
|
486
|
+
## MCP Tools
|
|
487
|
+
- search_rem: Execute REM queries (LOOKUP, FUZZY, SEARCH, SQL, TRAVERSE)
|
|
488
|
+
- ask_rem_agent: Natural language to REM query conversion
|
|
489
|
+
- ingest_into_rem: File ingestion pipeline
|
|
490
|
+
- read_resource: Access MCP resources
|
|
491
|
+
|
|
492
|
+
## Status
|
|
493
|
+
✓ System operational
|
|
494
|
+
✓ Ready to process queries
|
|
495
|
+
"""
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
# Resource dispatcher for read_resource tool
|
|
499
|
+
async def load_resource(uri: str) -> dict | str:
|
|
500
|
+
"""
|
|
501
|
+
Load an MCP resource by URI.
|
|
502
|
+
|
|
503
|
+
This function is called by the read_resource tool to dispatch to
|
|
504
|
+
registered resource handlers.
|
|
505
|
+
|
|
506
|
+
Args:
|
|
507
|
+
uri: Resource URI (e.g., "rem://schemas", "rem://status")
|
|
508
|
+
|
|
509
|
+
Returns:
|
|
510
|
+
Resource data (dict or string)
|
|
511
|
+
|
|
512
|
+
Raises:
|
|
513
|
+
ValueError: If URI is invalid or resource not found
|
|
514
|
+
"""
|
|
515
|
+
# Create temporary MCP instance with resources
|
|
516
|
+
from fastmcp import FastMCP
|
|
517
|
+
|
|
518
|
+
mcp = FastMCP(name="temp")
|
|
519
|
+
|
|
520
|
+
# Register all resources
|
|
521
|
+
register_schema_resources(mcp)
|
|
522
|
+
register_agent_resources(mcp)
|
|
523
|
+
register_file_resources(mcp)
|
|
524
|
+
register_status_resources(mcp)
|
|
525
|
+
|
|
526
|
+
# Get resource handlers from MCP internal registry
|
|
527
|
+
# FastMCP stores resources in a dict by URI
|
|
528
|
+
if hasattr(mcp, "_resources"):
|
|
529
|
+
if uri in mcp._resources:
|
|
530
|
+
handler = mcp._resources[uri]
|
|
531
|
+
if callable(handler):
|
|
532
|
+
result = handler()
|
|
533
|
+
return result if result else {"error": "Resource returned None"}
|
|
534
|
+
|
|
535
|
+
# If not found, raise error
|
|
536
|
+
raise ValueError(f"Resource not found: {uri}. Available resources: {list(mcp._resources.keys()) if hasattr(mcp, '_resources') else 'unknown'}")
|