remdb 0.3.14__py3-none-any.whl → 0.3.157__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/README.md +76 -0
- rem/agentic/__init__.py +15 -0
- rem/agentic/agents/__init__.py +32 -2
- rem/agentic/agents/agent_manager.py +310 -0
- rem/agentic/agents/sse_simulator.py +502 -0
- rem/agentic/context.py +51 -27
- rem/agentic/context_builder.py +5 -3
- rem/agentic/llm_provider_models.py +301 -0
- rem/agentic/mcp/tool_wrapper.py +155 -18
- rem/agentic/otel/setup.py +93 -4
- rem/agentic/providers/phoenix.py +371 -108
- rem/agentic/providers/pydantic_ai.py +280 -57
- rem/agentic/schema.py +361 -21
- rem/agentic/tools/rem_tools.py +3 -3
- rem/api/README.md +215 -1
- rem/api/deps.py +255 -0
- rem/api/main.py +132 -40
- rem/api/mcp_router/resources.py +1 -1
- rem/api/mcp_router/server.py +28 -5
- rem/api/mcp_router/tools.py +555 -7
- rem/api/routers/admin.py +494 -0
- rem/api/routers/auth.py +278 -4
- rem/api/routers/chat/completions.py +402 -20
- rem/api/routers/chat/models.py +88 -10
- rem/api/routers/chat/otel_utils.py +33 -0
- rem/api/routers/chat/sse_events.py +542 -0
- rem/api/routers/chat/streaming.py +697 -45
- rem/api/routers/dev.py +81 -0
- rem/api/routers/feedback.py +268 -0
- rem/api/routers/messages.py +473 -0
- rem/api/routers/models.py +78 -0
- rem/api/routers/query.py +360 -0
- rem/api/routers/shared_sessions.py +406 -0
- rem/auth/__init__.py +13 -3
- rem/auth/middleware.py +186 -22
- rem/auth/providers/__init__.py +4 -1
- rem/auth/providers/email.py +215 -0
- rem/cli/commands/README.md +237 -64
- rem/cli/commands/cluster.py +1808 -0
- rem/cli/commands/configure.py +4 -7
- rem/cli/commands/db.py +386 -143
- rem/cli/commands/experiments.py +468 -76
- rem/cli/commands/process.py +14 -8
- rem/cli/commands/schema.py +97 -50
- rem/cli/commands/session.py +336 -0
- rem/cli/dreaming.py +2 -2
- rem/cli/main.py +29 -6
- rem/config.py +10 -3
- rem/models/core/core_model.py +7 -1
- rem/models/core/experiment.py +58 -14
- rem/models/core/rem_query.py +5 -2
- rem/models/entities/__init__.py +25 -0
- rem/models/entities/domain_resource.py +38 -0
- rem/models/entities/feedback.py +123 -0
- rem/models/entities/message.py +30 -1
- rem/models/entities/ontology.py +1 -1
- rem/models/entities/ontology_config.py +1 -1
- rem/models/entities/session.py +83 -0
- rem/models/entities/shared_session.py +180 -0
- rem/models/entities/subscriber.py +175 -0
- rem/models/entities/user.py +1 -0
- rem/registry.py +10 -4
- rem/schemas/agents/core/agent-builder.yaml +134 -0
- rem/schemas/agents/examples/contract-analyzer.yaml +1 -1
- rem/schemas/agents/examples/contract-extractor.yaml +1 -1
- rem/schemas/agents/examples/cv-parser.yaml +1 -1
- rem/schemas/agents/rem.yaml +7 -3
- rem/services/__init__.py +3 -1
- rem/services/content/service.py +92 -19
- rem/services/email/__init__.py +10 -0
- rem/services/email/service.py +459 -0
- rem/services/email/templates.py +360 -0
- rem/services/embeddings/api.py +4 -4
- rem/services/embeddings/worker.py +16 -16
- rem/services/phoenix/client.py +154 -14
- rem/services/postgres/README.md +197 -15
- rem/services/postgres/__init__.py +2 -1
- rem/services/postgres/diff_service.py +547 -0
- rem/services/postgres/pydantic_to_sqlalchemy.py +470 -140
- rem/services/postgres/repository.py +132 -0
- rem/services/postgres/schema_generator.py +205 -4
- rem/services/postgres/service.py +6 -6
- rem/services/rem/parser.py +44 -9
- rem/services/rem/service.py +36 -2
- rem/services/session/compression.py +137 -51
- rem/services/session/reload.py +15 -8
- rem/settings.py +515 -27
- rem/sql/background_indexes.sql +21 -16
- rem/sql/migrations/001_install.sql +387 -54
- rem/sql/migrations/002_install_models.sql +2304 -377
- rem/sql/migrations/003_optional_extensions.sql +326 -0
- rem/sql/migrations/004_cache_system.sql +548 -0
- rem/sql/migrations/005_schema_update.sql +145 -0
- rem/utils/README.md +45 -0
- rem/utils/__init__.py +18 -0
- rem/utils/date_utils.py +2 -2
- rem/utils/files.py +157 -1
- rem/utils/model_helpers.py +156 -1
- rem/utils/schema_loader.py +220 -22
- rem/utils/sql_paths.py +146 -0
- rem/utils/sql_types.py +3 -1
- rem/utils/vision.py +1 -1
- rem/workers/__init__.py +3 -1
- rem/workers/db_listener.py +579 -0
- rem/workers/unlogged_maintainer.py +463 -0
- {remdb-0.3.14.dist-info → remdb-0.3.157.dist-info}/METADATA +340 -229
- {remdb-0.3.14.dist-info → remdb-0.3.157.dist-info}/RECORD +109 -80
- {remdb-0.3.14.dist-info → remdb-0.3.157.dist-info}/WHEEL +1 -1
- rem/sql/002_install_models.sql +0 -1068
- rem/sql/install_models.sql +0 -1051
- rem/sql/migrations/003_seed_default_user.sql +0 -48
- {remdb-0.3.14.dist-info → remdb-0.3.157.dist-info}/entry_points.txt +0 -0
rem/api/routers/auth.py
CHANGED
|
@@ -1,20 +1,68 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
Authentication Router.
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
Supports multiple authentication methods:
|
|
5
|
+
1. Email (passwordless): POST /api/auth/email/send-code, POST /api/auth/email/verify
|
|
6
|
+
2. OAuth (Google, Microsoft): GET /api/auth/{provider}/login, GET /api/auth/{provider}/callback
|
|
6
7
|
|
|
7
8
|
Endpoints:
|
|
9
|
+
- POST /api/auth/email/send-code - Send login code to email
|
|
10
|
+
- POST /api/auth/email/verify - Verify code and create session
|
|
8
11
|
- GET /api/auth/{provider}/login - Initiate OAuth flow
|
|
9
12
|
- GET /api/auth/{provider}/callback - OAuth callback
|
|
10
13
|
- POST /api/auth/logout - Clear session
|
|
11
14
|
- GET /api/auth/me - Current user info
|
|
12
15
|
|
|
13
16
|
Supported providers:
|
|
17
|
+
- email: Passwordless email login
|
|
14
18
|
- google: Google OAuth 2.0 / OIDC
|
|
15
19
|
- microsoft: Microsoft Entra ID OIDC
|
|
16
20
|
|
|
17
|
-
|
|
21
|
+
=============================================================================
|
|
22
|
+
Email Authentication Access Control
|
|
23
|
+
=============================================================================
|
|
24
|
+
|
|
25
|
+
The email auth provider implements a tiered access control system:
|
|
26
|
+
|
|
27
|
+
Access Control Flow (send-code):
|
|
28
|
+
User requests login code
|
|
29
|
+
├── User exists in database?
|
|
30
|
+
│ ├── Yes → Check user.tier
|
|
31
|
+
│ │ ├── tier == BLOCKED → Reject "Account is blocked"
|
|
32
|
+
│ │ └── tier != BLOCKED → Allow (send code, existing users grandfathered)
|
|
33
|
+
│ └── No (new user) → Check EMAIL__TRUSTED_EMAIL_DOMAINS
|
|
34
|
+
│ ├── Setting configured → domain in trusted list?
|
|
35
|
+
│ │ ├── Yes → Create user & send code
|
|
36
|
+
│ │ └── No → Reject "Email domain not allowed for signup"
|
|
37
|
+
│ └── Not configured (empty) → Create user & send code (no restrictions)
|
|
38
|
+
|
|
39
|
+
Key Behaviors:
|
|
40
|
+
- Existing users: Always allowed to login (unless tier=BLOCKED)
|
|
41
|
+
- New users: Must have email from trusted domain (if EMAIL__TRUSTED_EMAIL_DOMAINS is set)
|
|
42
|
+
- No restrictions: Leave EMAIL__TRUSTED_EMAIL_DOMAINS empty to allow all domains
|
|
43
|
+
|
|
44
|
+
User Tiers (models.entities.UserTier):
|
|
45
|
+
- BLOCKED: Cannot login (rejected at send-code)
|
|
46
|
+
- ANONYMOUS: Rate-limited anonymous access
|
|
47
|
+
- FREE: Standard free tier
|
|
48
|
+
- BASIC/PRO: Paid tiers with additional features
|
|
49
|
+
|
|
50
|
+
Configuration:
|
|
51
|
+
# Allow only specific domains for new signups
|
|
52
|
+
EMAIL__TRUSTED_EMAIL_DOMAINS=siggymd.ai,example.com
|
|
53
|
+
|
|
54
|
+
# Allow all domains (no restrictions)
|
|
55
|
+
EMAIL__TRUSTED_EMAIL_DOMAINS=
|
|
56
|
+
|
|
57
|
+
Example blocking a user:
|
|
58
|
+
user = await user_repo.get_by_id(user_id, tenant_id="default")
|
|
59
|
+
user.tier = UserTier.BLOCKED
|
|
60
|
+
await user_repo.upsert(user)
|
|
61
|
+
|
|
62
|
+
=============================================================================
|
|
63
|
+
OAuth Design Pattern (OAuth 2.1 + PKCE)
|
|
64
|
+
=============================================================================
|
|
65
|
+
|
|
18
66
|
1. User clicks "Login with Google"
|
|
19
67
|
2. /login generates state + PKCE code_verifier
|
|
20
68
|
3. Store code_verifier in session
|
|
@@ -37,6 +85,7 @@ Environment variables:
|
|
|
37
85
|
AUTH__MICROSOFT__CLIENT_ID=<microsoft-client-id>
|
|
38
86
|
AUTH__MICROSOFT__CLIENT_SECRET=<microsoft-client-secret>
|
|
39
87
|
AUTH__MICROSOFT__TENANT=common
|
|
88
|
+
EMAIL__TRUSTED_EMAIL_DOMAINS=example.com # Optional: restrict new signups
|
|
40
89
|
|
|
41
90
|
References:
|
|
42
91
|
- Authlib: https://docs.authlib.org/en/latest/
|
|
@@ -46,11 +95,13 @@ References:
|
|
|
46
95
|
from fastapi import APIRouter, HTTPException, Request
|
|
47
96
|
from fastapi.responses import RedirectResponse
|
|
48
97
|
from authlib.integrations.starlette_client import OAuth
|
|
98
|
+
from pydantic import BaseModel, EmailStr
|
|
49
99
|
from loguru import logger
|
|
50
100
|
|
|
51
101
|
from ...settings import settings
|
|
52
102
|
from ...services.postgres.service import PostgresService
|
|
53
103
|
from ...services.user_service import UserService
|
|
104
|
+
from ...auth.providers.email import EmailAuthProvider
|
|
54
105
|
|
|
55
106
|
router = APIRouter(prefix="/api/auth", tags=["auth"])
|
|
56
107
|
|
|
@@ -87,6 +138,159 @@ if settings.auth.microsoft.client_id:
|
|
|
87
138
|
logger.info(f"Microsoft OAuth provider registered (tenant: {tenant})")
|
|
88
139
|
|
|
89
140
|
|
|
141
|
+
# =============================================================================
|
|
142
|
+
# Email Authentication Endpoints
|
|
143
|
+
# =============================================================================
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class EmailSendCodeRequest(BaseModel):
|
|
147
|
+
"""Request to send login code."""
|
|
148
|
+
email: EmailStr
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class EmailVerifyRequest(BaseModel):
|
|
152
|
+
"""Request to verify login code."""
|
|
153
|
+
email: EmailStr
|
|
154
|
+
code: str
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
@router.post("/email/send-code")
|
|
158
|
+
async def send_email_code(request: Request, body: EmailSendCodeRequest):
|
|
159
|
+
"""
|
|
160
|
+
Send a login code to an email address.
|
|
161
|
+
|
|
162
|
+
Creates user if not exists (using deterministic UUID from email).
|
|
163
|
+
Stores code in user metadata with expiry.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
request: FastAPI request
|
|
167
|
+
body: EmailSendCodeRequest with email
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
Success status and message
|
|
171
|
+
"""
|
|
172
|
+
if not settings.email.is_configured:
|
|
173
|
+
raise HTTPException(
|
|
174
|
+
status_code=501,
|
|
175
|
+
detail="Email authentication is not configured"
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
# Get database connection
|
|
179
|
+
if not settings.postgres.enabled:
|
|
180
|
+
raise HTTPException(
|
|
181
|
+
status_code=501,
|
|
182
|
+
detail="Database is required for email authentication"
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
db = PostgresService()
|
|
186
|
+
try:
|
|
187
|
+
await db.connect()
|
|
188
|
+
|
|
189
|
+
# Initialize email auth provider
|
|
190
|
+
email_auth = EmailAuthProvider()
|
|
191
|
+
|
|
192
|
+
# Send code
|
|
193
|
+
result = await email_auth.send_code(
|
|
194
|
+
email=body.email,
|
|
195
|
+
db=db,
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
if result.success:
|
|
199
|
+
return {
|
|
200
|
+
"success": True,
|
|
201
|
+
"message": result.message,
|
|
202
|
+
"email": result.email,
|
|
203
|
+
}
|
|
204
|
+
else:
|
|
205
|
+
raise HTTPException(
|
|
206
|
+
status_code=400,
|
|
207
|
+
detail=result.message or result.error
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
except HTTPException:
|
|
211
|
+
raise
|
|
212
|
+
except Exception as e:
|
|
213
|
+
logger.error(f"Error sending login code: {e}")
|
|
214
|
+
raise HTTPException(status_code=500, detail="Failed to send login code")
|
|
215
|
+
finally:
|
|
216
|
+
await db.disconnect()
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
@router.post("/email/verify")
|
|
220
|
+
async def verify_email_code(request: Request, body: EmailVerifyRequest):
|
|
221
|
+
"""
|
|
222
|
+
Verify login code and create session.
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
request: FastAPI request
|
|
226
|
+
body: EmailVerifyRequest with email and code
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
Success status with user info
|
|
230
|
+
"""
|
|
231
|
+
if not settings.email.is_configured:
|
|
232
|
+
raise HTTPException(
|
|
233
|
+
status_code=501,
|
|
234
|
+
detail="Email authentication is not configured"
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
if not settings.postgres.enabled:
|
|
238
|
+
raise HTTPException(
|
|
239
|
+
status_code=501,
|
|
240
|
+
detail="Database is required for email authentication"
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
db = PostgresService()
|
|
244
|
+
try:
|
|
245
|
+
await db.connect()
|
|
246
|
+
|
|
247
|
+
# Initialize email auth provider
|
|
248
|
+
email_auth = EmailAuthProvider()
|
|
249
|
+
|
|
250
|
+
# Verify code
|
|
251
|
+
result = await email_auth.verify_code(
|
|
252
|
+
email=body.email,
|
|
253
|
+
code=body.code,
|
|
254
|
+
db=db,
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
if not result.success:
|
|
258
|
+
raise HTTPException(
|
|
259
|
+
status_code=400,
|
|
260
|
+
detail=result.message or result.error
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
# Create session - compatible with OAuth session format
|
|
264
|
+
user_dict = email_auth.get_user_dict(
|
|
265
|
+
email=result.email,
|
|
266
|
+
user_id=result.user_id,
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
# Store user in session
|
|
270
|
+
request.session["user"] = user_dict
|
|
271
|
+
|
|
272
|
+
logger.info(f"User authenticated via email: {result.email}")
|
|
273
|
+
|
|
274
|
+
return {
|
|
275
|
+
"success": True,
|
|
276
|
+
"message": result.message,
|
|
277
|
+
"user": user_dict,
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
except HTTPException:
|
|
281
|
+
raise
|
|
282
|
+
except Exception as e:
|
|
283
|
+
logger.error(f"Error verifying login code: {e}")
|
|
284
|
+
raise HTTPException(status_code=500, detail="Failed to verify login code")
|
|
285
|
+
finally:
|
|
286
|
+
await db.disconnect()
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
# =============================================================================
|
|
290
|
+
# OAuth Authentication Endpoints
|
|
291
|
+
# =============================================================================
|
|
292
|
+
|
|
293
|
+
|
|
90
294
|
@router.get("/{provider}/login")
|
|
91
295
|
async def login(provider: str, request: Request):
|
|
92
296
|
"""
|
|
@@ -281,3 +485,73 @@ async def me(request: Request):
|
|
|
281
485
|
raise HTTPException(status_code=401, detail="Not authenticated")
|
|
282
486
|
|
|
283
487
|
return user
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
# =============================================================================
|
|
491
|
+
# Development Token Endpoints (non-production only)
|
|
492
|
+
# =============================================================================
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
def generate_dev_token() -> str:
|
|
496
|
+
"""
|
|
497
|
+
Generate a dev token for testing.
|
|
498
|
+
|
|
499
|
+
Token format: dev_<hmac_signature>
|
|
500
|
+
The signature is based on the session secret to ensure only valid tokens work.
|
|
501
|
+
"""
|
|
502
|
+
import hashlib
|
|
503
|
+
import hmac
|
|
504
|
+
|
|
505
|
+
# Use session secret as key
|
|
506
|
+
secret = settings.auth.session_secret or "dev-secret"
|
|
507
|
+
message = "test-user:dev-token"
|
|
508
|
+
|
|
509
|
+
signature = hmac.new(
|
|
510
|
+
secret.encode(),
|
|
511
|
+
message.encode(),
|
|
512
|
+
hashlib.sha256
|
|
513
|
+
).hexdigest()[:32]
|
|
514
|
+
|
|
515
|
+
return f"dev_{signature}"
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
def verify_dev_token(token: str) -> bool:
|
|
519
|
+
"""Verify a dev token is valid."""
|
|
520
|
+
expected = generate_dev_token()
|
|
521
|
+
return token == expected
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
@router.get("/dev/token")
|
|
525
|
+
async def get_dev_token(request: Request):
|
|
526
|
+
"""
|
|
527
|
+
Get a development token for testing (non-production only).
|
|
528
|
+
|
|
529
|
+
This token can be used as a Bearer token to authenticate as the
|
|
530
|
+
test user (test-user / test@rem.local) without going through OAuth.
|
|
531
|
+
|
|
532
|
+
Usage:
|
|
533
|
+
curl -H "Authorization: Bearer <token>" http://localhost:8000/api/v1/...
|
|
534
|
+
|
|
535
|
+
Returns:
|
|
536
|
+
401 if in production environment
|
|
537
|
+
Token and usage instructions otherwise
|
|
538
|
+
"""
|
|
539
|
+
if settings.environment == "production":
|
|
540
|
+
raise HTTPException(
|
|
541
|
+
status_code=401,
|
|
542
|
+
detail="Dev tokens are not available in production"
|
|
543
|
+
)
|
|
544
|
+
|
|
545
|
+
token = generate_dev_token()
|
|
546
|
+
|
|
547
|
+
return {
|
|
548
|
+
"token": token,
|
|
549
|
+
"type": "Bearer",
|
|
550
|
+
"user": {
|
|
551
|
+
"id": "test-user",
|
|
552
|
+
"email": "test@rem.local",
|
|
553
|
+
"name": "Test User",
|
|
554
|
+
},
|
|
555
|
+
"usage": f'curl -H "Authorization: Bearer {token}" http://localhost:8000/api/v1/...',
|
|
556
|
+
"warning": "This token is for development/testing only and will not work in production.",
|
|
557
|
+
}
|