remdb 0.3.114__py3-none-any.whl → 0.3.172__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 +311 -0
- rem/agentic/agents/sse_simulator.py +2 -0
- rem/agentic/context.py +103 -5
- rem/agentic/context_builder.py +36 -9
- rem/agentic/mcp/tool_wrapper.py +161 -18
- rem/agentic/otel/setup.py +1 -0
- rem/agentic/providers/phoenix.py +371 -108
- rem/agentic/providers/pydantic_ai.py +172 -30
- rem/agentic/schema.py +8 -4
- rem/api/deps.py +3 -5
- rem/api/main.py +26 -4
- rem/api/mcp_router/resources.py +15 -10
- rem/api/mcp_router/server.py +11 -3
- rem/api/mcp_router/tools.py +418 -4
- rem/api/middleware/tracking.py +5 -5
- rem/api/routers/admin.py +218 -1
- rem/api/routers/auth.py +349 -6
- rem/api/routers/chat/completions.py +255 -7
- rem/api/routers/chat/models.py +81 -7
- rem/api/routers/chat/otel_utils.py +33 -0
- rem/api/routers/chat/sse_events.py +17 -1
- rem/api/routers/chat/streaming.py +126 -19
- rem/api/routers/feedback.py +134 -14
- rem/api/routers/messages.py +24 -15
- rem/api/routers/query.py +6 -3
- rem/auth/__init__.py +13 -3
- rem/auth/jwt.py +352 -0
- rem/auth/middleware.py +115 -10
- rem/auth/providers/__init__.py +4 -1
- rem/auth/providers/email.py +215 -0
- rem/cli/commands/README.md +42 -0
- rem/cli/commands/cluster.py +617 -168
- rem/cli/commands/configure.py +4 -7
- rem/cli/commands/db.py +66 -22
- rem/cli/commands/experiments.py +468 -76
- rem/cli/commands/schema.py +6 -5
- rem/cli/commands/session.py +336 -0
- rem/cli/dreaming.py +2 -2
- rem/cli/main.py +2 -0
- rem/config.py +8 -1
- rem/models/core/experiment.py +58 -14
- rem/models/entities/__init__.py +4 -0
- rem/models/entities/ontology.py +1 -1
- rem/models/entities/ontology_config.py +1 -1
- rem/models/entities/subscriber.py +175 -0
- rem/models/entities/user.py +1 -0
- rem/schemas/agents/core/agent-builder.yaml +235 -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/services/__init__.py +3 -1
- rem/services/content/service.py +4 -3
- rem/services/email/__init__.py +10 -0
- rem/services/email/service.py +513 -0
- rem/services/email/templates.py +360 -0
- rem/services/phoenix/client.py +59 -18
- rem/services/postgres/README.md +38 -0
- rem/services/postgres/diff_service.py +127 -6
- rem/services/postgres/pydantic_to_sqlalchemy.py +45 -13
- rem/services/postgres/repository.py +5 -4
- rem/services/postgres/schema_generator.py +205 -4
- rem/services/session/compression.py +120 -50
- rem/services/session/reload.py +14 -7
- rem/services/user_service.py +41 -9
- rem/settings.py +442 -23
- rem/sql/migrations/001_install.sql +156 -0
- rem/sql/migrations/002_install_models.sql +1951 -88
- 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/files.py +157 -1
- rem/utils/schema_loader.py +139 -10
- rem/utils/sql_paths.py +146 -0
- 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.114.dist-info → remdb-0.3.172.dist-info}/METADATA +218 -180
- {remdb-0.3.114.dist-info → remdb-0.3.172.dist-info}/RECORD +83 -68
- {remdb-0.3.114.dist-info → remdb-0.3.172.dist-info}/WHEEL +0 -0
- {remdb-0.3.114.dist-info → remdb-0.3.172.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Email Templates.
|
|
3
|
+
|
|
4
|
+
HTML email templates for transactional emails.
|
|
5
|
+
Uses inline CSS for maximum email client compatibility.
|
|
6
|
+
|
|
7
|
+
Downstream apps can customize by:
|
|
8
|
+
1. Overriding COLORS dict
|
|
9
|
+
2. Setting custom LOGO_URL and TAGLINE
|
|
10
|
+
3. Creating custom templates using base_template()
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
from typing import Optional
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# Default colors - override in downstream apps
|
|
18
|
+
COLORS = {
|
|
19
|
+
"background": "#F5F5F5",
|
|
20
|
+
"foreground": "#333333",
|
|
21
|
+
"primary": "#4A90D9",
|
|
22
|
+
"accent": "#5CB85C",
|
|
23
|
+
"card": "#FFFFFF",
|
|
24
|
+
"muted": "#6b7280",
|
|
25
|
+
"border": "#E0E0E0",
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# Branding - override in downstream apps
|
|
29
|
+
LOGO_URL: str | None = None
|
|
30
|
+
APP_NAME = "REM"
|
|
31
|
+
TAGLINE = "Your AI-powered platform"
|
|
32
|
+
WEBSITE_URL = "https://rem.ai"
|
|
33
|
+
PRIVACY_URL = "https://rem.ai/privacy"
|
|
34
|
+
TERMS_URL = "https://rem.ai/terms"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass
|
|
38
|
+
class EmailTemplate:
|
|
39
|
+
"""Email template with subject and HTML body."""
|
|
40
|
+
|
|
41
|
+
subject: str
|
|
42
|
+
html_body: str
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def base_template(
|
|
46
|
+
content: str,
|
|
47
|
+
preheader: Optional[str] = None,
|
|
48
|
+
colors: dict | None = None,
|
|
49
|
+
logo_url: str | None = None,
|
|
50
|
+
app_name: str | None = None,
|
|
51
|
+
tagline: str | None = None,
|
|
52
|
+
website_url: str | None = None,
|
|
53
|
+
privacy_url: str | None = None,
|
|
54
|
+
terms_url: str | None = None,
|
|
55
|
+
) -> str:
|
|
56
|
+
"""
|
|
57
|
+
Base email template.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
content: The main content HTML
|
|
61
|
+
preheader: Optional preview text shown in email clients
|
|
62
|
+
colors: Color overrides (merges with COLORS)
|
|
63
|
+
logo_url: Logo image URL
|
|
64
|
+
app_name: Application name
|
|
65
|
+
tagline: Footer tagline
|
|
66
|
+
website_url: Main website URL
|
|
67
|
+
privacy_url: Privacy policy URL
|
|
68
|
+
terms_url: Terms of service URL
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
Complete HTML email
|
|
72
|
+
"""
|
|
73
|
+
# Merge colors
|
|
74
|
+
c = {**COLORS, **(colors or {})}
|
|
75
|
+
|
|
76
|
+
# Use provided values or module defaults
|
|
77
|
+
logo = logo_url or LOGO_URL
|
|
78
|
+
name = app_name or APP_NAME
|
|
79
|
+
tag = tagline or TAGLINE
|
|
80
|
+
web = website_url or WEBSITE_URL
|
|
81
|
+
privacy = privacy_url or PRIVACY_URL
|
|
82
|
+
terms = terms_url or TERMS_URL
|
|
83
|
+
|
|
84
|
+
preheader_html = ""
|
|
85
|
+
if preheader:
|
|
86
|
+
preheader_html = f'''
|
|
87
|
+
<div style="display: none; max-height: 0; overflow: hidden;">
|
|
88
|
+
{preheader}
|
|
89
|
+
</div>
|
|
90
|
+
'''
|
|
91
|
+
|
|
92
|
+
logo_html = ""
|
|
93
|
+
if logo:
|
|
94
|
+
logo_html = f'''
|
|
95
|
+
<img src="{logo}" alt="{name}" width="40" height="40" style="display: block; margin: 0 auto 16px auto; border-radius: 8px;">
|
|
96
|
+
'''
|
|
97
|
+
|
|
98
|
+
return f'''<!DOCTYPE html>
|
|
99
|
+
<html lang="en">
|
|
100
|
+
<head>
|
|
101
|
+
<meta charset="UTF-8">
|
|
102
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
103
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
104
|
+
<title>{name}</title>
|
|
105
|
+
<!--[if mso]>
|
|
106
|
+
<style type="text/css">
|
|
107
|
+
body, table, td {{font-family: Arial, sans-serif !important;}}
|
|
108
|
+
</style>
|
|
109
|
+
<![endif]-->
|
|
110
|
+
</head>
|
|
111
|
+
<body style="margin: 0; padding: 0; background-color: {c['background']}; font-family: 'Georgia', 'Times New Roman', serif;">
|
|
112
|
+
{preheader_html}
|
|
113
|
+
|
|
114
|
+
<!-- Email Container -->
|
|
115
|
+
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" style="background-color: {c['background']};">
|
|
116
|
+
<tr>
|
|
117
|
+
<td style="padding: 40px 20px;">
|
|
118
|
+
<!-- Inner Container -->
|
|
119
|
+
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" style="max-width: 560px; margin: 0 auto;">
|
|
120
|
+
|
|
121
|
+
<!-- Main Content Card -->
|
|
122
|
+
<tr>
|
|
123
|
+
<td style="background-color: {c['card']}; border-radius: 16px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);">
|
|
124
|
+
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
|
125
|
+
<tr>
|
|
126
|
+
<td style="padding: 40px;">
|
|
127
|
+
{content}
|
|
128
|
+
</td>
|
|
129
|
+
</tr>
|
|
130
|
+
</table>
|
|
131
|
+
</td>
|
|
132
|
+
</tr>
|
|
133
|
+
|
|
134
|
+
<!-- Footer -->
|
|
135
|
+
<tr>
|
|
136
|
+
<td style="padding: 32px 20px; text-align: center;">
|
|
137
|
+
{logo_html}
|
|
138
|
+
|
|
139
|
+
<!-- Tagline -->
|
|
140
|
+
<p style="margin: 0 0 8px 0; font-family: 'Georgia', serif; font-size: 14px; color: {c['muted']}; font-style: italic;">
|
|
141
|
+
{tag}
|
|
142
|
+
</p>
|
|
143
|
+
|
|
144
|
+
<!-- Footer Links -->
|
|
145
|
+
<p style="margin: 0; font-family: Arial, sans-serif; font-size: 12px; color: {c['muted']};">
|
|
146
|
+
<a href="{web}" style="color: {c['primary']}; text-decoration: none;">{name}</a>
|
|
147
|
+
•
|
|
148
|
+
<a href="{privacy}" style="color: {c['primary']}; text-decoration: none;">Privacy</a>
|
|
149
|
+
•
|
|
150
|
+
<a href="{terms}" style="color: {c['primary']}; text-decoration: none;">Terms</a>
|
|
151
|
+
</p>
|
|
152
|
+
|
|
153
|
+
<!-- Copyright -->
|
|
154
|
+
<p style="margin: 16px 0 0 0; font-family: Arial, sans-serif; font-size: 11px; color: {c['muted']};">
|
|
155
|
+
© 2025 {name}. All rights reserved.
|
|
156
|
+
</p>
|
|
157
|
+
</td>
|
|
158
|
+
</tr>
|
|
159
|
+
|
|
160
|
+
</table>
|
|
161
|
+
</td>
|
|
162
|
+
</tr>
|
|
163
|
+
</table>
|
|
164
|
+
</body>
|
|
165
|
+
</html>'''
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def login_code_template(
|
|
169
|
+
code: str,
|
|
170
|
+
email: str,
|
|
171
|
+
colors: dict | None = None,
|
|
172
|
+
app_name: str | None = None,
|
|
173
|
+
**kwargs,
|
|
174
|
+
) -> EmailTemplate:
|
|
175
|
+
"""
|
|
176
|
+
Generate a login code email template.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
code: The 6-digit login code
|
|
180
|
+
email: The recipient's email address
|
|
181
|
+
colors: Color overrides
|
|
182
|
+
app_name: Application name
|
|
183
|
+
**kwargs: Additional arguments passed to base_template
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
EmailTemplate with subject and HTML body
|
|
187
|
+
"""
|
|
188
|
+
c = {**COLORS, **(colors or {})}
|
|
189
|
+
name = app_name or APP_NAME
|
|
190
|
+
|
|
191
|
+
# Format code with spaces for readability (e.g., "123 456")
|
|
192
|
+
formatted_code = f"{code[:3]} {code[3:]}" if len(code) == 6 else code
|
|
193
|
+
|
|
194
|
+
content = f'''
|
|
195
|
+
<!-- Greeting -->
|
|
196
|
+
<h1 style="margin: 0 0 8px 0; font-family: 'Arial', sans-serif; font-size: 24px; font-weight: 600; color: {c['foreground']};">
|
|
197
|
+
Hi there!
|
|
198
|
+
</h1>
|
|
199
|
+
|
|
200
|
+
<p style="margin: 0 0 24px 0; font-family: 'Georgia', serif; font-size: 16px; line-height: 1.6; color: {c['foreground']};">
|
|
201
|
+
Here's your login code for {name}. Enter this code to securely access your account.
|
|
202
|
+
</p>
|
|
203
|
+
|
|
204
|
+
<!-- Code Box -->
|
|
205
|
+
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" style="margin: 0 0 24px 0;">
|
|
206
|
+
<tr>
|
|
207
|
+
<td align="center">
|
|
208
|
+
<div style="
|
|
209
|
+
display: inline-block;
|
|
210
|
+
padding: 20px 40px;
|
|
211
|
+
background-color: {c['background']};
|
|
212
|
+
border: 2px solid {c['border']};
|
|
213
|
+
border-radius: 12px;
|
|
214
|
+
">
|
|
215
|
+
<span style="
|
|
216
|
+
font-family: 'Courier New', monospace;
|
|
217
|
+
font-size: 32px;
|
|
218
|
+
font-weight: 700;
|
|
219
|
+
letter-spacing: 6px;
|
|
220
|
+
color: {c['foreground']};
|
|
221
|
+
">{formatted_code}</span>
|
|
222
|
+
</div>
|
|
223
|
+
</td>
|
|
224
|
+
</tr>
|
|
225
|
+
</table>
|
|
226
|
+
|
|
227
|
+
<!-- Instructions -->
|
|
228
|
+
<p style="margin: 0 0 8px 0; font-family: 'Georgia', serif; font-size: 14px; line-height: 1.6; color: {c['muted']};">
|
|
229
|
+
This code expires in <strong>10 minutes</strong>.
|
|
230
|
+
</p>
|
|
231
|
+
|
|
232
|
+
<p style="margin: 0 0 24px 0; font-family: 'Georgia', serif; font-size: 14px; line-height: 1.6; color: {c['muted']};">
|
|
233
|
+
If you didn't request this code, you can safely ignore this email.
|
|
234
|
+
</p>
|
|
235
|
+
|
|
236
|
+
<!-- Divider -->
|
|
237
|
+
<hr style="border: none; border-top: 1px solid {c['border']}; margin: 24px 0;">
|
|
238
|
+
|
|
239
|
+
<!-- Security Note -->
|
|
240
|
+
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
|
241
|
+
<tr>
|
|
242
|
+
<td style="padding: 16px; background-color: {c['background']}; border-radius: 8px;">
|
|
243
|
+
<p style="margin: 0; font-family: Arial, sans-serif; font-size: 12px; color: {c['muted']};">
|
|
244
|
+
<strong style="color: {c['primary']};">Security tip:</strong>
|
|
245
|
+
Never share your login code with anyone. {name} will never ask for your code via phone or text.
|
|
246
|
+
</p>
|
|
247
|
+
</td>
|
|
248
|
+
</tr>
|
|
249
|
+
</table>
|
|
250
|
+
'''
|
|
251
|
+
|
|
252
|
+
return EmailTemplate(
|
|
253
|
+
subject=f"Your {name} Login Code",
|
|
254
|
+
html_body=base_template(
|
|
255
|
+
content=content,
|
|
256
|
+
preheader=f"Your login code is {formatted_code}",
|
|
257
|
+
colors=colors,
|
|
258
|
+
app_name=app_name,
|
|
259
|
+
**kwargs,
|
|
260
|
+
),
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def welcome_template(
|
|
265
|
+
name: Optional[str] = None,
|
|
266
|
+
colors: dict | None = None,
|
|
267
|
+
app_name: str | None = None,
|
|
268
|
+
features: list[str] | None = None,
|
|
269
|
+
cta_url: str | None = None,
|
|
270
|
+
cta_text: str = "Get Started",
|
|
271
|
+
**kwargs,
|
|
272
|
+
) -> EmailTemplate:
|
|
273
|
+
"""
|
|
274
|
+
Generate a welcome email template for new users.
|
|
275
|
+
|
|
276
|
+
Args:
|
|
277
|
+
name: Optional user's name
|
|
278
|
+
colors: Color overrides
|
|
279
|
+
app_name: Application name
|
|
280
|
+
features: List of feature descriptions
|
|
281
|
+
cta_url: Call-to-action button URL
|
|
282
|
+
cta_text: Call-to-action button text
|
|
283
|
+
**kwargs: Additional arguments passed to base_template
|
|
284
|
+
|
|
285
|
+
Returns:
|
|
286
|
+
EmailTemplate with subject and HTML body
|
|
287
|
+
"""
|
|
288
|
+
c = {**COLORS, **(colors or {})}
|
|
289
|
+
app = app_name or APP_NAME
|
|
290
|
+
greeting = f"Hi {name}!" if name else "Welcome!"
|
|
291
|
+
url = cta_url or WEBSITE_URL
|
|
292
|
+
|
|
293
|
+
# Default features if not provided
|
|
294
|
+
default_features = [
|
|
295
|
+
"Access powerful AI capabilities",
|
|
296
|
+
"Store and organize your data",
|
|
297
|
+
"Collaborate with your team",
|
|
298
|
+
]
|
|
299
|
+
feature_list = features or default_features
|
|
300
|
+
|
|
301
|
+
features_html = "".join(f"<li>{f}</li>" for f in feature_list)
|
|
302
|
+
|
|
303
|
+
content = f'''
|
|
304
|
+
<!-- Greeting -->
|
|
305
|
+
<h1 style="margin: 0 0 8px 0; font-family: 'Arial', sans-serif; font-size: 24px; font-weight: 600; color: {c['foreground']};">
|
|
306
|
+
{greeting}
|
|
307
|
+
</h1>
|
|
308
|
+
|
|
309
|
+
<p style="margin: 0 0 24px 0; font-family: 'Georgia', serif; font-size: 16px; line-height: 1.6; color: {c['foreground']};">
|
|
310
|
+
Welcome to {app}! We're excited to have you on board.
|
|
311
|
+
</p>
|
|
312
|
+
|
|
313
|
+
<!-- Feature List -->
|
|
314
|
+
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" style="margin: 0 0 24px 0;">
|
|
315
|
+
<tr>
|
|
316
|
+
<td style="padding: 16px; background-color: {c['background']}; border-radius: 8px;">
|
|
317
|
+
<p style="margin: 0 0 12px 0; font-family: Arial, sans-serif; font-size: 14px; font-weight: 600; color: {c['primary']};">
|
|
318
|
+
What you can do with {app}:
|
|
319
|
+
</p>
|
|
320
|
+
<ul style="margin: 0; padding-left: 20px; font-family: 'Georgia', serif; font-size: 14px; line-height: 1.8; color: {c['foreground']};">
|
|
321
|
+
{features_html}
|
|
322
|
+
</ul>
|
|
323
|
+
</td>
|
|
324
|
+
</tr>
|
|
325
|
+
</table>
|
|
326
|
+
|
|
327
|
+
<!-- CTA Button -->
|
|
328
|
+
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" style="margin: 0 0 24px 0;">
|
|
329
|
+
<tr>
|
|
330
|
+
<td align="center">
|
|
331
|
+
<a href="{url}" style="
|
|
332
|
+
display: inline-block;
|
|
333
|
+
padding: 14px 32px;
|
|
334
|
+
background-color: {c['primary']};
|
|
335
|
+
color: #FFFFFF;
|
|
336
|
+
font-family: Arial, sans-serif;
|
|
337
|
+
font-size: 16px;
|
|
338
|
+
font-weight: 600;
|
|
339
|
+
text-decoration: none;
|
|
340
|
+
border-radius: 8px;
|
|
341
|
+
">{cta_text}</a>
|
|
342
|
+
</td>
|
|
343
|
+
</tr>
|
|
344
|
+
</table>
|
|
345
|
+
|
|
346
|
+
<p style="margin: 0; font-family: 'Georgia', serif; font-size: 14px; line-height: 1.6; color: {c['muted']}; text-align: center;">
|
|
347
|
+
We're glad you're here!
|
|
348
|
+
</p>
|
|
349
|
+
'''
|
|
350
|
+
|
|
351
|
+
return EmailTemplate(
|
|
352
|
+
subject=f"Welcome to {app}",
|
|
353
|
+
html_body=base_template(
|
|
354
|
+
content=content,
|
|
355
|
+
preheader=f"Welcome to {app}! Get started today.",
|
|
356
|
+
colors=colors,
|
|
357
|
+
app_name=app_name,
|
|
358
|
+
**kwargs,
|
|
359
|
+
),
|
|
360
|
+
)
|
rem/services/phoenix/client.py
CHANGED
|
@@ -793,40 +793,72 @@ class PhoenixClient:
|
|
|
793
793
|
score: float | None = None,
|
|
794
794
|
explanation: str | None = None,
|
|
795
795
|
metadata: dict[str, Any] | None = None,
|
|
796
|
+
trace_id: str | None = None,
|
|
796
797
|
) -> str | None:
|
|
797
|
-
"""Add feedback annotation to a span.
|
|
798
|
+
"""Add feedback annotation to a span via Phoenix REST API.
|
|
799
|
+
|
|
800
|
+
Uses direct HTTP POST to /v1/span_annotations for reliability
|
|
801
|
+
(Phoenix Python client API changes frequently).
|
|
798
802
|
|
|
799
803
|
Args:
|
|
800
|
-
span_id: Span ID to annotate
|
|
804
|
+
span_id: Span ID to annotate (hex string)
|
|
801
805
|
annotation_name: Name of the annotation (e.g., "correctness", "user_feedback")
|
|
802
806
|
annotator_kind: Type of annotator ("HUMAN", "LLM", "CODE")
|
|
803
807
|
label: Optional label (e.g., "correct", "incorrect", "helpful")
|
|
804
808
|
score: Optional numeric score (0.0-1.0)
|
|
805
809
|
explanation: Optional explanation text
|
|
806
810
|
metadata: Optional additional metadata dict
|
|
811
|
+
trace_id: Optional trace ID (used if span lookup needed)
|
|
807
812
|
|
|
808
813
|
Returns:
|
|
809
814
|
Annotation ID if successful, None otherwise
|
|
810
815
|
"""
|
|
816
|
+
import httpx
|
|
817
|
+
|
|
811
818
|
try:
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
819
|
+
# Build annotation payload for Phoenix REST API
|
|
820
|
+
annotation_data = {
|
|
821
|
+
"span_id": span_id,
|
|
822
|
+
"name": annotation_name,
|
|
823
|
+
"annotator_kind": annotator_kind,
|
|
824
|
+
"result": {
|
|
825
|
+
"label": label,
|
|
826
|
+
"score": score,
|
|
827
|
+
"explanation": explanation,
|
|
828
|
+
},
|
|
829
|
+
"metadata": metadata or {},
|
|
830
|
+
}
|
|
821
831
|
|
|
822
|
-
|
|
823
|
-
|
|
832
|
+
# Add trace_id if provided
|
|
833
|
+
if trace_id:
|
|
834
|
+
annotation_data["trace_id"] = trace_id
|
|
835
|
+
|
|
836
|
+
# POST to Phoenix REST API
|
|
837
|
+
annotations_endpoint = f"{self.config.base_url}/v1/span_annotations"
|
|
838
|
+
headers = {}
|
|
839
|
+
if self.config.api_key:
|
|
840
|
+
headers["Authorization"] = f"Bearer {self.config.api_key}"
|
|
841
|
+
|
|
842
|
+
with httpx.Client(timeout=5.0) as client:
|
|
843
|
+
response = client.post(
|
|
844
|
+
annotations_endpoint,
|
|
845
|
+
json={"data": [annotation_data]},
|
|
846
|
+
headers=headers,
|
|
847
|
+
)
|
|
848
|
+
response.raise_for_status()
|
|
824
849
|
|
|
825
|
-
|
|
850
|
+
logger.info(f"Added {annotator_kind} feedback to span {span_id}")
|
|
851
|
+
return span_id # Return span_id as annotation reference
|
|
826
852
|
|
|
853
|
+
except httpx.HTTPStatusError as e:
|
|
854
|
+
logger.error(
|
|
855
|
+
f"Failed to add span feedback (HTTP {e.response.status_code}): "
|
|
856
|
+
f"{e.response.text if hasattr(e, 'response') else 'N/A'}"
|
|
857
|
+
)
|
|
858
|
+
return None
|
|
827
859
|
except Exception as e:
|
|
828
860
|
logger.error(f"Failed to add span feedback: {e}")
|
|
829
|
-
|
|
861
|
+
return None
|
|
830
862
|
|
|
831
863
|
def sync_user_feedback(
|
|
832
864
|
self,
|
|
@@ -835,6 +867,7 @@ class PhoenixClient:
|
|
|
835
867
|
categories: list[str] | None = None,
|
|
836
868
|
comment: str | None = None,
|
|
837
869
|
feedback_id: str | None = None,
|
|
870
|
+
trace_id: str | None = None,
|
|
838
871
|
) -> str | None:
|
|
839
872
|
"""Sync user feedback to Phoenix as a span annotation.
|
|
840
873
|
|
|
@@ -847,6 +880,7 @@ class PhoenixClient:
|
|
|
847
880
|
categories: List of feedback categories
|
|
848
881
|
comment: Free-text comment
|
|
849
882
|
feedback_id: Optional REM feedback ID for reference
|
|
883
|
+
trace_id: Optional trace ID for the span
|
|
850
884
|
|
|
851
885
|
Returns:
|
|
852
886
|
Phoenix annotation ID if successful
|
|
@@ -860,12 +894,18 @@ class PhoenixClient:
|
|
|
860
894
|
... )
|
|
861
895
|
"""
|
|
862
896
|
# Convert rating to 0-1 score
|
|
897
|
+
# Rating scheme:
|
|
898
|
+
# -1 = thumbs down → score 0.0
|
|
899
|
+
# 1 = thumbs up → score 1.0
|
|
900
|
+
# 2-5 = star rating → normalized to 0-1 range
|
|
863
901
|
score = None
|
|
864
902
|
if rating is not None:
|
|
865
903
|
if rating == -1:
|
|
866
904
|
score = 0.0
|
|
867
|
-
elif
|
|
868
|
-
score =
|
|
905
|
+
elif rating == 1:
|
|
906
|
+
score = 1.0 # Thumbs up
|
|
907
|
+
elif 2 <= rating <= 5:
|
|
908
|
+
score = (rating - 1) / 4.0 # 2→0.25, 3→0.5, 4→0.75, 5→1.0
|
|
869
909
|
|
|
870
910
|
# Use primary category as label
|
|
871
911
|
label = categories[0] if categories else None
|
|
@@ -880,7 +920,7 @@ class PhoenixClient:
|
|
|
880
920
|
explanation = f"Categories: {cats_str}"
|
|
881
921
|
|
|
882
922
|
# Build metadata
|
|
883
|
-
metadata = {
|
|
923
|
+
metadata: dict[str, Any] = {
|
|
884
924
|
"rating": rating,
|
|
885
925
|
"categories": categories or [],
|
|
886
926
|
}
|
|
@@ -895,6 +935,7 @@ class PhoenixClient:
|
|
|
895
935
|
score=score,
|
|
896
936
|
explanation=explanation,
|
|
897
937
|
metadata=metadata,
|
|
938
|
+
trace_id=trace_id,
|
|
898
939
|
)
|
|
899
940
|
|
|
900
941
|
def get_span_annotations(
|
rem/services/postgres/README.md
CHANGED
|
@@ -688,6 +688,44 @@ from rem.api.main import app # Use REM's FastAPI app
|
|
|
688
688
|
# Or build your own app using rem.services
|
|
689
689
|
```
|
|
690
690
|
|
|
691
|
+
## Adding Models & Migrations
|
|
692
|
+
|
|
693
|
+
Quick workflow for adding new database models:
|
|
694
|
+
|
|
695
|
+
1. **Create a model** in `models/__init__.py` (or a submodule):
|
|
696
|
+
```python
|
|
697
|
+
import rem
|
|
698
|
+
from rem.models.core import CoreModel
|
|
699
|
+
|
|
700
|
+
@rem.register_model
|
|
701
|
+
class MyEntity(CoreModel):
|
|
702
|
+
name: str
|
|
703
|
+
description: str # Auto-embedded (common field name)
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
2. **Check for schema drift** - REM auto-detects `./models` directory:
|
|
707
|
+
```bash
|
|
708
|
+
rem db diff # Show pending changes (additive only)
|
|
709
|
+
rem db diff --strategy full # Include destructive changes
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
3. **Generate migration** (optional - for version-controlled SQL):
|
|
713
|
+
```bash
|
|
714
|
+
rem db diff --generate # Creates numbered .sql file
|
|
715
|
+
```
|
|
716
|
+
|
|
717
|
+
4. **Apply changes**:
|
|
718
|
+
```bash
|
|
719
|
+
rem db migrate # Apply all pending migrations
|
|
720
|
+
```
|
|
721
|
+
|
|
722
|
+
**Key points:**
|
|
723
|
+
- Models in `./models/` are auto-discovered (must have `__init__.py`)
|
|
724
|
+
- Or set `MODELS__IMPORT_MODULES=myapp.models` for custom paths
|
|
725
|
+
- `CoreModel` provides: `id`, `tenant_id`, `user_id`, `created_at`, `updated_at`, `deleted_at`, `graph_edges`, `metadata`, `tags`
|
|
726
|
+
- Fields named `content`, `description`, `summary`, `text`, `body`, `message`, `notes` get embeddings by default
|
|
727
|
+
- Use `Field(json_schema_extra={"embed": True})` to embed other fields
|
|
728
|
+
|
|
691
729
|
## Configuration
|
|
692
730
|
|
|
693
731
|
Environment variables:
|