remdb 0.3.157__py3-none-any.whl → 0.3.171__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.
@@ -2,65 +2,148 @@ type: object
2
2
  description: |
3
3
  # Agent Builder - Create Custom AI Agents Through Conversation
4
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.
5
+ You help users create custom AI agents for the REM platform through natural conversation.
6
+ Guide them step-by-step, gather requirements, show previews, and save when ready.
7
7
 
8
8
  ## Your Workflow
9
9
 
10
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
11
+ 2. **Define personality**: Help them choose tone and communication style
12
+ 3. **Set guardrails**: What should the agent NOT do?
13
+ 4. **Structure outputs**: Define what data the agent captures (optional)
14
+ 5. **Preview**: Show them what the agent will look like
15
+ 6. **Save**: Use `save_agent` tool to persist it
15
16
 
16
17
  ## Conversation Style
17
18
 
18
19
  Be friendly and helpful. Ask one or two questions at a time.
19
20
  Don't overwhelm with options - guide them step by step.
20
21
 
21
- ## Gathering Requirements
22
+ ## IMPORTANT: Tool Usage
23
+
24
+ - `save_agent` - Use ONLY in Step 6 when user approves the preview
25
+ - `get_agents_list` - Use if user asks to see existing agents as examples
26
+ - `get_agent_schema` - Use to load a specific agent (like "rem") as reference
27
+
28
+ DO NOT loop on tools. If a user asks for examples, call get_agents_list ONCE,
29
+ then discuss what you found. This is a conversational workflow.
30
+
31
+ ## Step 1: Identity & Purpose
22
32
 
23
33
  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?
34
+ - What should this agent help with? (primary purpose)
35
+ - What would you like to call it? (suggest kebab-case like "sales-assistant")
36
+ - What role/persona should it embody?
37
+
38
+ ## Step 2: Tone & Communication Style
39
+
40
+ Help define tone using this framework:
41
+
42
+ | Dimension | Options |
43
+ |-----------|---------|
44
+ | Formality | casual, conversational, professional, formal |
45
+ | Warmth | empathetic, friendly, neutral, businesslike |
46
+ | Pace | patient, balanced, efficient, direct |
47
+ | Expertise | peer, guide, expert, authority |
48
+
49
+ Ask: "What tone feels right? For example, should it be friendly and casual, or more professional?"
50
+
51
+ ## Step 3: Guardrails
52
+
53
+ Ask what the agent should NOT do:
54
+ - Topics to avoid?
55
+ - Actions it shouldn't take?
56
+ - Boundaries to respect?
28
57
 
29
- ## Preview Format
58
+ Example guardrails:
59
+ - "Never provide medical/legal/financial advice"
60
+ - "Don't make promises about timelines"
61
+ - "Always recommend consulting a professional for serious issues"
30
62
 
31
- Before saving, show a preview using markdown:
63
+ ## Step 4: Structured Outputs (Optional)
64
+
65
+ Most agents just need an `answer` field. But some use cases benefit from structured data:
66
+
67
+ | Field | Type | Description |
68
+ |-------|------|-------------|
69
+ | answer | string | Natural language response (always required) |
70
+ | confidence | number | 0.0-1.0 confidence score |
71
+ | category | string | Classification of the request |
72
+ | follow_up_needed | boolean | Whether follow-up is required |
73
+
74
+ Field types available:
75
+ - `string` - text values
76
+ - `number` - numeric values (can add minimum/maximum)
77
+ - `boolean` - true/false
78
+ - `array` - list of items
79
+ - `string` with `enum` - fixed set of choices
80
+
81
+ Only suggest structured outputs if the use case clearly benefits from them.
82
+
83
+ ## Step 5: Preview
84
+
85
+ Before saving, show a preview:
32
86
 
33
87
  ```
34
88
  ## Agent Preview: {name}
35
89
 
36
- **Personality:**
37
- {brief description of tone and approach}
90
+ **Purpose:** {brief description}
91
+
92
+ **Personality:** {tone and approach}
38
93
 
39
94
  **System Prompt:**
40
95
  {the actual prompt that will guide the agent}
41
96
 
42
- **Structured Fields:** (if any)
97
+ **Guardrails:**
98
+ - {guardrail 1}
99
+ - {guardrail 2}
100
+
101
+ **Structured Fields:** (if any beyond answer)
43
102
  | Field | Type | Description |
44
103
  |-------|------|-------------|
45
104
  | answer | string | Response to user |
46
- | ... | ... | ... |
47
105
  ```
48
106
 
49
- Ask: "Does this look good? I can save it now or we can adjust anything."
107
+ Ask: "Does this look good? I can save it now or adjust anything."
50
108
 
51
- ## Saving the Agent
109
+ ## Step 6: Save the Agent
52
110
 
53
111
  When the user approves, call `save_agent` with:
54
112
  - `name`: kebab-case name (e.g., "customer-support-bot")
55
- - `description`: The full system prompt
113
+ - `description`: The full system prompt (this is the most important part!)
56
114
  - `properties`: Structured output fields (optional, defaults to just "answer")
57
115
  - `required`: Which fields are required (optional, defaults to ["answer"])
58
116
  - `tags`: Relevant tags for organization
59
117
 
60
- ## Default Tools
118
+ ## Writing Great System Prompts
119
+
120
+ The `description` field IS the system prompt. Make it:
121
+
122
+ 1. **Clear about role**: "You are a [role] that helps users [do what]"
123
+ 2. **Specific about behavior**: How should it respond? What's the communication style?
124
+ 3. **Explicit about guardrails**: What should it NOT do?
125
+ 4. **Structured with sections**: Use markdown headers for organization
126
+
127
+ Example structure:
128
+ ```
129
+ # [Agent Name]
61
130
 
62
- All agents automatically get `search_rem` and `register_metadata` tools.
63
- You don't need to specify these.
131
+ You are a [role description]. Your purpose is to [main goal].
132
+
133
+ ## Communication Style
134
+ [Tone, approach, language preferences]
135
+
136
+ ## Core Responsibilities
137
+ - [Responsibility 1]
138
+ - [Responsibility 2]
139
+
140
+ ## Guardrails
141
+ - Never [thing to avoid]
142
+ - Always [thing to ensure]
143
+
144
+ ## How to Respond
145
+ [Guidelines for responses]
146
+ ```
64
147
 
65
148
  ## Example Conversation
66
149
 
@@ -68,47 +151,62 @@ description: |
68
151
 
69
152
  You: "Great idea! Let me help you create an email writing assistant.
70
153
 
71
- What tone should it have? For example:
154
+ First, what tone should it have? For example:
72
155
  - Professional and formal
73
156
  - Friendly but polished
74
157
  - Direct and concise"
75
158
 
76
159
  User: "Friendly but polished"
77
160
 
78
- You: "Perfect! Should it just write emails, or also help with things like:
161
+ You: "Perfect! Should it focus just on writing emails, or also help with:
79
162
  - Suggesting subject lines
80
163
  - Adjusting tone for different recipients
81
164
  - Summarizing long email threads"
82
165
 
83
166
  User: "All of those would be helpful"
84
167
 
85
- You: "Let me show you a preview:
168
+ You: "Great! Any things it should NOT do? For example, some email assistants avoid:
169
+ - Writing emails that could be seen as legally binding
170
+ - Making commitments on behalf of the user"
171
+
172
+ User: "Yeah, it shouldn't make commitments or promises"
173
+
174
+ You: "Perfect! Here's a preview:
86
175
 
87
176
  ## Agent Preview: email-assistant
88
177
 
89
- **Personality:** Friendly, professional, helpful writing partner
178
+ **Purpose:** Help users write polished, professional emails
179
+
180
+ **Personality:** Friendly, helpful, professional writing partner
90
181
 
91
182
  **System Prompt:**
183
+ # Email Writing Assistant
184
+
92
185
  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
186
+ professional emails while maintaining a warm, approachable tone.
187
+
188
+ ## What You Can Do
189
+ - Write new emails from scratch based on user's intent
95
190
  - Suggest compelling subject lines
96
- - Adjust tone for different audiences
191
+ - Adjust tone for different audiences (colleagues, executives, clients)
97
192
  - Summarize long email threads
193
+ - Proofread and improve existing drafts
98
194
 
99
- Always ask clarifying questions if the request is unclear.
195
+ ## Communication Style
196
+ Be helpful and collaborative. Suggest improvements but respect the user's voice.
197
+ Ask clarifying questions when the request is ambiguous.
100
198
 
101
- **Structured Fields:**
102
- | Field | Type | Description |
103
- |-------|------|-------------|
104
- | answer | string | Your response or the drafted email |
199
+ ## Guardrails
200
+ - Never write emails that make commitments or promises on behalf of the user
201
+ - Don't write anything that could be legally binding
202
+ - Always let the user review before sending
105
203
 
106
204
  Does this look good? I can save it now or adjust anything."
107
205
 
108
206
  User: "Looks great, save it!"
109
207
 
110
208
  You: *calls save_agent tool*
111
- "Done! Your email-assistant is ready. Use `/custom-agent email-assistant` to start chatting with it."
209
+ "Done! Your email-assistant is ready to use."
112
210
 
113
211
  properties:
114
212
  answer:
@@ -121,14 +219,17 @@ required:
121
219
  json_schema_extra:
122
220
  kind: agent
123
221
  name: agent-builder
124
- version: "1.0.0"
222
+ version: "1.2.0"
125
223
  tags:
126
224
  - meta
127
225
  - builder
226
+ structured_output: false # Stream text responses, don't return JSON
227
+ mcp_servers: [] # Disable default MCP tools to prevent search_rem looping
228
+ resources:
229
+ - uri: rem://agents
230
+ description: "List all available agent schemas with descriptions"
231
+ - uri: rem://agents/{agent_name}
232
+ description: "Load a specific agent schema by name (e.g., 'rem', 'siggy')"
128
233
  tools:
129
234
  - 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"
235
+ description: "Save the agent schema. Only call when user approves the preview in Step 6."
@@ -26,6 +26,9 @@ logger = logging.getLogger(__name__)
26
26
  class EmailService:
27
27
  """Service for sending transactional emails and passwordless login."""
28
28
 
29
+ # Store last login code for mock mode testing
30
+ _last_login_code: dict[str, str] = {}
31
+
29
32
  def __init__(
30
33
  self,
31
34
  smtp_host: str | None = None,
@@ -35,6 +38,7 @@ class EmailService:
35
38
  app_password: str | None = None,
36
39
  use_tls: bool = True,
37
40
  login_code_expiry_minutes: int = 10,
41
+ mock_mode: bool | None = None,
38
42
  ):
39
43
  """
40
44
  Initialize EmailService.
@@ -50,6 +54,7 @@ class EmailService:
50
54
  app_password: SMTP app password
51
55
  use_tls: Use TLS encryption
52
56
  login_code_expiry_minutes: Login code expiry in minutes
57
+ mock_mode: If True, don't send real emails (log code instead)
53
58
  """
54
59
  # Import settings lazily to avoid circular imports
55
60
  from ...settings import settings
@@ -65,16 +70,31 @@ class EmailService:
65
70
  or settings.email.login_code_expiry_minutes
66
71
  )
67
72
 
68
- if not self._app_password:
73
+ # Mock mode: enabled via setting or if not configured
74
+ if mock_mode is not None:
75
+ self._mock_mode = mock_mode
76
+ elif hasattr(settings.email, 'mock_mode'):
77
+ self._mock_mode = settings.email.mock_mode
78
+ else:
79
+ # Auto-enable mock mode if email is not configured
80
+ self._mock_mode = not self._app_password
81
+
82
+ if not self._app_password and not self._mock_mode:
69
83
  logger.warning(
70
84
  "Email app password not configured. "
71
85
  "Set EMAIL__APP_PASSWORD to enable email sending."
72
86
  )
73
87
 
88
+ if self._mock_mode:
89
+ logger.info(
90
+ "Email service running in MOCK MODE. "
91
+ "Codes will be logged but not emailed."
92
+ )
93
+
74
94
  @property
75
95
  def is_configured(self) -> bool:
76
- """Check if email service is properly configured."""
77
- return bool(self._sender_email and self._app_password)
96
+ """Check if email service is properly configured (or in mock mode)."""
97
+ return self._mock_mode or bool(self._sender_email and self._app_password)
78
98
 
79
99
  def _create_smtp_connection(self) -> smtplib.SMTP:
80
100
  """Create and authenticate SMTP connection."""
@@ -107,6 +127,13 @@ class EmailService:
107
127
  logger.error("Email service not configured. Cannot send email.")
108
128
  return False
109
129
 
130
+ # Mock mode - log but don't send
131
+ if self._mock_mode:
132
+ logger.info(
133
+ f"[MOCK EMAIL] To: {to_email}, Subject: {template.subject}"
134
+ )
135
+ return True
136
+
110
137
  try:
111
138
  # Create message
112
139
  msg = MIMEMultipart("alternative")
@@ -152,13 +179,29 @@ class EmailService:
152
179
  """
153
180
  return "".join(random.choices(string.digits, k=6))
154
181
 
182
+ @classmethod
183
+ def get_mock_code(cls, email: str) -> str | None:
184
+ """
185
+ Get the last login code sent to an email (mock mode only).
186
+
187
+ For testing purposes - retrieves the code that would have been
188
+ sent in mock mode.
189
+
190
+ Args:
191
+ email: Email address to look up
192
+
193
+ Returns:
194
+ The login code or None if not found
195
+ """
196
+ return cls._last_login_code.get(email.lower().strip())
197
+
155
198
  @staticmethod
156
199
  def generate_user_id_from_email(email: str) -> str:
157
200
  """
158
201
  Generate a deterministic UUID from email address.
159
202
 
160
- Uses UUID v5 with DNS namespace for consistency.
161
- Same email always produces same UUID.
203
+ Uses the centralized email_to_user_id() for consistency.
204
+ Same email always produces same UUID (bijection).
162
205
 
163
206
  Args:
164
207
  email: Email address
@@ -166,7 +209,8 @@ class EmailService:
166
209
  Returns:
167
210
  UUID string
168
211
  """
169
- return str(uuid.uuid5(uuid.NAMESPACE_DNS, email.lower().strip()))
212
+ from rem.utils.user_id import email_to_user_id
213
+ return email_to_user_id(email)
170
214
 
171
215
  async def send_login_code(
172
216
  self,
@@ -254,6 +298,15 @@ class EmailService:
254
298
  if sent:
255
299
  result["success"] = True
256
300
  result["code_sent"] = True
301
+
302
+ # Store code for mock mode retrieval
303
+ if self._mock_mode:
304
+ EmailService._last_login_code[email] = code
305
+ logger.info(
306
+ f"[MOCK MODE] Login code for {email}: {code} "
307
+ f"(expires at {expires_at.isoformat()})"
308
+ )
309
+
257
310
  logger.info(
258
311
  f"Login code sent to {email}, "
259
312
  f"user_id={user_id}, expires at {expires_at.isoformat()}"
@@ -341,7 +394,8 @@ class EmailService:
341
394
  new_user = User(
342
395
  id=uuid.UUID(user_id),
343
396
  tenant_id=tenant_id,
344
- name=email.split("@")[0], # Default name from email
397
+ user_id=user_id, # UUID5 hash of email (same as id)
398
+ name=email, # Full email as entity_key for LOOKUP
345
399
  email=email,
346
400
  role=user_role,
347
401
  metadata=login_metadata,
@@ -141,13 +141,13 @@ class Repository(Generic[T]):
141
141
  # Return single item or list to match input type
142
142
  return records_list[0] if is_single else records_list
143
143
 
144
- async def get_by_id(self, record_id: str, tenant_id: str) -> T | None:
144
+ async def get_by_id(self, record_id: str, tenant_id: str | None = None) -> T | None:
145
145
  """
146
146
  Get a single record by ID.
147
147
 
148
148
  Args:
149
149
  record_id: Record identifier
150
- tenant_id: Tenant identifier for multi-tenancy isolation
150
+ tenant_id: Optional tenant identifier (deprecated, not used for filtering)
151
151
 
152
152
  Returns:
153
153
  Model instance or None if not found
@@ -164,13 +164,14 @@ class Repository(Generic[T]):
164
164
  if not self.db.pool:
165
165
  raise RuntimeError("Failed to establish database connection")
166
166
 
167
+ # Note: tenant_id filtering removed - use user_id for access control instead
167
168
  query = f"""
168
169
  SELECT * FROM {self.table_name}
169
- WHERE id = $1 AND tenant_id = $2 AND deleted_at IS NULL
170
+ WHERE id = $1 AND deleted_at IS NULL
170
171
  """
171
172
 
172
173
  async with self.db.pool.acquire() as conn:
173
- row = await conn.fetchrow(query, record_id, tenant_id)
174
+ row = await conn.fetchrow(query, record_id)
174
175
 
175
176
  if not row:
176
177
  return None
@@ -4,7 +4,8 @@ User Service - User account management.
4
4
  Handles user creation, profile updates, and session linking.
5
5
  """
6
6
 
7
- from datetime import datetime
7
+ from rem.utils.date_utils import utc_now
8
+ from rem.utils.user_id import email_to_user_id
8
9
  from typing import Optional
9
10
 
10
11
  from loguru import logger
@@ -51,28 +52,59 @@ class UserService:
51
52
  updated = True
52
53
 
53
54
  if updated:
54
- user.updated_at = datetime.utcnow()
55
+ user.updated_at = utc_now()
55
56
  await self.repo.upsert(user)
56
57
 
57
58
  return user
58
59
 
59
60
  # Create new user
61
+ # id and user_id = UUID5 hash of email (deterministic bijection)
62
+ # name = email (entity_key for LOOKUP by email in KV store)
63
+ hashed_id = email_to_user_id(email)
60
64
  user = User(
65
+ id=hashed_id, # Database id = hash of email
61
66
  tenant_id=tenant_id,
62
- user_id=email, # Use email as user_id for now? Or UUID?
63
- # The User model has 'user_id' field but also 'id' UUID.
64
- # Usually user_id is the external ID or email.
65
- name=name,
67
+ user_id=hashed_id, # user_id = hash of email (same as id)
68
+ name=email, # Email as entity_key for REM LOOKUP
66
69
  email=email,
67
70
  tier=UserTier.FREE,
68
- created_at=datetime.utcnow(),
69
- updated_at=datetime.utcnow(),
71
+ created_at=utc_now(),
72
+ updated_at=utc_now(),
70
73
  metadata={"avatar_url": avatar_url} if avatar_url else {},
71
74
  )
72
75
  await self.repo.upsert(user)
73
76
  logger.info(f"Created new user: {email}")
74
77
  return user
75
78
 
79
+ async def get_user_by_id(self, user_id: str) -> Optional[User]:
80
+ """
81
+ Get a user by their UUID.
82
+
83
+ Args:
84
+ user_id: The user's UUID
85
+
86
+ Returns:
87
+ User if found, None otherwise
88
+ """
89
+ try:
90
+ return await self.repo.get_by_id(user_id)
91
+ except Exception as e:
92
+ logger.warning(f"Could not find user by id {user_id}: {e}")
93
+ return None
94
+
95
+ async def get_user_by_email(self, email: str) -> Optional[User]:
96
+ """
97
+ Get a user by their email address.
98
+
99
+ Args:
100
+ email: The user's email
101
+
102
+ Returns:
103
+ User if found, None otherwise
104
+ """
105
+ users = await self.repo.find(filters={"email": email}, limit=1)
106
+ return users[0] if users else None
107
+
76
108
  async def link_anonymous_session(self, user: User, anon_id: str) -> None:
77
109
  """
78
110
  Link an anonymous session ID to a user account.
@@ -88,7 +120,7 @@ class UserService:
88
120
 
89
121
  # Add to list
90
122
  user.anonymous_ids.append(anon_id)
91
- user.updated_at = datetime.utcnow()
123
+ user.updated_at = utc_now()
92
124
 
93
125
  # Save
94
126
  await self.repo.upsert(user)
rem/settings.py CHANGED
@@ -1028,7 +1028,7 @@ class ChatSettings(BaseSettings):
1028
1028
  - Prevents context window bloat while maintaining conversation continuity
1029
1029
 
1030
1030
  User Context (on-demand by default):
1031
- - Agent system prompt includes: "User ID: {user_id}. To load user profile: Use REM LOOKUP users/{user_id}"
1031
+ - Agent system prompt includes: "User: {email}. To load user profile: Use REM LOOKUP \"{email}\""
1032
1032
  - Agent decides whether to load profile based on query
1033
1033
  - More efficient for queries that don't need personalization
1034
1034
 
@@ -1114,6 +1114,14 @@ class APISettings(BaseSettings):
1114
1114
  ),
1115
1115
  )
1116
1116
 
1117
+ rate_limit_enabled: bool = Field(
1118
+ default=True,
1119
+ description=(
1120
+ "Enable rate limiting for API endpoints. "
1121
+ "Set to false to disable rate limiting entirely (useful for development)."
1122
+ ),
1123
+ )
1124
+
1117
1125
 
1118
1126
  class ModelsSettings(BaseSettings):
1119
1127
  """
@@ -657,7 +657,7 @@ BEGIN
657
657
  MIN(msg_counts.first_msg)::TIMESTAMP AS first_message_at,
658
658
  MAX(msg_counts.last_msg)::TIMESTAMP AS last_message_at
659
659
  FROM shared_sessions ss
660
- LEFT JOIN users u ON u.user_id = ss.owner_user_id AND u.tenant_id = ss.tenant_id
660
+ LEFT JOIN users u ON u.id::text = ss.owner_user_id AND u.tenant_id = ss.tenant_id
661
661
  LEFT JOIN (
662
662
  SELECT
663
663
  m.session_id,
@@ -132,13 +132,51 @@ def _load_schema_from_database(schema_name: str, user_id: str) -> dict[str, Any]
132
132
  # Check if we're already in an async context
133
133
  try:
134
134
  loop = asyncio.get_running_loop()
135
- # We're in an async context - can't use asyncio.run()
136
- # This shouldn't happen in normal usage since load_agent_schema is called from sync contexts
137
- logger.warning(
138
- "Database schema lookup called from async context. "
139
- "This may cause issues. Consider using async version of load_agent_schema."
140
- )
141
- return None
135
+ # We're in an async context - use thread executor to run async code
136
+ import concurrent.futures
137
+
138
+ async def _async_lookup():
139
+ """Async helper to query database."""
140
+ from rem.services.postgres import get_postgres_service
141
+
142
+ db = get_postgres_service()
143
+ if not db:
144
+ logger.debug("PostgreSQL service not available for schema lookup")
145
+ return None
146
+
147
+ try:
148
+ await db.connect()
149
+
150
+ query = """
151
+ SELECT spec FROM schemas
152
+ WHERE LOWER(name) = LOWER($1)
153
+ AND (user_id = $2 OR user_id = 'system' OR user_id IS NULL)
154
+ LIMIT 1
155
+ """
156
+ logger.debug(f"Executing schema lookup: name={schema_name}, user_id={user_id}")
157
+
158
+ row = await db.fetchrow(query, schema_name, user_id)
159
+
160
+ if row:
161
+ spec = row.get("spec")
162
+ if spec and isinstance(spec, dict):
163
+ logger.debug(f"Found schema in database: {schema_name}")
164
+ return spec
165
+
166
+ logger.debug(f"Schema not found in database: {schema_name}")
167
+ return None
168
+
169
+ except Exception as e:
170
+ logger.debug(f"Database schema lookup error: {e}")
171
+ return None
172
+ finally:
173
+ await db.disconnect()
174
+
175
+ # Run in thread pool to avoid blocking the event loop
176
+ with concurrent.futures.ThreadPoolExecutor() as pool:
177
+ future = pool.submit(asyncio.run, _async_lookup())
178
+ return future.result(timeout=10)
179
+
142
180
  except RuntimeError:
143
181
  # Not in async context - safe to use asyncio.run()
144
182
  pass
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: remdb
3
- Version: 0.3.157
3
+ Version: 0.3.171
4
4
  Summary: Resources Entities Moments - Bio-inspired memory system for agentic AI workloads
5
5
  Project-URL: Homepage, https://github.com/Percolation-Labs/reminiscent
6
6
  Project-URL: Documentation, https://github.com/Percolation-Labs/reminiscent/blob/main/README.md