remdb 0.3.242__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/__init__.py +129 -0
- rem/agentic/README.md +760 -0
- rem/agentic/__init__.py +54 -0
- rem/agentic/agents/README.md +155 -0
- rem/agentic/agents/__init__.py +38 -0
- rem/agentic/agents/agent_manager.py +311 -0
- rem/agentic/agents/sse_simulator.py +502 -0
- rem/agentic/context.py +425 -0
- rem/agentic/context_builder.py +360 -0
- rem/agentic/llm_provider_models.py +301 -0
- rem/agentic/mcp/__init__.py +0 -0
- rem/agentic/mcp/tool_wrapper.py +273 -0
- rem/agentic/otel/__init__.py +5 -0
- rem/agentic/otel/setup.py +240 -0
- rem/agentic/providers/phoenix.py +926 -0
- rem/agentic/providers/pydantic_ai.py +854 -0
- rem/agentic/query.py +117 -0
- rem/agentic/query_helper.py +89 -0
- rem/agentic/schema.py +737 -0
- rem/agentic/serialization.py +245 -0
- rem/agentic/tools/__init__.py +5 -0
- rem/agentic/tools/rem_tools.py +242 -0
- rem/api/README.md +657 -0
- rem/api/deps.py +253 -0
- rem/api/main.py +460 -0
- rem/api/mcp_router/prompts.py +182 -0
- rem/api/mcp_router/resources.py +820 -0
- rem/api/mcp_router/server.py +243 -0
- rem/api/mcp_router/tools.py +1605 -0
- rem/api/middleware/tracking.py +172 -0
- rem/api/routers/admin.py +520 -0
- rem/api/routers/auth.py +898 -0
- rem/api/routers/chat/__init__.py +5 -0
- rem/api/routers/chat/child_streaming.py +394 -0
- rem/api/routers/chat/completions.py +702 -0
- rem/api/routers/chat/json_utils.py +76 -0
- rem/api/routers/chat/models.py +202 -0
- rem/api/routers/chat/otel_utils.py +33 -0
- rem/api/routers/chat/sse_events.py +546 -0
- rem/api/routers/chat/streaming.py +950 -0
- rem/api/routers/chat/streaming_utils.py +327 -0
- rem/api/routers/common.py +18 -0
- rem/api/routers/dev.py +87 -0
- rem/api/routers/feedback.py +276 -0
- rem/api/routers/messages.py +620 -0
- rem/api/routers/models.py +86 -0
- rem/api/routers/query.py +362 -0
- rem/api/routers/shared_sessions.py +422 -0
- rem/auth/README.md +258 -0
- rem/auth/__init__.py +36 -0
- rem/auth/jwt.py +367 -0
- rem/auth/middleware.py +318 -0
- rem/auth/providers/__init__.py +16 -0
- rem/auth/providers/base.py +376 -0
- rem/auth/providers/email.py +215 -0
- rem/auth/providers/google.py +163 -0
- rem/auth/providers/microsoft.py +237 -0
- rem/cli/README.md +517 -0
- rem/cli/__init__.py +8 -0
- rem/cli/commands/README.md +299 -0
- rem/cli/commands/__init__.py +3 -0
- rem/cli/commands/ask.py +549 -0
- rem/cli/commands/cluster.py +1808 -0
- rem/cli/commands/configure.py +495 -0
- rem/cli/commands/db.py +828 -0
- rem/cli/commands/dreaming.py +324 -0
- rem/cli/commands/experiments.py +1698 -0
- rem/cli/commands/mcp.py +66 -0
- rem/cli/commands/process.py +388 -0
- rem/cli/commands/query.py +109 -0
- rem/cli/commands/scaffold.py +47 -0
- rem/cli/commands/schema.py +230 -0
- rem/cli/commands/serve.py +106 -0
- rem/cli/commands/session.py +453 -0
- rem/cli/dreaming.py +363 -0
- rem/cli/main.py +123 -0
- rem/config.py +244 -0
- rem/mcp_server.py +41 -0
- rem/models/core/__init__.py +49 -0
- rem/models/core/core_model.py +70 -0
- rem/models/core/engram.py +333 -0
- rem/models/core/experiment.py +672 -0
- rem/models/core/inline_edge.py +132 -0
- rem/models/core/rem_query.py +246 -0
- rem/models/entities/__init__.py +68 -0
- rem/models/entities/domain_resource.py +38 -0
- rem/models/entities/feedback.py +123 -0
- rem/models/entities/file.py +57 -0
- rem/models/entities/image_resource.py +88 -0
- rem/models/entities/message.py +64 -0
- rem/models/entities/moment.py +123 -0
- rem/models/entities/ontology.py +181 -0
- rem/models/entities/ontology_config.py +131 -0
- rem/models/entities/resource.py +95 -0
- rem/models/entities/schema.py +87 -0
- rem/models/entities/session.py +84 -0
- rem/models/entities/shared_session.py +180 -0
- rem/models/entities/subscriber.py +175 -0
- rem/models/entities/user.py +93 -0
- rem/py.typed +0 -0
- rem/registry.py +373 -0
- rem/schemas/README.md +507 -0
- rem/schemas/__init__.py +6 -0
- rem/schemas/agents/README.md +92 -0
- rem/schemas/agents/core/agent-builder.yaml +235 -0
- rem/schemas/agents/core/moment-builder.yaml +178 -0
- rem/schemas/agents/core/rem-query-agent.yaml +226 -0
- rem/schemas/agents/core/resource-affinity-assessor.yaml +99 -0
- rem/schemas/agents/core/simple-assistant.yaml +19 -0
- rem/schemas/agents/core/user-profile-builder.yaml +163 -0
- rem/schemas/agents/examples/contract-analyzer.yaml +317 -0
- rem/schemas/agents/examples/contract-extractor.yaml +134 -0
- rem/schemas/agents/examples/cv-parser.yaml +263 -0
- rem/schemas/agents/examples/hello-world.yaml +37 -0
- rem/schemas/agents/examples/query.yaml +54 -0
- rem/schemas/agents/examples/simple.yaml +21 -0
- rem/schemas/agents/examples/test.yaml +29 -0
- rem/schemas/agents/rem.yaml +132 -0
- rem/schemas/evaluators/hello-world/default.yaml +77 -0
- rem/schemas/evaluators/rem/faithfulness.yaml +219 -0
- rem/schemas/evaluators/rem/lookup-correctness.yaml +182 -0
- rem/schemas/evaluators/rem/retrieval-precision.yaml +199 -0
- rem/schemas/evaluators/rem/retrieval-recall.yaml +211 -0
- rem/schemas/evaluators/rem/search-correctness.yaml +192 -0
- rem/services/__init__.py +18 -0
- rem/services/audio/INTEGRATION.md +308 -0
- rem/services/audio/README.md +376 -0
- rem/services/audio/__init__.py +15 -0
- rem/services/audio/chunker.py +354 -0
- rem/services/audio/transcriber.py +259 -0
- rem/services/content/README.md +1269 -0
- rem/services/content/__init__.py +5 -0
- rem/services/content/providers.py +760 -0
- rem/services/content/service.py +762 -0
- rem/services/dreaming/README.md +230 -0
- rem/services/dreaming/__init__.py +53 -0
- rem/services/dreaming/affinity_service.py +322 -0
- rem/services/dreaming/moment_service.py +251 -0
- rem/services/dreaming/ontology_service.py +54 -0
- rem/services/dreaming/user_model_service.py +297 -0
- rem/services/dreaming/utils.py +39 -0
- rem/services/email/__init__.py +10 -0
- rem/services/email/service.py +522 -0
- rem/services/email/templates.py +360 -0
- rem/services/embeddings/__init__.py +11 -0
- rem/services/embeddings/api.py +127 -0
- rem/services/embeddings/worker.py +435 -0
- rem/services/fs/README.md +662 -0
- rem/services/fs/__init__.py +62 -0
- rem/services/fs/examples.py +206 -0
- rem/services/fs/examples_paths.py +204 -0
- rem/services/fs/git_provider.py +935 -0
- rem/services/fs/local_provider.py +760 -0
- rem/services/fs/parsing-hooks-examples.md +172 -0
- rem/services/fs/paths.py +276 -0
- rem/services/fs/provider.py +460 -0
- rem/services/fs/s3_provider.py +1042 -0
- rem/services/fs/service.py +186 -0
- rem/services/git/README.md +1075 -0
- rem/services/git/__init__.py +17 -0
- rem/services/git/service.py +469 -0
- rem/services/phoenix/EXPERIMENT_DESIGN.md +1146 -0
- rem/services/phoenix/README.md +453 -0
- rem/services/phoenix/__init__.py +46 -0
- rem/services/phoenix/client.py +960 -0
- rem/services/phoenix/config.py +88 -0
- rem/services/phoenix/prompt_labels.py +477 -0
- rem/services/postgres/README.md +757 -0
- rem/services/postgres/__init__.py +49 -0
- rem/services/postgres/diff_service.py +599 -0
- rem/services/postgres/migration_service.py +427 -0
- rem/services/postgres/programmable_diff_service.py +635 -0
- rem/services/postgres/pydantic_to_sqlalchemy.py +562 -0
- rem/services/postgres/register_type.py +353 -0
- rem/services/postgres/repository.py +481 -0
- rem/services/postgres/schema_generator.py +661 -0
- rem/services/postgres/service.py +802 -0
- rem/services/postgres/sql_builder.py +355 -0
- rem/services/rate_limit.py +113 -0
- rem/services/rem/README.md +318 -0
- rem/services/rem/__init__.py +23 -0
- rem/services/rem/exceptions.py +71 -0
- rem/services/rem/executor.py +293 -0
- rem/services/rem/parser.py +180 -0
- rem/services/rem/queries.py +196 -0
- rem/services/rem/query.py +371 -0
- rem/services/rem/service.py +608 -0
- rem/services/session/README.md +374 -0
- rem/services/session/__init__.py +13 -0
- rem/services/session/compression.py +488 -0
- rem/services/session/pydantic_messages.py +310 -0
- rem/services/session/reload.py +85 -0
- rem/services/user_service.py +130 -0
- rem/settings.py +1877 -0
- rem/sql/background_indexes.sql +52 -0
- rem/sql/migrations/001_install.sql +983 -0
- rem/sql/migrations/002_install_models.sql +3157 -0
- rem/sql/migrations/003_optional_extensions.sql +326 -0
- rem/sql/migrations/004_cache_system.sql +282 -0
- rem/sql/migrations/005_schema_update.sql +145 -0
- rem/sql/migrations/migrate_session_id_to_uuid.sql +45 -0
- rem/utils/AGENTIC_CHUNKING.md +597 -0
- rem/utils/README.md +628 -0
- rem/utils/__init__.py +61 -0
- rem/utils/agentic_chunking.py +622 -0
- rem/utils/batch_ops.py +343 -0
- rem/utils/chunking.py +108 -0
- rem/utils/clip_embeddings.py +276 -0
- rem/utils/constants.py +97 -0
- rem/utils/date_utils.py +228 -0
- rem/utils/dict_utils.py +98 -0
- rem/utils/embeddings.py +436 -0
- rem/utils/examples/embeddings_example.py +305 -0
- rem/utils/examples/sql_types_example.py +202 -0
- rem/utils/files.py +323 -0
- rem/utils/markdown.py +16 -0
- rem/utils/mime_types.py +158 -0
- rem/utils/model_helpers.py +492 -0
- rem/utils/schema_loader.py +649 -0
- rem/utils/sql_paths.py +146 -0
- rem/utils/sql_types.py +350 -0
- rem/utils/user_id.py +81 -0
- rem/utils/vision.py +325 -0
- rem/workers/README.md +506 -0
- rem/workers/__init__.py +7 -0
- rem/workers/db_listener.py +579 -0
- rem/workers/db_maintainer.py +74 -0
- rem/workers/dreaming.py +502 -0
- rem/workers/engram_processor.py +312 -0
- rem/workers/sqs_file_processor.py +193 -0
- rem/workers/unlogged_maintainer.py +463 -0
- remdb-0.3.242.dist-info/METADATA +1632 -0
- remdb-0.3.242.dist-info/RECORD +235 -0
- remdb-0.3.242.dist-info/WHEEL +4 -0
- remdb-0.3.242.dist-info/entry_points.txt +2 -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
|
+
)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Embeddings service for background embedding generation."""
|
|
2
|
+
|
|
3
|
+
from .api import generate_embedding, generate_embedding_async
|
|
4
|
+
from .worker import EmbeddingTask, EmbeddingWorker
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"EmbeddingTask",
|
|
8
|
+
"EmbeddingWorker",
|
|
9
|
+
"generate_embedding",
|
|
10
|
+
"generate_embedding_async",
|
|
11
|
+
]
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Embedding API utilities for generating embeddings from text.
|
|
3
|
+
|
|
4
|
+
Provides synchronous and async wrappers for embedding generation using
|
|
5
|
+
raw HTTP requests (no OpenAI SDK dependency).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Optional, cast
|
|
9
|
+
|
|
10
|
+
import httpx
|
|
11
|
+
import requests
|
|
12
|
+
from loguru import logger
|
|
13
|
+
|
|
14
|
+
from rem.utils.constants import DEFAULT_EMBEDDING_DIMS, HTTP_TIMEOUT_DEFAULT
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _get_openai_api_key() -> Optional[str]:
|
|
18
|
+
"""Get OpenAI API key from settings."""
|
|
19
|
+
from rem.settings import settings
|
|
20
|
+
return settings.llm.openai_api_key
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def generate_embedding(
|
|
24
|
+
text: str,
|
|
25
|
+
model: str = "text-embedding-3-small",
|
|
26
|
+
provider: str = "openai",
|
|
27
|
+
api_key: Optional[str] = None,
|
|
28
|
+
) -> list[float]:
|
|
29
|
+
"""
|
|
30
|
+
Generate embedding for a single text string using requests.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
text: Text to embed
|
|
34
|
+
model: Model name (default: text-embedding-3-small)
|
|
35
|
+
provider: Provider name (default: openai)
|
|
36
|
+
api_key: API key (defaults to settings.llm.openai_api_key)
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
Embedding vector (1536 dimensions for text-embedding-3-small)
|
|
40
|
+
"""
|
|
41
|
+
if provider == "openai":
|
|
42
|
+
api_key = api_key or _get_openai_api_key()
|
|
43
|
+
if not api_key:
|
|
44
|
+
logger.warning("No OpenAI API key - returning zero vector")
|
|
45
|
+
return [0.0] * DEFAULT_EMBEDDING_DIMS
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
logger.debug(f"Generating OpenAI embedding for text using {model}")
|
|
49
|
+
|
|
50
|
+
response = requests.post(
|
|
51
|
+
"https://api.openai.com/v1/embeddings",
|
|
52
|
+
headers={
|
|
53
|
+
"Authorization": f"Bearer {api_key}",
|
|
54
|
+
"Content-Type": "application/json",
|
|
55
|
+
},
|
|
56
|
+
json={"input": [text], "model": model},
|
|
57
|
+
timeout=HTTP_TIMEOUT_DEFAULT,
|
|
58
|
+
)
|
|
59
|
+
response.raise_for_status()
|
|
60
|
+
|
|
61
|
+
data = response.json()
|
|
62
|
+
embedding = data["data"][0]["embedding"]
|
|
63
|
+
logger.debug(f"Successfully generated embedding (dimension: {len(embedding)})")
|
|
64
|
+
return cast(list[float], embedding)
|
|
65
|
+
|
|
66
|
+
except Exception as e:
|
|
67
|
+
logger.error(f"Failed to generate embedding from OpenAI: {e}", exc_info=True)
|
|
68
|
+
return [0.0] * DEFAULT_EMBEDDING_DIMS
|
|
69
|
+
|
|
70
|
+
else:
|
|
71
|
+
logger.warning(f"Unsupported provider '{provider}' - returning zero vector")
|
|
72
|
+
return [0.0] * DEFAULT_EMBEDDING_DIMS
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
async def generate_embedding_async(
|
|
76
|
+
text: str,
|
|
77
|
+
model: str = "text-embedding-3-small",
|
|
78
|
+
provider: str = "openai",
|
|
79
|
+
api_key: Optional[str] = None,
|
|
80
|
+
) -> list[float]:
|
|
81
|
+
"""
|
|
82
|
+
Generate embedding for a single text string (async version) using httpx.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
text: Text to embed
|
|
86
|
+
model: Model name (default: text-embedding-3-small)
|
|
87
|
+
provider: Provider name (default: openai)
|
|
88
|
+
api_key: API key (defaults to settings.llm.openai_api_key)
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Embedding vector (1536 dimensions for text-embedding-3-small)
|
|
92
|
+
"""
|
|
93
|
+
if provider == "openai":
|
|
94
|
+
api_key = api_key or _get_openai_api_key()
|
|
95
|
+
if not api_key:
|
|
96
|
+
logger.warning("No OpenAI API key - returning zero vector")
|
|
97
|
+
return [0.0] * DEFAULT_EMBEDDING_DIMS
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
logger.debug(f"Generating OpenAI embedding for text using {model}")
|
|
101
|
+
|
|
102
|
+
async with httpx.AsyncClient() as client:
|
|
103
|
+
response = await client.post(
|
|
104
|
+
"https://api.openai.com/v1/embeddings",
|
|
105
|
+
headers={
|
|
106
|
+
"Authorization": f"Bearer {api_key}",
|
|
107
|
+
"Content-Type": "application/json",
|
|
108
|
+
},
|
|
109
|
+
json={"input": [text], "model": model},
|
|
110
|
+
timeout=HTTP_TIMEOUT_DEFAULT,
|
|
111
|
+
)
|
|
112
|
+
response.raise_for_status()
|
|
113
|
+
|
|
114
|
+
data = response.json()
|
|
115
|
+
embedding = data["data"][0]["embedding"]
|
|
116
|
+
logger.debug(
|
|
117
|
+
f"Successfully generated embedding (dimension: {len(embedding)})"
|
|
118
|
+
)
|
|
119
|
+
return cast(list[float], embedding)
|
|
120
|
+
|
|
121
|
+
except Exception as e:
|
|
122
|
+
logger.error(f"Failed to generate embedding from OpenAI: {e}", exc_info=True)
|
|
123
|
+
return [0.0] * DEFAULT_EMBEDDING_DIMS
|
|
124
|
+
|
|
125
|
+
else:
|
|
126
|
+
logger.warning(f"Unsupported provider '{provider}' - returning zero vector")
|
|
127
|
+
return [0.0] * DEFAULT_EMBEDDING_DIMS
|