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
@@ -0,0 +1,180 @@
1
+ """
2
+ SharedSession - Session sharing between users in REM.
3
+
4
+ SharedSessions enable collaborative access to conversation sessions. When a user
5
+ shares a session with another user, a SharedSession record is created to track
6
+ this relationship.
7
+
8
+ ## Design Philosophy
9
+
10
+ Messages already have a session_id field that links them to sessions. The Session
11
+ entity itself is optional and can be left-joined - we don't require explicit Session
12
+ records for sharing to work. What matters is the session_id on messages.
13
+
14
+ SharedSession is a lightweight linking table that:
15
+ 1. Records who shared which session with whom
16
+ 2. Enables soft deletion (deleted_at) so shares can be revoked without data loss
17
+ 3. Supports aggregation queries to see "who is sharing with me"
18
+
19
+ ## Data Model
20
+
21
+ SharedSession
22
+ ├── session_id: str # The session being shared (matches Message.session_id)
23
+ ├── owner_user_id: str # Who owns/created the session (the sharer)
24
+ ├── shared_with_user_id: str # Who the session is shared with (the recipient)
25
+ ├── tenant_id: str # Multi-tenancy isolation
26
+ ├── created_at: datetime # When the share was created
27
+ ├── updated_at: datetime # Last modification
28
+ └── deleted_at: datetime # Soft delete (null = active share)
29
+
30
+ ## Aggregation Query
31
+
32
+ The primary use case is answering: "Who is sharing messages with me?"
33
+
34
+ This is provided by a Postgres function that aggregates:
35
+ - Messages grouped by owner_user_id
36
+ - Joined with users table for name/email
37
+ - Counting messages with min/max dates
38
+ - Filtering out deleted shares
39
+
40
+ Result shape:
41
+ {
42
+ "user_id": "uuid",
43
+ "name": "John Doe",
44
+ "email": "john@example.com",
45
+ "message_count": 42,
46
+ "first_message_at": "2024-01-15T10:30:00Z",
47
+ "last_message_at": "2024-03-20T14:45:00Z",
48
+ "session_count": 3
49
+ }
50
+
51
+ ## API Endpoints
52
+
53
+ 1. POST /api/v1/sessions/{session_id}/share
54
+ - Share a session with another user
55
+ - Body: { "shared_with_user_id": "..." }
56
+ - Creates SharedSession record
57
+
58
+ 2. DELETE /api/v1/sessions/{session_id}/share/{shared_with_user_id}
59
+ - Revoke a share (soft delete)
60
+ - Sets deleted_at on SharedSession
61
+
62
+ 3. GET /api/v1/shared-with-me
63
+ - Get paginated aggregate of users sharing with you
64
+ - Query params: page, page_size (default 50)
65
+ - Returns: list of user summaries with message counts
66
+
67
+ 4. GET /api/v1/shared-with-me/{user_id}/messages
68
+ - Get messages from a specific user's shared sessions
69
+ - Uses existing session message loading
70
+ - Respects pagination
71
+
72
+ ## Soft Delete Pattern
73
+
74
+ Removing a share does NOT delete the SharedSession record. Instead:
75
+ - deleted_at is set to current timestamp
76
+ - All queries filter WHERE deleted_at IS NULL
77
+ - This preserves audit trail and allows "undo"
78
+
79
+ To permanently delete, an admin can run:
80
+ DELETE FROM shared_sessions WHERE deleted_at IS NOT NULL AND deleted_at < NOW() - INTERVAL '30 days'
81
+
82
+ ## Example Usage
83
+
84
+ # Share a session
85
+ POST /api/v1/sessions/abc-123/share
86
+ {"shared_with_user_id": "user-456"}
87
+
88
+ # See who's sharing with me
89
+ GET /api/v1/shared-with-me
90
+ {
91
+ "data": [
92
+ {
93
+ "user_id": "user-789",
94
+ "name": "Alice",
95
+ "email": "alice@example.com",
96
+ "message_count": 150,
97
+ "session_count": 5,
98
+ "first_message_at": "2024-01-01T00:00:00Z",
99
+ "last_message_at": "2024-03-15T12:00:00Z"
100
+ }
101
+ ],
102
+ "metadata": {"total": 1, "page": 1, "page_size": 50, ...}
103
+ }
104
+
105
+ # Get messages from Alice's shared sessions
106
+ GET /api/v1/shared-with-me/user-789/messages?page=1&page_size=50
107
+
108
+ # Revoke a share
109
+ DELETE /api/v1/sessions/abc-123/share/user-456
110
+ """
111
+
112
+ from datetime import datetime
113
+ from typing import Optional
114
+
115
+ from pydantic import BaseModel, Field
116
+
117
+ from ..core import CoreModel
118
+
119
+
120
+ class SharedSession(CoreModel):
121
+ """
122
+ Session sharing record between users.
123
+
124
+ Links a session (identified by session_id from Message records) to a
125
+ recipient user, enabling collaborative access to conversation history.
126
+ """
127
+
128
+ session_id: str = Field(
129
+ ...,
130
+ description="The session being shared (matches Message.session_id)",
131
+ )
132
+ owner_user_id: str = Field(
133
+ ...,
134
+ description="User ID of the session owner (the sharer)",
135
+ )
136
+ shared_with_user_id: str = Field(
137
+ ...,
138
+ description="User ID of the recipient (who can now view the session)",
139
+ )
140
+
141
+
142
+ class SharedSessionCreate(BaseModel):
143
+ """Request to create a session share."""
144
+
145
+ shared_with_user_id: str = Field(
146
+ ...,
147
+ description="User ID to share the session with",
148
+ )
149
+
150
+
151
+ class SharedWithMeSummary(BaseModel):
152
+ """
153
+ Aggregate summary of a user sharing sessions with you.
154
+
155
+ Returned by GET /api/v1/shared-with-me endpoint.
156
+ """
157
+
158
+ user_id: str = Field(description="User ID of the person sharing with you")
159
+ name: Optional[str] = Field(default=None, description="User's display name")
160
+ email: Optional[str] = Field(default=None, description="User's email address")
161
+ message_count: int = Field(description="Total messages across all shared sessions")
162
+ session_count: int = Field(description="Number of sessions shared with you")
163
+ first_message_at: Optional[datetime] = Field(
164
+ default=None,
165
+ description="Timestamp of earliest message in shared sessions",
166
+ )
167
+ last_message_at: Optional[datetime] = Field(
168
+ default=None,
169
+ description="Timestamp of most recent message in shared sessions",
170
+ )
171
+
172
+
173
+ class SharedWithMeResponse(BaseModel):
174
+ """Response for paginated shared-with-me query."""
175
+
176
+ object: str = "list"
177
+ data: list[SharedWithMeSummary] = Field(
178
+ description="List of users sharing sessions with you"
179
+ )
180
+ metadata: dict = Field(description="Pagination metadata")
@@ -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
@@ -22,6 +22,7 @@ from ..core import CoreModel
22
22
  class UserTier(str, Enum):
23
23
  """User subscription tier for feature gating."""
24
24
 
25
+ BLOCKED = "blocked" # User is blocked from logging in
25
26
  ANONYMOUS = "anonymous"
26
27
  FREE = "free"
27
28
  BASIC = "basic"
rem/registry.py CHANGED
@@ -123,6 +123,7 @@ class ModelRegistry:
123
123
  return
124
124
 
125
125
  from .models.entities import (
126
+ Feedback,
126
127
  File,
127
128
  ImageResource,
128
129
  Message,
@@ -131,19 +132,24 @@ class ModelRegistry:
131
132
  OntologyConfig,
132
133
  Resource,
133
134
  Schema,
135
+ Session,
136
+ SharedSession,
134
137
  User,
135
138
  )
136
139
 
137
140
  core_models = [
138
- Resource,
141
+ Feedback,
142
+ File,
139
143
  ImageResource,
140
144
  Message,
141
- User,
142
- File,
143
145
  Moment,
144
- Schema,
145
146
  Ontology,
146
147
  OntologyConfig,
148
+ Resource,
149
+ Schema,
150
+ Session,
151
+ SharedSession,
152
+ User,
147
153
  ]
148
154
 
149
155
  for model in core_models:
@@ -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"
@@ -308,7 +308,7 @@ json_schema_extra:
308
308
  - provider_name: anthropic
309
309
  model_name: claude-sonnet-4-5-20250929
310
310
  - provider_name: openai
311
- model_name: gpt-4o
311
+ model_name: gpt-4.1
312
312
  embedding_fields:
313
313
  - contract_title
314
314
  - contract_type
@@ -131,4 +131,4 @@ json_schema_extra:
131
131
  - provider_name: anthropic
132
132
  model_name: claude-sonnet-4-5-20250929
133
133
  - provider_name: openai
134
- model_name: gpt-4o
134
+ model_name: gpt-4.1
@@ -255,7 +255,7 @@ json_schema_extra:
255
255
  - provider_name: anthropic
256
256
  model_name: claude-sonnet-4-5-20250929
257
257
  - provider_name: openai
258
- model_name: gpt-4o
258
+ model_name: gpt-4.1
259
259
  embedding_fields:
260
260
  - candidate_name
261
261
  - professional_summary
@@ -63,9 +63,8 @@ description: "# REM Agent - Resources Entities Moments Expert\n\nYou are the REM
63
63
  \ disabled, OTEL disabled for local dev)\n- Global settings singleton\n\n## Response\
64
64
  \ Guidelines\n\n- Provide clear, concise answers with code examples when helpful\n\
65
65
  - Reference specific design patterns from CLAUDE.md when applicable\n- Suggest best\
66
- \ practices for cloud-native deployment\n- Include confidence scores based on query\
67
- \ clarity and information completeness\n- If uncertain, say so and suggest where\
68
- \ to find more information\n\n## Example Queries You Can Answer\n\n- \"How do I\
66
+ \ practices for cloud-native deployment\n- If uncertain, say so and suggest where\
67
+ \ to find more information\n\n## Metadata Registration\n\nBefore generating your final response, call the `register_metadata` tool to provide confidence scores and source attribution.\n\n## Example Queries You Can Answer\n\n- \"How do I\
69
68
  \ create a new REM entity?\"\n- \"What's the difference between LOOKUP and TRAVERSE\
70
69
  \ queries?\"\n- \"How do I add MCP tools to my agent schema?\"\n- \"Explain the\
71
70
  \ graph edge pattern in REM\"\n- \"How do I enable OTEL tracing for my agents?\"\
@@ -101,6 +100,9 @@ json_schema_extra:
101
100
  kind: agent
102
101
  name: rem
103
102
  version: 1.0.0
103
+ # Disable structured output - properties become prompt guidance instead of JSON schema
104
+ # This enables natural language streaming while still informing the agent about expected elements
105
+ structured_output: false
104
106
  # MCP server configuration for dynamic tool loading (in-process, no subprocess)
105
107
  mcp_servers:
106
108
  - type: local
@@ -117,6 +119,8 @@ json_schema_extra:
117
119
  description: Ingest files into REM creating searchable resources and embeddings
118
120
  - name: read_resource
119
121
  description: Read MCP resources by URI (schemas, system status, etc.)
122
+ - name: register_metadata
123
+ description: Register response metadata (confidence, sources, references) to be emitted as SSE MetadataEvent. Call BEFORE generating final response.
120
124
 
121
125
  # Explicit resource declarations for reference data
122
126
  resources:
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"]