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.
Files changed (112) hide show
  1. rem/agentic/README.md +76 -0
  2. rem/agentic/__init__.py +15 -0
  3. rem/agentic/agents/__init__.py +32 -2
  4. rem/agentic/agents/agent_manager.py +310 -0
  5. rem/agentic/agents/sse_simulator.py +502 -0
  6. rem/agentic/context.py +51 -27
  7. rem/agentic/context_builder.py +5 -3
  8. rem/agentic/llm_provider_models.py +301 -0
  9. rem/agentic/mcp/tool_wrapper.py +155 -18
  10. rem/agentic/otel/setup.py +93 -4
  11. rem/agentic/providers/phoenix.py +371 -108
  12. rem/agentic/providers/pydantic_ai.py +280 -57
  13. rem/agentic/schema.py +361 -21
  14. rem/agentic/tools/rem_tools.py +3 -3
  15. rem/api/README.md +215 -1
  16. rem/api/deps.py +255 -0
  17. rem/api/main.py +132 -40
  18. rem/api/mcp_router/resources.py +1 -1
  19. rem/api/mcp_router/server.py +28 -5
  20. rem/api/mcp_router/tools.py +555 -7
  21. rem/api/routers/admin.py +494 -0
  22. rem/api/routers/auth.py +278 -4
  23. rem/api/routers/chat/completions.py +402 -20
  24. rem/api/routers/chat/models.py +88 -10
  25. rem/api/routers/chat/otel_utils.py +33 -0
  26. rem/api/routers/chat/sse_events.py +542 -0
  27. rem/api/routers/chat/streaming.py +697 -45
  28. rem/api/routers/dev.py +81 -0
  29. rem/api/routers/feedback.py +268 -0
  30. rem/api/routers/messages.py +473 -0
  31. rem/api/routers/models.py +78 -0
  32. rem/api/routers/query.py +360 -0
  33. rem/api/routers/shared_sessions.py +406 -0
  34. rem/auth/__init__.py +13 -3
  35. rem/auth/middleware.py +186 -22
  36. rem/auth/providers/__init__.py +4 -1
  37. rem/auth/providers/email.py +215 -0
  38. rem/cli/commands/README.md +237 -64
  39. rem/cli/commands/cluster.py +1808 -0
  40. rem/cli/commands/configure.py +4 -7
  41. rem/cli/commands/db.py +386 -143
  42. rem/cli/commands/experiments.py +468 -76
  43. rem/cli/commands/process.py +14 -8
  44. rem/cli/commands/schema.py +97 -50
  45. rem/cli/commands/session.py +336 -0
  46. rem/cli/dreaming.py +2 -2
  47. rem/cli/main.py +29 -6
  48. rem/config.py +10 -3
  49. rem/models/core/core_model.py +7 -1
  50. rem/models/core/experiment.py +58 -14
  51. rem/models/core/rem_query.py +5 -2
  52. rem/models/entities/__init__.py +25 -0
  53. rem/models/entities/domain_resource.py +38 -0
  54. rem/models/entities/feedback.py +123 -0
  55. rem/models/entities/message.py +30 -1
  56. rem/models/entities/ontology.py +1 -1
  57. rem/models/entities/ontology_config.py +1 -1
  58. rem/models/entities/session.py +83 -0
  59. rem/models/entities/shared_session.py +180 -0
  60. rem/models/entities/subscriber.py +175 -0
  61. rem/models/entities/user.py +1 -0
  62. rem/registry.py +10 -4
  63. rem/schemas/agents/core/agent-builder.yaml +134 -0
  64. rem/schemas/agents/examples/contract-analyzer.yaml +1 -1
  65. rem/schemas/agents/examples/contract-extractor.yaml +1 -1
  66. rem/schemas/agents/examples/cv-parser.yaml +1 -1
  67. rem/schemas/agents/rem.yaml +7 -3
  68. rem/services/__init__.py +3 -1
  69. rem/services/content/service.py +92 -19
  70. rem/services/email/__init__.py +10 -0
  71. rem/services/email/service.py +459 -0
  72. rem/services/email/templates.py +360 -0
  73. rem/services/embeddings/api.py +4 -4
  74. rem/services/embeddings/worker.py +16 -16
  75. rem/services/phoenix/client.py +154 -14
  76. rem/services/postgres/README.md +197 -15
  77. rem/services/postgres/__init__.py +2 -1
  78. rem/services/postgres/diff_service.py +547 -0
  79. rem/services/postgres/pydantic_to_sqlalchemy.py +470 -140
  80. rem/services/postgres/repository.py +132 -0
  81. rem/services/postgres/schema_generator.py +205 -4
  82. rem/services/postgres/service.py +6 -6
  83. rem/services/rem/parser.py +44 -9
  84. rem/services/rem/service.py +36 -2
  85. rem/services/session/compression.py +137 -51
  86. rem/services/session/reload.py +15 -8
  87. rem/settings.py +515 -27
  88. rem/sql/background_indexes.sql +21 -16
  89. rem/sql/migrations/001_install.sql +387 -54
  90. rem/sql/migrations/002_install_models.sql +2304 -377
  91. rem/sql/migrations/003_optional_extensions.sql +326 -0
  92. rem/sql/migrations/004_cache_system.sql +548 -0
  93. rem/sql/migrations/005_schema_update.sql +145 -0
  94. rem/utils/README.md +45 -0
  95. rem/utils/__init__.py +18 -0
  96. rem/utils/date_utils.py +2 -2
  97. rem/utils/files.py +157 -1
  98. rem/utils/model_helpers.py +156 -1
  99. rem/utils/schema_loader.py +220 -22
  100. rem/utils/sql_paths.py +146 -0
  101. rem/utils/sql_types.py +3 -1
  102. rem/utils/vision.py +1 -1
  103. rem/workers/__init__.py +3 -1
  104. rem/workers/db_listener.py +579 -0
  105. rem/workers/unlogged_maintainer.py +463 -0
  106. {remdb-0.3.14.dist-info → remdb-0.3.157.dist-info}/METADATA +340 -229
  107. {remdb-0.3.14.dist-info → remdb-0.3.157.dist-info}/RECORD +109 -80
  108. {remdb-0.3.14.dist-info → remdb-0.3.157.dist-info}/WHEEL +1 -1
  109. rem/sql/002_install_models.sql +0 -1068
  110. rem/sql/install_models.sql +0 -1051
  111. rem/sql/migrations/003_seed_default_user.sql +0 -48
  112. {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
- OAuth 2.1 Authentication Router.
2
+ Authentication Router.
3
3
 
4
- Leverages Authlib for standards-compliant OAuth/OIDC implementation.
5
- Minimal custom code - Authlib handles PKCE, token validation, JWKS.
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
- Design Pattern (OAuth 2.1 + PKCE):
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
+ }