remdb 0.2.6__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 +565 -0
- rem/cli/commands/configure.py +423 -0
- rem/cli/commands/db.py +493 -0
- rem/cli/commands/dreaming.py +324 -0
- rem/cli/commands/experiments.py +1124 -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 +88 -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 +657 -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 +229 -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.2.6.dist-info/METADATA +1191 -0
- remdb-0.2.6.dist-info/RECORD +187 -0
- remdb-0.2.6.dist-info/WHEEL +4 -0
- remdb-0.2.6.dist-info/entry_points.txt +2 -0
rem/api/main.py
ADDED
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
"""
|
|
2
|
+
REM API Server - FastAPI application with integrated MCP server.
|
|
3
|
+
|
|
4
|
+
Design Pattern:
|
|
5
|
+
1. Create FastMCP server with create_mcp_server()
|
|
6
|
+
2. Get HTTP app with mcp.http_app(path="/", transport="http", stateless_http=True)
|
|
7
|
+
3. Mount on FastAPI at /api/v1/mcp
|
|
8
|
+
4. Add middleware in specific order (sessions, logging, auth, CORS)
|
|
9
|
+
5. Register API routers for v1 endpoints
|
|
10
|
+
|
|
11
|
+
Key Architecture Decisions
|
|
12
|
+
- MCP mounted at /api/v1/mcp (not /mcp) for consistency
|
|
13
|
+
- Stateless HTTP prevents stale session errors across pod restarts
|
|
14
|
+
- Auth middleware excludes /api/auth and /api/v1/mcp/auth paths
|
|
15
|
+
- CORS added LAST so it runs FIRST (middleware runs in reverse)
|
|
16
|
+
- Combined lifespan for proper initialization order
|
|
17
|
+
|
|
18
|
+
Middleware Order (runs in reverse):
|
|
19
|
+
1. CORS (runs first - adds headers to all responses)
|
|
20
|
+
2. Auth (protects /api/v1/* paths)
|
|
21
|
+
3. Logging (logs all requests)
|
|
22
|
+
4. Sessions (OAuth state management)
|
|
23
|
+
|
|
24
|
+
Endpoints:
|
|
25
|
+
- / : API information
|
|
26
|
+
- /health : Health check
|
|
27
|
+
- /api/v1/mcp : MCP endpoint (HTTP transport)
|
|
28
|
+
- /api/v1/chat/completions : OpenAI-compatible chat completions (streaming & non-streaming)
|
|
29
|
+
- /api/v1/query : REM query execution (TODO)
|
|
30
|
+
- /api/v1/resources : Resource CRUD (TODO)
|
|
31
|
+
- /api/v1/moments : Moment CRUD (TODO)
|
|
32
|
+
- /api/auth/* : OAuth/OIDC authentication (TODO)
|
|
33
|
+
- /docs : OpenAPI documentation
|
|
34
|
+
|
|
35
|
+
Headers → AgentContext Mapping:
|
|
36
|
+
The following HTTP headers are automatically mapped to AgentContext fields:
|
|
37
|
+
- X-User-Id → context.user_id (user identifier)
|
|
38
|
+
- X-Tenant-Id → context.tenant_id (tenant identifier, required for REM)
|
|
39
|
+
- X-Session-Id → context.session_id (session/conversation identifier)
|
|
40
|
+
- X-Agent-Schema → context.agent_schema_uri (agent schema to use)
|
|
41
|
+
|
|
42
|
+
Example:
|
|
43
|
+
POST /api/v1/chat/completions
|
|
44
|
+
X-Tenant-Id: acme-corp
|
|
45
|
+
X-User-Id: user123
|
|
46
|
+
X-Agent-Schema: rem-agents-query-agent
|
|
47
|
+
|
|
48
|
+
{
|
|
49
|
+
"model": "anthropic:claude-sonnet-4-5-20250929",
|
|
50
|
+
"messages": [{"role": "user", "content": "Find Sarah's documents"}],
|
|
51
|
+
"stream": true
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
Running:
|
|
55
|
+
# Development (auto-reload)
|
|
56
|
+
uv run python -m rem.api.main
|
|
57
|
+
|
|
58
|
+
# Production (Docker with hypercorn)
|
|
59
|
+
hypercorn rem.api.main:app --bind 0.0.0.0:8000
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
import secrets
|
|
63
|
+
import time
|
|
64
|
+
from contextlib import asynccontextmanager
|
|
65
|
+
|
|
66
|
+
from fastapi import FastAPI, Request
|
|
67
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
68
|
+
from fastapi.responses import JSONResponse
|
|
69
|
+
from loguru import logger
|
|
70
|
+
from starlette.middleware.base import BaseHTTPMiddleware
|
|
71
|
+
from starlette.middleware.sessions import SessionMiddleware
|
|
72
|
+
|
|
73
|
+
from .mcp_router.server import create_mcp_server
|
|
74
|
+
from ..settings import settings
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class RequestLoggingMiddleware(BaseHTTPMiddleware):
|
|
78
|
+
"""
|
|
79
|
+
Log all incoming HTTP requests and responses.
|
|
80
|
+
|
|
81
|
+
Design Pattern:
|
|
82
|
+
- Logs request method, path, client, user-agent
|
|
83
|
+
- Logs response status, content-type, duration
|
|
84
|
+
- Essential for debugging OAuth flow and MCP sessions
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
async def dispatch(self, request: Request, call_next):
|
|
88
|
+
start_time = time.time()
|
|
89
|
+
|
|
90
|
+
# Log incoming request
|
|
91
|
+
client_host = request.client.host if request.client else "unknown"
|
|
92
|
+
logger.info(
|
|
93
|
+
f"→ REQUEST: {request.method} {request.url.path} | "
|
|
94
|
+
f"Client: {client_host} | "
|
|
95
|
+
f"User-Agent: {request.headers.get('user-agent', 'unknown')[:100]}"
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# Process request
|
|
99
|
+
response = await call_next(request)
|
|
100
|
+
|
|
101
|
+
# Log response
|
|
102
|
+
duration_ms = (time.time() - start_time) * 1000
|
|
103
|
+
logger.info(
|
|
104
|
+
f"← RESPONSE: {request.method} {request.url.path} | "
|
|
105
|
+
f"Status: {response.status_code} | "
|
|
106
|
+
f"Duration: {duration_ms:.2f}ms"
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
return response
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class SSEBufferingMiddleware(BaseHTTPMiddleware):
|
|
113
|
+
"""
|
|
114
|
+
Disable proxy buffering for SSE responses.
|
|
115
|
+
|
|
116
|
+
Adds X-Accel-Buffering: no header to prevent Nginx/Traefik
|
|
117
|
+
from buffering Server-Sent Events (critical for MCP SSE transport).
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
async def dispatch(self, request: Request, call_next):
|
|
121
|
+
response = await call_next(request)
|
|
122
|
+
|
|
123
|
+
# Disable buffering for SSE responses
|
|
124
|
+
content_type = response.headers.get("content-type", "")
|
|
125
|
+
if "text/event-stream" in content_type:
|
|
126
|
+
response.headers["X-Accel-Buffering"] = "no"
|
|
127
|
+
response.headers["Cache-Control"] = "no-cache"
|
|
128
|
+
|
|
129
|
+
return response
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@asynccontextmanager
|
|
133
|
+
async def lifespan(app: FastAPI):
|
|
134
|
+
"""
|
|
135
|
+
Application lifespan manager.
|
|
136
|
+
|
|
137
|
+
Handles startup and shutdown tasks.
|
|
138
|
+
OTEL instrumentation must be initialized at startup before any agents are created.
|
|
139
|
+
"""
|
|
140
|
+
logger.info(f"Starting REM API ({settings.environment})")
|
|
141
|
+
|
|
142
|
+
# Initialize OTEL instrumentation if enabled
|
|
143
|
+
# Must be done at startup to instrument Pydantic AI before any agents are created
|
|
144
|
+
if settings.otel.enabled:
|
|
145
|
+
from ..agentic.otel.setup import setup_instrumentation
|
|
146
|
+
|
|
147
|
+
setup_instrumentation()
|
|
148
|
+
|
|
149
|
+
# Check database configuration
|
|
150
|
+
if not settings.postgres.enabled:
|
|
151
|
+
logger.warning(
|
|
152
|
+
"Running in NO-DATABASE mode - database connection disabled. "
|
|
153
|
+
"Agent execution works with file-based schemas, but session storage "
|
|
154
|
+
"and history lookups are unavailable. Enable database with POSTGRES__ENABLED=true"
|
|
155
|
+
)
|
|
156
|
+
else:
|
|
157
|
+
logger.info(f"Database enabled: {settings.postgres.connection_string}")
|
|
158
|
+
|
|
159
|
+
yield
|
|
160
|
+
|
|
161
|
+
logger.info("Shutting down REM API")
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def create_app() -> FastAPI:
|
|
165
|
+
"""
|
|
166
|
+
Create and configure the FastAPI application.
|
|
167
|
+
|
|
168
|
+
Design Pattern:
|
|
169
|
+
1. Create MCP server
|
|
170
|
+
2. Get HTTP app with stateless_http=True
|
|
171
|
+
3. Combine lifespans (app + MCP)
|
|
172
|
+
4. Create FastAPI with combined lifespan
|
|
173
|
+
5. Add middleware (sessions, logging, auth, CORS) in specific order
|
|
174
|
+
6. Define health endpoints
|
|
175
|
+
7. Register API routers
|
|
176
|
+
8. Mount MCP app
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
Configured FastAPI application
|
|
180
|
+
"""
|
|
181
|
+
# Create MCP server and get HTTP app
|
|
182
|
+
# path="/" creates routes at root, then mount at /api/v1/mcp
|
|
183
|
+
# transport="http" for MCP HTTP protocol
|
|
184
|
+
# stateless_http=True prevents stale session errors (pods can restart)
|
|
185
|
+
mcp_server = create_mcp_server()
|
|
186
|
+
mcp_app = mcp_server.http_app(path="/", transport="http", stateless_http=True)
|
|
187
|
+
|
|
188
|
+
# Disable trailing slash redirects (prevents 307 redirects that strip auth headers)
|
|
189
|
+
if hasattr(mcp_app, "router"):
|
|
190
|
+
mcp_app.router.redirect_slashes = False
|
|
191
|
+
|
|
192
|
+
# Combine MCP and API lifespans
|
|
193
|
+
# Explicit nesting ensures proper initialization order
|
|
194
|
+
@asynccontextmanager
|
|
195
|
+
async def combined_lifespan(app: FastAPI):
|
|
196
|
+
async with lifespan(app):
|
|
197
|
+
async with mcp_app.lifespan(app):
|
|
198
|
+
yield
|
|
199
|
+
|
|
200
|
+
app = FastAPI(
|
|
201
|
+
title="REM API",
|
|
202
|
+
description="Resources Entities Moments system for agentic AI",
|
|
203
|
+
version="0.1.0",
|
|
204
|
+
lifespan=combined_lifespan,
|
|
205
|
+
root_path=settings.root_path if settings.root_path else "",
|
|
206
|
+
redirect_slashes=False, # Don't redirect /mcp/ -> /mcp
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
# Add session middleware for OAuth state management
|
|
210
|
+
session_secret = settings.auth.session_secret or secrets.token_hex(32)
|
|
211
|
+
if not settings.auth.session_secret:
|
|
212
|
+
logger.warning(
|
|
213
|
+
"AUTH__SESSION_SECRET not set - using generated key "
|
|
214
|
+
"(sessions won't persist across restarts)"
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
app.add_middleware(
|
|
218
|
+
SessionMiddleware,
|
|
219
|
+
secret_key=session_secret,
|
|
220
|
+
session_cookie="rem_session",
|
|
221
|
+
max_age=3600, # 1 hour
|
|
222
|
+
same_site="lax",
|
|
223
|
+
https_only=settings.environment == "production",
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
# Add request logging middleware
|
|
227
|
+
app.add_middleware(RequestLoggingMiddleware)
|
|
228
|
+
|
|
229
|
+
# Add SSE buffering middleware (for MCP SSE transport)
|
|
230
|
+
app.add_middleware(SSEBufferingMiddleware)
|
|
231
|
+
|
|
232
|
+
# Add authentication middleware (if enabled)
|
|
233
|
+
if settings.auth.enabled:
|
|
234
|
+
from ..auth.middleware import AuthMiddleware
|
|
235
|
+
|
|
236
|
+
app.add_middleware(
|
|
237
|
+
AuthMiddleware,
|
|
238
|
+
protected_paths=["/api/v1"],
|
|
239
|
+
excluded_paths=["/api/auth", "/api/v1/mcp/auth"],
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
# Add CORS middleware LAST (runs first in middleware chain)
|
|
243
|
+
# Must expose mcp-session-id header for MCP session management
|
|
244
|
+
CORS_ORIGIN_WHITELIST = [
|
|
245
|
+
"http://localhost:5173", # Local development (Vite)
|
|
246
|
+
"http://localhost:3000", # Local development (React)
|
|
247
|
+
]
|
|
248
|
+
|
|
249
|
+
app.add_middleware(
|
|
250
|
+
CORSMiddleware,
|
|
251
|
+
allow_origins=CORS_ORIGIN_WHITELIST,
|
|
252
|
+
allow_credentials=True,
|
|
253
|
+
allow_methods=["*"],
|
|
254
|
+
allow_headers=["*", "mcp-protocol-version", "mcp-session-id", "authorization"],
|
|
255
|
+
expose_headers=["mcp-session-id"],
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
# Root endpoint
|
|
259
|
+
@app.get("/")
|
|
260
|
+
async def root():
|
|
261
|
+
"""API information endpoint."""
|
|
262
|
+
# TODO: If auth enabled and no user, return 401 with WWW-Authenticate
|
|
263
|
+
return {
|
|
264
|
+
"name": "REM API",
|
|
265
|
+
"version": "0.1.0",
|
|
266
|
+
"mcp_endpoint": "/api/v1/mcp",
|
|
267
|
+
"docs": "/docs",
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
# Health check endpoint
|
|
271
|
+
@app.get("/health")
|
|
272
|
+
async def health():
|
|
273
|
+
"""Health check endpoint."""
|
|
274
|
+
return {"status": "healthy", "version": "0.1.0"}
|
|
275
|
+
|
|
276
|
+
# Register API routers
|
|
277
|
+
from .routers.chat import router as chat_router
|
|
278
|
+
|
|
279
|
+
app.include_router(chat_router)
|
|
280
|
+
|
|
281
|
+
# Register auth router (if enabled)
|
|
282
|
+
if settings.auth.enabled:
|
|
283
|
+
from .routers.auth import router as auth_router
|
|
284
|
+
|
|
285
|
+
app.include_router(auth_router)
|
|
286
|
+
|
|
287
|
+
# TODO: Register additional routers
|
|
288
|
+
# from .routers.query import router as query_router
|
|
289
|
+
# from .routers.resources import router as resources_router
|
|
290
|
+
# from .routers.moments import router as moments_router
|
|
291
|
+
#
|
|
292
|
+
# app.include_router(query_router)
|
|
293
|
+
# app.include_router(resources_router)
|
|
294
|
+
# app.include_router(moments_router)
|
|
295
|
+
|
|
296
|
+
# Add middleware to rewrite /api/v1/mcp to /api/v1/mcp/
|
|
297
|
+
@app.middleware("http")
|
|
298
|
+
async def mcp_path_rewrite_middleware(request: Request, call_next):
|
|
299
|
+
"""Rewrite /api/v1/mcp to /api/v1/mcp/ to handle Claude Desktop requests."""
|
|
300
|
+
if request.url.path == "/api/v1/mcp":
|
|
301
|
+
request.scope["path"] = "/api/v1/mcp/"
|
|
302
|
+
request.scope["raw_path"] = b"/api/v1/mcp/"
|
|
303
|
+
return await call_next(request)
|
|
304
|
+
|
|
305
|
+
# Mount MCP app at /api/v1/mcp
|
|
306
|
+
app.mount("/api/v1/mcp", mcp_app)
|
|
307
|
+
|
|
308
|
+
return app
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
# Create application instance
|
|
312
|
+
app = create_app()
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
# Main entry point for uvicorn
|
|
316
|
+
if __name__ == "__main__":
|
|
317
|
+
import uvicorn
|
|
318
|
+
|
|
319
|
+
uvicorn.run(
|
|
320
|
+
"rem.api.main:app",
|
|
321
|
+
host="0.0.0.0",
|
|
322
|
+
port=8000,
|
|
323
|
+
reload=True,
|
|
324
|
+
)
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP Prompts for REM operations.
|
|
3
|
+
|
|
4
|
+
Prompts are interactive templates that help users perform complex tasks.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from fastmcp import FastMCP
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
CREATEAGENT_PROMPT = """
|
|
11
|
+
Create a custom REM agent schema.
|
|
12
|
+
|
|
13
|
+
I'll help you create an agent schema that can be uploaded to REM and automatically processed.
|
|
14
|
+
|
|
15
|
+
## What I need from you:
|
|
16
|
+
|
|
17
|
+
1. **Agent purpose**: What should this agent do? What domain knowledge does it need?
|
|
18
|
+
2. **Short name**: Lowercase with hyphens (e.g., "cv-parser", "contract-analyzer")
|
|
19
|
+
3. **Version**: Semantic version (e.g., "1.0.0")
|
|
20
|
+
4. **Structured output fields**: What data should the agent extract?
|
|
21
|
+
|
|
22
|
+
## Agent Schema Format
|
|
23
|
+
|
|
24
|
+
REM agents use JSON Schema format with these sections:
|
|
25
|
+
|
|
26
|
+
```yaml
|
|
27
|
+
---
|
|
28
|
+
type: object
|
|
29
|
+
description: |
|
|
30
|
+
System prompt with LLM instructions.
|
|
31
|
+
|
|
32
|
+
Provide clear, detailed guidance on what the agent should do.
|
|
33
|
+
|
|
34
|
+
properties:
|
|
35
|
+
field_name:
|
|
36
|
+
type: string
|
|
37
|
+
description: Field description
|
|
38
|
+
|
|
39
|
+
required:
|
|
40
|
+
- required_field
|
|
41
|
+
|
|
42
|
+
json_schema_extra:
|
|
43
|
+
kind: agent
|
|
44
|
+
name: your-agent
|
|
45
|
+
version: "1.0.0"
|
|
46
|
+
tags: [domain, category]
|
|
47
|
+
|
|
48
|
+
# Optional: Fields to embed for semantic search
|
|
49
|
+
embedding_fields:
|
|
50
|
+
- field1
|
|
51
|
+
- field2
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Example: CV Parser
|
|
55
|
+
|
|
56
|
+
```yaml
|
|
57
|
+
---
|
|
58
|
+
type: object
|
|
59
|
+
description: |
|
|
60
|
+
Parse CV/resume documents to extract candidate information.
|
|
61
|
+
|
|
62
|
+
Extract:
|
|
63
|
+
- Candidate details (name, contact, summary)
|
|
64
|
+
- Work experience with dates
|
|
65
|
+
- Education history
|
|
66
|
+
- Skills and competencies
|
|
67
|
+
- Seniority level assessment
|
|
68
|
+
|
|
69
|
+
properties:
|
|
70
|
+
candidate_name:
|
|
71
|
+
type: string
|
|
72
|
+
description: Full name of the candidate
|
|
73
|
+
|
|
74
|
+
skills:
|
|
75
|
+
type: array
|
|
76
|
+
items:
|
|
77
|
+
type: string
|
|
78
|
+
description: Technical and professional skills
|
|
79
|
+
|
|
80
|
+
experience:
|
|
81
|
+
type: array
|
|
82
|
+
items:
|
|
83
|
+
type: object
|
|
84
|
+
properties:
|
|
85
|
+
company: {type: string}
|
|
86
|
+
title: {type: string}
|
|
87
|
+
start_date: {type: string}
|
|
88
|
+
end_date: {type: string}
|
|
89
|
+
description: Work experience history
|
|
90
|
+
|
|
91
|
+
seniority_level:
|
|
92
|
+
type: string
|
|
93
|
+
enum: ["junior", "mid-level", "senior", "lead", "executive"]
|
|
94
|
+
description: Assessed seniority level
|
|
95
|
+
|
|
96
|
+
required:
|
|
97
|
+
- candidate_name
|
|
98
|
+
- skills
|
|
99
|
+
|
|
100
|
+
json_schema_extra:
|
|
101
|
+
kind: agent
|
|
102
|
+
name: cv-parser
|
|
103
|
+
version: "1.0.0"
|
|
104
|
+
|
|
105
|
+
tags: [recruitment, ontology-extractor]
|
|
106
|
+
|
|
107
|
+
embedding_fields:
|
|
108
|
+
- candidate_name
|
|
109
|
+
- skills
|
|
110
|
+
|
|
111
|
+
category: ontology-extractor
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Upload Process
|
|
115
|
+
|
|
116
|
+
After creating your schema:
|
|
117
|
+
|
|
118
|
+
1. **Save to local file system**: `~/.rem/fs/my-agent.yaml` or request an upload path for remote servers.
|
|
119
|
+
|
|
120
|
+
2. **Upload via ingest_file**:
|
|
121
|
+
```python
|
|
122
|
+
ingest_file(
|
|
123
|
+
file_uri="LOCAL PATH for local servers or remote S3 path for remote servers",
|
|
124
|
+
category="agent"
|
|
125
|
+
)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
3. **Automatic processing**:
|
|
129
|
+
- File detected by worker
|
|
130
|
+
- Schema validated and stored in schemas table
|
|
131
|
+
- Available for immediate use
|
|
132
|
+
|
|
133
|
+
## Ready?
|
|
134
|
+
|
|
135
|
+
Tell me:
|
|
136
|
+
1. What should your agent do?
|
|
137
|
+
2. What data should it extract?
|
|
138
|
+
3. What should we name it?
|
|
139
|
+
|
|
140
|
+
I'll generate the complete schema for you!
|
|
141
|
+
"""
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def register_prompts(mcp: FastMCP):
|
|
145
|
+
"""
|
|
146
|
+
Register MCP prompts.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
mcp: FastMCP server instance
|
|
150
|
+
"""
|
|
151
|
+
|
|
152
|
+
@mcp.prompt()
|
|
153
|
+
def create_agent(
|
|
154
|
+
purpose: str = "",
|
|
155
|
+
short_name: str = "",
|
|
156
|
+
version: str = "1.0.0",
|
|
157
|
+
) -> str:
|
|
158
|
+
"""
|
|
159
|
+
Interactive prompt for creating custom REM agent schemas.
|
|
160
|
+
|
|
161
|
+
Guides users through creating agent schemas with domain knowledge,
|
|
162
|
+
structured output definitions, and upload instructions.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
purpose: Agent purpose and domain (optional, will prompt if empty)
|
|
166
|
+
short_name: Agent short name in kebab-case (optional, will suggest)
|
|
167
|
+
version: Semantic version (default: "1.0.0")
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
Interactive prompt with examples and upload instructions
|
|
171
|
+
"""
|
|
172
|
+
prompt = CREATEAGENT_PROMPT
|
|
173
|
+
|
|
174
|
+
# Add context if parameters provided
|
|
175
|
+
if purpose:
|
|
176
|
+
prompt += f"\n\nYou mentioned: \"{purpose}\"\n"
|
|
177
|
+
if short_name:
|
|
178
|
+
prompt += f"Short name: {short_name}\n"
|
|
179
|
+
if version != "1.0.0":
|
|
180
|
+
prompt += f"Version: {version}\n"
|
|
181
|
+
|
|
182
|
+
return prompt
|