remdb 0.3.141__py3-none-any.whl → 0.3.163__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/agentic/agents/__init__.py +16 -0
- rem/agentic/agents/agent_manager.py +310 -0
- rem/agentic/context.py +81 -3
- rem/agentic/context_builder.py +18 -3
- rem/api/deps.py +3 -5
- rem/api/main.py +22 -3
- rem/api/mcp_router/server.py +2 -0
- rem/api/mcp_router/tools.py +90 -0
- rem/api/middleware/tracking.py +5 -5
- rem/api/routers/auth.py +346 -5
- rem/api/routers/chat/completions.py +4 -2
- rem/api/routers/chat/streaming.py +77 -22
- rem/api/routers/messages.py +24 -15
- rem/auth/__init__.py +13 -3
- rem/auth/jwt.py +352 -0
- rem/auth/middleware.py +108 -6
- rem/auth/providers/__init__.py +4 -1
- rem/auth/providers/email.py +215 -0
- rem/cli/commands/experiments.py +32 -46
- rem/models/core/experiment.py +4 -14
- rem/models/entities/__init__.py +4 -0
- rem/models/entities/subscriber.py +175 -0
- rem/models/entities/user.py +1 -0
- rem/schemas/agents/core/agent-builder.yaml +134 -0
- rem/services/__init__.py +3 -1
- rem/services/content/service.py +4 -3
- rem/services/email/__init__.py +10 -0
- rem/services/email/service.py +511 -0
- rem/services/email/templates.py +360 -0
- rem/services/postgres/README.md +38 -0
- rem/services/postgres/diff_service.py +19 -3
- rem/services/postgres/pydantic_to_sqlalchemy.py +45 -13
- rem/services/postgres/repository.py +5 -4
- rem/services/session/compression.py +113 -50
- rem/services/session/reload.py +14 -7
- rem/services/user_service.py +29 -0
- rem/settings.py +199 -4
- rem/sql/migrations/005_schema_update.sql +145 -0
- rem/utils/README.md +45 -0
- rem/utils/files.py +157 -1
- {remdb-0.3.141.dist-info → remdb-0.3.163.dist-info}/METADATA +7 -5
- {remdb-0.3.141.dist-info → remdb-0.3.163.dist-info}/RECORD +44 -35
- {remdb-0.3.141.dist-info → remdb-0.3.163.dist-info}/WHEEL +0 -0
- {remdb-0.3.141.dist-info → remdb-0.3.163.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Subscriber - Email subscription management.
|
|
3
|
+
|
|
4
|
+
This model stores subscribers who sign up via websites/apps.
|
|
5
|
+
Subscribers can be collected before user registration for newsletters,
|
|
6
|
+
updates, and approval-based access control.
|
|
7
|
+
|
|
8
|
+
Key features:
|
|
9
|
+
- Deterministic UUID from email (same email = same ID)
|
|
10
|
+
- Approval workflow for access control
|
|
11
|
+
- Tags for segmentation
|
|
12
|
+
- Origin tracking for analytics
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import uuid
|
|
16
|
+
from datetime import datetime, timezone
|
|
17
|
+
from enum import Enum
|
|
18
|
+
from typing import Optional
|
|
19
|
+
|
|
20
|
+
from pydantic import Field, EmailStr, model_validator
|
|
21
|
+
|
|
22
|
+
from ..core import CoreModel
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class SubscriberStatus(str, Enum):
|
|
26
|
+
"""Subscription status."""
|
|
27
|
+
|
|
28
|
+
ACTIVE = "active" # Actively subscribed
|
|
29
|
+
UNSUBSCRIBED = "unsubscribed" # User unsubscribed
|
|
30
|
+
BOUNCED = "bounced" # Email bounced
|
|
31
|
+
PENDING = "pending" # Pending confirmation (if double opt-in)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class SubscriberOrigin(str, Enum):
|
|
35
|
+
"""Where the subscription originated from."""
|
|
36
|
+
|
|
37
|
+
WEBSITE = "website" # Main website subscribe form
|
|
38
|
+
LANDING_PAGE = "landing_page" # Campaign landing page
|
|
39
|
+
APP = "app" # In-app subscription
|
|
40
|
+
IMPORT = "import" # Bulk import
|
|
41
|
+
REFERRAL = "referral" # Referred by another user
|
|
42
|
+
OTHER = "other"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class Subscriber(CoreModel):
|
|
46
|
+
"""
|
|
47
|
+
Email subscriber for newsletters and access control.
|
|
48
|
+
|
|
49
|
+
This model captures subscribers who sign up via the website, landing pages,
|
|
50
|
+
or in-app prompts. Uses deterministic UUID from email for natural upserts.
|
|
51
|
+
|
|
52
|
+
Access control via `approved` field:
|
|
53
|
+
- When email auth checks subscriber status, only approved subscribers
|
|
54
|
+
can complete login (if approval is enabled in settings).
|
|
55
|
+
- Subscribers can be pre-approved, or approved manually/automatically.
|
|
56
|
+
|
|
57
|
+
Usage:
|
|
58
|
+
from rem.services.postgres import Repository
|
|
59
|
+
from rem.models.entities import Subscriber, SubscriberStatus
|
|
60
|
+
|
|
61
|
+
repo = Repository(Subscriber, db=db)
|
|
62
|
+
|
|
63
|
+
# Create subscriber (ID auto-generated from email)
|
|
64
|
+
subscriber = Subscriber(
|
|
65
|
+
email="user@example.com",
|
|
66
|
+
name="John Doe",
|
|
67
|
+
origin=SubscriberOrigin.WEBSITE,
|
|
68
|
+
)
|
|
69
|
+
await repo.upsert(subscriber)
|
|
70
|
+
|
|
71
|
+
# Check if approved for login
|
|
72
|
+
subscriber = await repo.get_by_id(subscriber.id, tenant_id="default")
|
|
73
|
+
if subscriber and subscriber.approved:
|
|
74
|
+
# Allow login
|
|
75
|
+
pass
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
# Required field
|
|
79
|
+
email: EmailStr = Field(
|
|
80
|
+
description="Subscriber's email address (unique identifier)"
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Optional fields
|
|
84
|
+
name: Optional[str] = Field(
|
|
85
|
+
default=None,
|
|
86
|
+
description="Subscriber's name (optional)"
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
comment: Optional[str] = Field(
|
|
90
|
+
default=None,
|
|
91
|
+
max_length=500,
|
|
92
|
+
description="Optional comment or message from subscriber"
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
status: SubscriberStatus = Field(
|
|
96
|
+
default=SubscriberStatus.ACTIVE,
|
|
97
|
+
description="Current subscription status"
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# Access control
|
|
101
|
+
approved: bool = Field(
|
|
102
|
+
default=False,
|
|
103
|
+
description="Whether subscriber is approved for login (for approval workflows)"
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
approved_at: Optional[datetime] = Field(
|
|
107
|
+
default=None,
|
|
108
|
+
description="When the subscriber was approved"
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
approved_by: Optional[str] = Field(
|
|
112
|
+
default=None,
|
|
113
|
+
description="Who approved the subscriber (user ID or 'system')"
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# Origin tracking
|
|
117
|
+
origin: SubscriberOrigin = Field(
|
|
118
|
+
default=SubscriberOrigin.WEBSITE,
|
|
119
|
+
description="Where the subscription originated"
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
origin_detail: Optional[str] = Field(
|
|
123
|
+
default=None,
|
|
124
|
+
description="Additional origin context (e.g., campaign name, page URL)"
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# Timestamps
|
|
128
|
+
subscribed_at: datetime = Field(
|
|
129
|
+
default_factory=lambda: datetime.now(timezone.utc),
|
|
130
|
+
description="When the subscription was created"
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
unsubscribed_at: Optional[datetime] = Field(
|
|
134
|
+
default=None,
|
|
135
|
+
description="When the user unsubscribed (if applicable)"
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
# Compliance
|
|
139
|
+
ip_address: Optional[str] = Field(
|
|
140
|
+
default=None,
|
|
141
|
+
description="IP address at subscription time (for compliance)"
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
user_agent: Optional[str] = Field(
|
|
145
|
+
default=None,
|
|
146
|
+
description="Browser user agent at subscription time"
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
# Segmentation
|
|
150
|
+
tags: list[str] = Field(
|
|
151
|
+
default_factory=list,
|
|
152
|
+
description="Tags for segmentation (e.g., ['early-access', 'beta'])"
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
@staticmethod
|
|
156
|
+
def email_to_uuid(email: str) -> uuid.UUID:
|
|
157
|
+
"""Generate a deterministic UUID from an email address.
|
|
158
|
+
|
|
159
|
+
Uses UUID v5 with DNS namespace for consistency with
|
|
160
|
+
EmailService.generate_user_id_from_email().
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
email: Email address
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
Deterministic UUID
|
|
167
|
+
"""
|
|
168
|
+
return uuid.uuid5(uuid.NAMESPACE_DNS, email.lower().strip())
|
|
169
|
+
|
|
170
|
+
@model_validator(mode="after")
|
|
171
|
+
def set_id_from_email(self) -> "Subscriber":
|
|
172
|
+
"""Auto-generate deterministic ID from email for natural upsert."""
|
|
173
|
+
if self.email:
|
|
174
|
+
self.id = self.email_to_uuid(self.email)
|
|
175
|
+
return self
|
rem/models/entities/user.py
CHANGED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
type: object
|
|
2
|
+
description: |
|
|
3
|
+
# Agent Builder - Create Custom AI Agents Through Conversation
|
|
4
|
+
|
|
5
|
+
You help users create custom AI agents by chatting with them naturally.
|
|
6
|
+
Gather requirements conversationally, show previews, and save the agent when ready.
|
|
7
|
+
|
|
8
|
+
## Your Workflow
|
|
9
|
+
|
|
10
|
+
1. **Understand the need**: Ask what they want the agent to do
|
|
11
|
+
2. **Define personality**: Help them choose tone and style
|
|
12
|
+
3. **Structure outputs**: If needed, define what data the agent captures
|
|
13
|
+
4. **Preview**: Show them what the agent will look like
|
|
14
|
+
5. **Save**: Use `save_agent` tool to persist it
|
|
15
|
+
|
|
16
|
+
## Conversation Style
|
|
17
|
+
|
|
18
|
+
Be friendly and helpful. Ask one or two questions at a time.
|
|
19
|
+
Don't overwhelm with options - guide them step by step.
|
|
20
|
+
|
|
21
|
+
## Gathering Requirements
|
|
22
|
+
|
|
23
|
+
Ask about:
|
|
24
|
+
- What should this agent help with?
|
|
25
|
+
- What tone should it have? (casual, professional, empathetic, etc.)
|
|
26
|
+
- Should it capture any specific information? (optional)
|
|
27
|
+
- What should it be called?
|
|
28
|
+
|
|
29
|
+
## Preview Format
|
|
30
|
+
|
|
31
|
+
Before saving, show a preview using markdown:
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
## Agent Preview: {name}
|
|
35
|
+
|
|
36
|
+
**Personality:**
|
|
37
|
+
{brief description of tone and approach}
|
|
38
|
+
|
|
39
|
+
**System Prompt:**
|
|
40
|
+
{the actual prompt that will guide the agent}
|
|
41
|
+
|
|
42
|
+
**Structured Fields:** (if any)
|
|
43
|
+
| Field | Type | Description |
|
|
44
|
+
|-------|------|-------------|
|
|
45
|
+
| answer | string | Response to user |
|
|
46
|
+
| ... | ... | ... |
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Ask: "Does this look good? I can save it now or we can adjust anything."
|
|
50
|
+
|
|
51
|
+
## Saving the Agent
|
|
52
|
+
|
|
53
|
+
When the user approves, call `save_agent` with:
|
|
54
|
+
- `name`: kebab-case name (e.g., "customer-support-bot")
|
|
55
|
+
- `description`: The full system prompt
|
|
56
|
+
- `properties`: Structured output fields (optional, defaults to just "answer")
|
|
57
|
+
- `required`: Which fields are required (optional, defaults to ["answer"])
|
|
58
|
+
- `tags`: Relevant tags for organization
|
|
59
|
+
|
|
60
|
+
## Default Tools
|
|
61
|
+
|
|
62
|
+
All agents automatically get `search_rem` and `register_metadata` tools.
|
|
63
|
+
You don't need to specify these.
|
|
64
|
+
|
|
65
|
+
## Example Conversation
|
|
66
|
+
|
|
67
|
+
User: "I want an agent that helps write professional emails"
|
|
68
|
+
|
|
69
|
+
You: "Great idea! Let me help you create an email writing assistant.
|
|
70
|
+
|
|
71
|
+
What tone should it have? For example:
|
|
72
|
+
- Professional and formal
|
|
73
|
+
- Friendly but polished
|
|
74
|
+
- Direct and concise"
|
|
75
|
+
|
|
76
|
+
User: "Friendly but polished"
|
|
77
|
+
|
|
78
|
+
You: "Perfect! Should it just write emails, or also help with things like:
|
|
79
|
+
- Suggesting subject lines
|
|
80
|
+
- Adjusting tone for different recipients
|
|
81
|
+
- Summarizing long email threads"
|
|
82
|
+
|
|
83
|
+
User: "All of those would be helpful"
|
|
84
|
+
|
|
85
|
+
You: "Let me show you a preview:
|
|
86
|
+
|
|
87
|
+
## Agent Preview: email-assistant
|
|
88
|
+
|
|
89
|
+
**Personality:** Friendly, professional, helpful writing partner
|
|
90
|
+
|
|
91
|
+
**System Prompt:**
|
|
92
|
+
You are a friendly email writing assistant. Help users craft polished,
|
|
93
|
+
professional emails while maintaining a warm tone. You can:
|
|
94
|
+
- Write new emails from scratch
|
|
95
|
+
- Suggest compelling subject lines
|
|
96
|
+
- Adjust tone for different audiences
|
|
97
|
+
- Summarize long email threads
|
|
98
|
+
|
|
99
|
+
Always ask clarifying questions if the request is unclear.
|
|
100
|
+
|
|
101
|
+
**Structured Fields:**
|
|
102
|
+
| Field | Type | Description |
|
|
103
|
+
|-------|------|-------------|
|
|
104
|
+
| answer | string | Your response or the drafted email |
|
|
105
|
+
|
|
106
|
+
Does this look good? I can save it now or adjust anything."
|
|
107
|
+
|
|
108
|
+
User: "Looks great, save it!"
|
|
109
|
+
|
|
110
|
+
You: *calls save_agent tool*
|
|
111
|
+
"Done! Your email-assistant is ready. Use `/custom-agent email-assistant` to start chatting with it."
|
|
112
|
+
|
|
113
|
+
properties:
|
|
114
|
+
answer:
|
|
115
|
+
type: string
|
|
116
|
+
description: Your conversational response to the user
|
|
117
|
+
|
|
118
|
+
required:
|
|
119
|
+
- answer
|
|
120
|
+
|
|
121
|
+
json_schema_extra:
|
|
122
|
+
kind: agent
|
|
123
|
+
name: agent-builder
|
|
124
|
+
version: "1.0.0"
|
|
125
|
+
tags:
|
|
126
|
+
- meta
|
|
127
|
+
- builder
|
|
128
|
+
tools:
|
|
129
|
+
- name: save_agent
|
|
130
|
+
description: "Save the agent schema to make it available for use"
|
|
131
|
+
- name: search_rem
|
|
132
|
+
description: "Search for existing agents as examples"
|
|
133
|
+
- name: register_metadata
|
|
134
|
+
description: "Record session metadata"
|
rem/services/__init__.py
CHANGED
|
@@ -4,13 +4,15 @@ REM Services
|
|
|
4
4
|
Service layer for REM system operations:
|
|
5
5
|
- PostgresService: PostgreSQL/CloudNativePG database operations
|
|
6
6
|
- RemService: REM query execution and graph operations
|
|
7
|
+
- EmailService: Transactional emails and passwordless login
|
|
7
8
|
|
|
8
9
|
For file/S3 operations, use rem.services.fs instead:
|
|
9
10
|
from rem.services.fs import FS, S3Provider
|
|
10
11
|
"""
|
|
11
12
|
|
|
13
|
+
from .email import EmailService
|
|
12
14
|
from .fs.service import FileSystemService
|
|
13
15
|
from .postgres import PostgresService
|
|
14
16
|
from .rem import RemService
|
|
15
17
|
|
|
16
|
-
__all__ = ["PostgresService", "RemService", "FileSystemService"]
|
|
18
|
+
__all__ = ["EmailService", "PostgresService", "RemService", "FileSystemService"]
|
rem/services/content/service.py
CHANGED
|
@@ -666,10 +666,11 @@ class ContentService:
|
|
|
666
666
|
# IMPORTANT: category field distinguishes agents from evaluators
|
|
667
667
|
# - kind=agent → category="agent" (AI agents with tools/resources)
|
|
668
668
|
# - kind=evaluator → category="evaluator" (LLM-as-a-Judge evaluators)
|
|
669
|
-
#
|
|
669
|
+
# User-scoped schemas: if user_id provided, scope to user's tenant
|
|
670
|
+
# System schemas: if no user_id, use "system" tenant for shared access
|
|
670
671
|
schema_entity = Schema(
|
|
671
|
-
tenant_id="system",
|
|
672
|
-
user_id=
|
|
672
|
+
tenant_id=user_id or "system",
|
|
673
|
+
user_id=user_id,
|
|
673
674
|
name=name,
|
|
674
675
|
spec=schema_data,
|
|
675
676
|
category=kind, # Maps kind → category for database filtering
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Email Service Module.
|
|
3
|
+
|
|
4
|
+
Provides EmailService for sending transactional emails and passwordless login.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .service import EmailService
|
|
8
|
+
from .templates import EmailTemplate, login_code_template
|
|
9
|
+
|
|
10
|
+
__all__ = ["EmailService", "EmailTemplate", "login_code_template"]
|