remdb 0.2.6__py3-none-any.whl → 0.3.118__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.

Files changed (104) hide show
  1. rem/__init__.py +129 -2
  2. rem/agentic/README.md +76 -0
  3. rem/agentic/__init__.py +15 -0
  4. rem/agentic/agents/__init__.py +16 -2
  5. rem/agentic/agents/sse_simulator.py +500 -0
  6. rem/agentic/context.py +28 -22
  7. rem/agentic/llm_provider_models.py +301 -0
  8. rem/agentic/mcp/tool_wrapper.py +29 -3
  9. rem/agentic/otel/setup.py +92 -4
  10. rem/agentic/providers/phoenix.py +32 -43
  11. rem/agentic/providers/pydantic_ai.py +168 -24
  12. rem/agentic/schema.py +358 -21
  13. rem/agentic/tools/rem_tools.py +3 -3
  14. rem/api/README.md +238 -1
  15. rem/api/deps.py +255 -0
  16. rem/api/main.py +154 -37
  17. rem/api/mcp_router/resources.py +1 -1
  18. rem/api/mcp_router/server.py +26 -5
  19. rem/api/mcp_router/tools.py +454 -7
  20. rem/api/middleware/tracking.py +172 -0
  21. rem/api/routers/admin.py +494 -0
  22. rem/api/routers/auth.py +124 -0
  23. rem/api/routers/chat/completions.py +152 -16
  24. rem/api/routers/chat/models.py +7 -3
  25. rem/api/routers/chat/sse_events.py +526 -0
  26. rem/api/routers/chat/streaming.py +608 -45
  27. rem/api/routers/dev.py +81 -0
  28. rem/api/routers/feedback.py +148 -0
  29. rem/api/routers/messages.py +473 -0
  30. rem/api/routers/models.py +78 -0
  31. rem/api/routers/query.py +360 -0
  32. rem/api/routers/shared_sessions.py +406 -0
  33. rem/auth/middleware.py +126 -27
  34. rem/cli/commands/README.md +237 -64
  35. rem/cli/commands/ask.py +15 -11
  36. rem/cli/commands/cluster.py +1300 -0
  37. rem/cli/commands/configure.py +170 -97
  38. rem/cli/commands/db.py +396 -139
  39. rem/cli/commands/experiments.py +278 -96
  40. rem/cli/commands/process.py +22 -15
  41. rem/cli/commands/scaffold.py +47 -0
  42. rem/cli/commands/schema.py +97 -50
  43. rem/cli/main.py +37 -6
  44. rem/config.py +2 -2
  45. rem/models/core/core_model.py +7 -1
  46. rem/models/core/rem_query.py +5 -2
  47. rem/models/entities/__init__.py +21 -0
  48. rem/models/entities/domain_resource.py +38 -0
  49. rem/models/entities/feedback.py +123 -0
  50. rem/models/entities/message.py +30 -1
  51. rem/models/entities/session.py +83 -0
  52. rem/models/entities/shared_session.py +180 -0
  53. rem/models/entities/user.py +10 -3
  54. rem/registry.py +373 -0
  55. rem/schemas/agents/rem.yaml +7 -3
  56. rem/services/content/providers.py +94 -140
  57. rem/services/content/service.py +115 -24
  58. rem/services/dreaming/affinity_service.py +2 -16
  59. rem/services/dreaming/moment_service.py +2 -15
  60. rem/services/embeddings/api.py +24 -17
  61. rem/services/embeddings/worker.py +16 -16
  62. rem/services/phoenix/EXPERIMENT_DESIGN.md +3 -3
  63. rem/services/phoenix/client.py +252 -19
  64. rem/services/postgres/README.md +159 -15
  65. rem/services/postgres/__init__.py +2 -1
  66. rem/services/postgres/diff_service.py +531 -0
  67. rem/services/postgres/pydantic_to_sqlalchemy.py +427 -129
  68. rem/services/postgres/repository.py +132 -0
  69. rem/services/postgres/schema_generator.py +291 -9
  70. rem/services/postgres/service.py +6 -6
  71. rem/services/rate_limit.py +113 -0
  72. rem/services/rem/README.md +14 -0
  73. rem/services/rem/parser.py +44 -9
  74. rem/services/rem/service.py +36 -2
  75. rem/services/session/compression.py +17 -1
  76. rem/services/session/reload.py +1 -1
  77. rem/services/user_service.py +98 -0
  78. rem/settings.py +169 -22
  79. rem/sql/background_indexes.sql +21 -16
  80. rem/sql/migrations/001_install.sql +387 -54
  81. rem/sql/migrations/002_install_models.sql +2320 -393
  82. rem/sql/migrations/003_optional_extensions.sql +326 -0
  83. rem/sql/migrations/004_cache_system.sql +548 -0
  84. rem/utils/__init__.py +18 -0
  85. rem/utils/constants.py +97 -0
  86. rem/utils/date_utils.py +228 -0
  87. rem/utils/embeddings.py +17 -4
  88. rem/utils/files.py +167 -0
  89. rem/utils/mime_types.py +158 -0
  90. rem/utils/model_helpers.py +156 -1
  91. rem/utils/schema_loader.py +284 -21
  92. rem/utils/sql_paths.py +146 -0
  93. rem/utils/sql_types.py +3 -1
  94. rem/utils/vision.py +9 -14
  95. rem/workers/README.md +14 -14
  96. rem/workers/__init__.py +2 -1
  97. rem/workers/db_maintainer.py +74 -0
  98. rem/workers/unlogged_maintainer.py +463 -0
  99. {remdb-0.2.6.dist-info → remdb-0.3.118.dist-info}/METADATA +598 -171
  100. {remdb-0.2.6.dist-info → remdb-0.3.118.dist-info}/RECORD +102 -73
  101. {remdb-0.2.6.dist-info → remdb-0.3.118.dist-info}/WHEEL +1 -1
  102. rem/sql/002_install_models.sql +0 -1068
  103. rem/sql/install_models.sql +0 -1038
  104. {remdb-0.2.6.dist-info → remdb-0.3.118.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,526 @@
1
+ """
2
+ SSE Event Types for Rich Streaming Responses.
3
+
4
+ This module defines custom Server-Sent Events (SSE) event types that extend
5
+ beyond simple text streaming.
6
+
7
+ ## SSE Protocol
8
+
9
+ Text content uses **OpenAI-compatible format** (plain `data:` prefix):
10
+ ```
11
+ data: {"id":"chatcmpl-...","choices":[{"delta":{"content":"Hello"}}]}
12
+ ```
13
+
14
+ Custom events use **named event format** (`event:` prefix):
15
+ ```
16
+ event: reasoning
17
+ data: {"type": "reasoning", "content": "Analyzing...", "step": 1}
18
+ ```
19
+
20
+ ## Event Types
21
+
22
+ | Event | Format | Purpose |
23
+ |-------|--------|---------|
24
+ | (text) | `data:` (OpenAI) | Content chunks - main response |
25
+ | reasoning | `event:` | Model thinking/chain-of-thought |
26
+ | progress | `event:` | Step indicators |
27
+ | tool_call | `event:` | Tool invocation start/complete |
28
+ | metadata | `event:` | System metadata (confidence, sources) |
29
+ | action_request | `event:` | UI solicitation (buttons, forms) |
30
+ | error | `event:` | Error notifications |
31
+ | done | `event:` | Stream completion marker |
32
+
33
+ ## Action Schema Design
34
+
35
+ - Inspired by Microsoft Adaptive Cards (https://adaptivecards.io/)
36
+ - JSON Schema-based UI element definitions
37
+ - Cross-platform compatibility for React, mobile, etc.
38
+
39
+ ## References
40
+
41
+ - MDN SSE: https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events
42
+ - Adaptive Cards: https://adaptivecards.io/explorer/
43
+ - Model Context Protocol: https://modelcontextprotocol.io/specification/2025-06-18
44
+ """
45
+
46
+ from enum import Enum
47
+ from typing import Any, Literal
48
+ from pydantic import BaseModel, Field
49
+
50
+
51
+ class SSEEventType(str, Enum):
52
+ """SSE event types for streaming responses."""
53
+
54
+ TEXT_DELTA = "text_delta" # Standard text chunk
55
+ REASONING = "reasoning" # Model thinking/reasoning
56
+ ACTION_REQUEST = "action_request" # UI action solicitation
57
+ METADATA = "metadata" # System metadata
58
+ PROGRESS = "progress" # Progress indicator
59
+ TOOL_CALL = "tool_call" # Tool invocation
60
+ ERROR = "error" # Error notification
61
+ DONE = "done" # Stream complete
62
+
63
+
64
+ # =============================================================================
65
+ # Action Solicitation Schema (Adaptive Cards-inspired)
66
+ # =============================================================================
67
+
68
+ class ActionStyle(str, Enum):
69
+ """Visual style for action buttons."""
70
+
71
+ DEFAULT = "default"
72
+ PRIMARY = "primary"
73
+ SECONDARY = "secondary"
74
+ DESTRUCTIVE = "destructive"
75
+ POSITIVE = "positive"
76
+
77
+
78
+ class ActionSubmit(BaseModel):
79
+ """
80
+ Submit action - triggers callback to server with payload.
81
+
82
+ Inspired by Adaptive Cards Action.Submit:
83
+ https://adaptivecards.io/explorer/Action.Submit.html
84
+ """
85
+
86
+ type: Literal["Action.Submit"] = "Action.Submit"
87
+ id: str = Field(description="Unique action identifier")
88
+ title: str = Field(description="Button label text")
89
+ style: ActionStyle = Field(
90
+ default=ActionStyle.DEFAULT,
91
+ description="Visual style"
92
+ )
93
+ data: dict[str, Any] = Field(
94
+ default_factory=dict,
95
+ description="Payload sent to server when action is triggered"
96
+ )
97
+ tooltip: str | None = Field(
98
+ default=None,
99
+ description="Tooltip text on hover"
100
+ )
101
+ icon_url: str | None = Field(
102
+ default=None,
103
+ description="Optional icon URL"
104
+ )
105
+
106
+
107
+ class ActionOpenUrl(BaseModel):
108
+ """
109
+ Open URL action - navigates to external URL.
110
+
111
+ Inspired by Adaptive Cards Action.OpenUrl:
112
+ https://adaptivecards.io/explorer/Action.OpenUrl.html
113
+ """
114
+
115
+ type: Literal["Action.OpenUrl"] = "Action.OpenUrl"
116
+ id: str = Field(description="Unique action identifier")
117
+ title: str = Field(description="Button label text")
118
+ url: str = Field(description="URL to open")
119
+ style: ActionStyle = Field(default=ActionStyle.DEFAULT)
120
+ tooltip: str | None = None
121
+
122
+
123
+ class ActionShowCard(BaseModel):
124
+ """
125
+ Show card action - reveals nested content inline.
126
+
127
+ Inspired by Adaptive Cards Action.ShowCard:
128
+ https://adaptivecards.io/explorer/Action.ShowCard.html
129
+ """
130
+
131
+ type: Literal["Action.ShowCard"] = "Action.ShowCard"
132
+ id: str = Field(description="Unique action identifier")
133
+ title: str = Field(description="Button label text")
134
+ card: dict[str, Any] = Field(
135
+ description="Nested card content to reveal (Adaptive Card JSON)"
136
+ )
137
+ style: ActionStyle = Field(default=ActionStyle.DEFAULT)
138
+
139
+
140
+ # Union type for all action types
141
+ ActionType = ActionSubmit | ActionOpenUrl | ActionShowCard
142
+
143
+
144
+ class InputText(BaseModel):
145
+ """Text input field for action cards."""
146
+
147
+ type: Literal["Input.Text"] = "Input.Text"
148
+ id: str = Field(description="Input field identifier (used in submit payload)")
149
+ label: str | None = Field(default=None, description="Input label")
150
+ placeholder: str | None = Field(default=None, description="Placeholder text")
151
+ is_required: bool = Field(default=False, description="Whether input is required")
152
+ is_multiline: bool = Field(default=False, description="Multi-line text area")
153
+ max_length: int | None = Field(default=None, description="Maximum character length")
154
+ value: str | None = Field(default=None, description="Default value")
155
+
156
+
157
+ class InputChoiceSet(BaseModel):
158
+ """Choice/select input for action cards."""
159
+
160
+ type: Literal["Input.ChoiceSet"] = "Input.ChoiceSet"
161
+ id: str = Field(description="Input field identifier")
162
+ label: str | None = None
163
+ choices: list[dict[str, str]] = Field(
164
+ description="List of {title, value} choice objects"
165
+ )
166
+ is_required: bool = False
167
+ is_multi_select: bool = Field(default=False, description="Allow multiple selections")
168
+ value: str | None = Field(default=None, description="Default selected value")
169
+
170
+
171
+ class InputToggle(BaseModel):
172
+ """Toggle/checkbox input for action cards."""
173
+
174
+ type: Literal["Input.Toggle"] = "Input.Toggle"
175
+ id: str = Field(description="Input field identifier")
176
+ title: str = Field(description="Toggle label text")
177
+ value: str = Field(default="false", description="Current value ('true'/'false')")
178
+ value_on: str = Field(default="true", description="Value when toggled on")
179
+ value_off: str = Field(default="false", description="Value when toggled off")
180
+
181
+
182
+ # Union type for all input types
183
+ InputType = InputText | InputChoiceSet | InputToggle
184
+
185
+
186
+ class ActionDisplayStyle(str, Enum):
187
+ """How to display the action request in the UI."""
188
+
189
+ INLINE = "inline" # Rendered inline after message content
190
+ FLOATING = "floating" # Floating panel/overlay
191
+ MODAL = "modal" # Modal dialog
192
+
193
+
194
+ class ActionRequestCard(BaseModel):
195
+ """
196
+ Action solicitation card - requests user input or action.
197
+
198
+ This is the main payload for action_request SSE events.
199
+ Uses Adaptive Cards-inspired schema for cross-platform UI compatibility.
200
+
201
+ Example use cases:
202
+ - Confirm/cancel dialogs
203
+ - Form inputs (name, email, etc.)
204
+ - Multi-choice selections
205
+ - Quick reply buttons
206
+ - Feedback collection (thumbs up/down)
207
+
208
+ Example:
209
+ ```json
210
+ {
211
+ "id": "confirm-delete-123",
212
+ "prompt": "Are you sure you want to delete this item?",
213
+ "display_style": "modal",
214
+ "actions": [
215
+ {
216
+ "type": "Action.Submit",
217
+ "id": "confirm",
218
+ "title": "Delete",
219
+ "style": "destructive",
220
+ "data": {"action": "delete", "item_id": "123"}
221
+ },
222
+ {
223
+ "type": "Action.Submit",
224
+ "id": "cancel",
225
+ "title": "Cancel",
226
+ "style": "secondary",
227
+ "data": {"action": "cancel"}
228
+ }
229
+ ],
230
+ "timeout_ms": 30000
231
+ }
232
+ ```
233
+ """
234
+
235
+ id: str = Field(description="Unique card identifier for response correlation")
236
+ prompt: str = Field(description="Prompt text explaining what action is requested")
237
+ display_style: ActionDisplayStyle = Field(
238
+ default=ActionDisplayStyle.INLINE,
239
+ description="How to display in the UI"
240
+ )
241
+ actions: list[ActionType] = Field(
242
+ default_factory=list,
243
+ description="Available actions (buttons)"
244
+ )
245
+ inputs: list[InputType] = Field(
246
+ default_factory=list,
247
+ description="Input fields for data collection"
248
+ )
249
+ timeout_ms: int | None = Field(
250
+ default=None,
251
+ description="Auto-dismiss timeout in milliseconds"
252
+ )
253
+ fallback_text: str | None = Field(
254
+ default=None,
255
+ description="Text to show if card rendering fails"
256
+ )
257
+
258
+
259
+ # =============================================================================
260
+ # SSE Event Payloads
261
+ # =============================================================================
262
+
263
+ class TextDeltaEvent(BaseModel):
264
+ """Text content delta event (OpenAI-compatible)."""
265
+
266
+ type: Literal["text_delta"] = "text_delta"
267
+ content: str = Field(description="Text content chunk")
268
+
269
+
270
+ class ReasoningEvent(BaseModel):
271
+ """
272
+ Reasoning/thinking event.
273
+
274
+ Used to stream model's chain-of-thought reasoning separate from
275
+ the main response content. UI can display this in a collapsible
276
+ "thinking" section.
277
+ """
278
+
279
+ type: Literal["reasoning"] = "reasoning"
280
+ content: str = Field(description="Reasoning text chunk")
281
+ step: int | None = Field(
282
+ default=None,
283
+ description="Reasoning step number (for multi-step reasoning)"
284
+ )
285
+
286
+
287
+ class ActionRequestEvent(BaseModel):
288
+ """
289
+ Action request event - solicits user action.
290
+
291
+ Sent when the agent needs user input or confirmation.
292
+ """
293
+
294
+ type: Literal["action_request"] = "action_request"
295
+ card: ActionRequestCard = Field(description="Action card definition")
296
+
297
+
298
+ class MetadataEvent(BaseModel):
299
+ """
300
+ Metadata event - system information (often hidden from user).
301
+
302
+ Used for confidence scores, sources, model info, message IDs, etc.
303
+ """
304
+
305
+ type: Literal["metadata"] = "metadata"
306
+
307
+ # Message correlation IDs
308
+ message_id: str | None = Field(
309
+ default=None,
310
+ description="Database ID of the assistant message being streamed"
311
+ )
312
+ in_reply_to: str | None = Field(
313
+ default=None,
314
+ description="Database ID of the user message this is responding to"
315
+ )
316
+ session_id: str | None = Field(
317
+ default=None,
318
+ description="Session ID for this conversation"
319
+ )
320
+
321
+ # Agent info
322
+ agent_schema: str | None = Field(
323
+ default=None,
324
+ description="Name of the agent schema used for this response (e.g., 'rem', 'Siggy')"
325
+ )
326
+
327
+ # Quality indicators
328
+ confidence: float | None = Field(
329
+ default=None, ge=0, le=1,
330
+ description="Confidence score (0-1)"
331
+ )
332
+ sources: list[str] | None = Field(
333
+ default=None,
334
+ description="Referenced sources/citations"
335
+ )
336
+
337
+ # Model info
338
+ model_version: str | None = Field(
339
+ default=None,
340
+ description="Model version used"
341
+ )
342
+
343
+ # Performance metrics
344
+ latency_ms: int | None = Field(
345
+ default=None,
346
+ description="Response latency in milliseconds"
347
+ )
348
+ token_count: int | None = Field(
349
+ default=None,
350
+ description="Token count for this response"
351
+ )
352
+
353
+ # System flags
354
+ flags: list[str] | None = Field(
355
+ default=None,
356
+ description="System flags (e.g., 'uncertain', 'needs_review')"
357
+ )
358
+ hidden: bool = Field(
359
+ default=False,
360
+ description="If true, should not be displayed to user"
361
+ )
362
+ extra: dict[str, Any] | None = Field(
363
+ default=None,
364
+ description="Additional metadata"
365
+ )
366
+
367
+
368
+ class ProgressEvent(BaseModel):
369
+ """Progress indicator event."""
370
+
371
+ type: Literal["progress"] = "progress"
372
+ step: int = Field(description="Current step number")
373
+ total_steps: int = Field(description="Total number of steps")
374
+ label: str = Field(description="Step description")
375
+ status: Literal["pending", "in_progress", "completed", "failed"] = Field(
376
+ description="Step status"
377
+ )
378
+
379
+
380
+ class ToolCallEvent(BaseModel):
381
+ """Tool invocation event."""
382
+
383
+ type: Literal["tool_call"] = "tool_call"
384
+ tool_name: str = Field(description="Name of tool being called")
385
+ tool_id: str | None = Field(
386
+ default=None,
387
+ description="Unique call identifier"
388
+ )
389
+ status: Literal["started", "completed", "failed"] = Field(
390
+ description="Tool call status"
391
+ )
392
+ arguments: dict[str, Any] | None = Field(
393
+ default=None,
394
+ description="Tool arguments (for 'started' status)"
395
+ )
396
+ result: str | None = Field(
397
+ default=None,
398
+ description="Tool result summary (for 'completed' status)"
399
+ )
400
+ error: str | None = Field(
401
+ default=None,
402
+ description="Error message (for 'failed' status)"
403
+ )
404
+
405
+
406
+ class ErrorEvent(BaseModel):
407
+ """Error notification event."""
408
+
409
+ type: Literal["error"] = "error"
410
+ code: str = Field(description="Error code")
411
+ message: str = Field(description="Human-readable error message")
412
+ details: dict[str, Any] | None = Field(
413
+ default=None,
414
+ description="Additional error details"
415
+ )
416
+ recoverable: bool = Field(
417
+ default=True,
418
+ description="Whether error is recoverable"
419
+ )
420
+
421
+
422
+ class DoneEvent(BaseModel):
423
+ """Stream completion event."""
424
+
425
+ type: Literal["done"] = "done"
426
+ reason: Literal["stop", "length", "error", "cancelled"] = Field(
427
+ default="stop",
428
+ description="Completion reason"
429
+ )
430
+
431
+
432
+ # Union type for all SSE events
433
+ SSEEvent = (
434
+ TextDeltaEvent
435
+ | ReasoningEvent
436
+ | ActionRequestEvent
437
+ | MetadataEvent
438
+ | ProgressEvent
439
+ | ToolCallEvent
440
+ | ErrorEvent
441
+ | DoneEvent
442
+ )
443
+
444
+
445
+ # =============================================================================
446
+ # SSE Formatting Helpers
447
+ # =============================================================================
448
+
449
+ def format_sse_event(event: SSEEvent) -> str:
450
+ """
451
+ Format an SSE event for transmission.
452
+
453
+ Standard data: format for text_delta (OpenAI compatibility).
454
+ Named event: format for other event types.
455
+
456
+ Args:
457
+ event: SSE event to format
458
+
459
+ Returns:
460
+ Formatted SSE string ready for transmission
461
+
462
+ Example:
463
+ >>> event = ReasoningEvent(content="Analyzing...")
464
+ >>> format_sse_event(event)
465
+ 'event: reasoning\\ndata: {"type": "reasoning", "content": "Analyzing..."}\\n\\n'
466
+ """
467
+ import json
468
+
469
+ event_json = event.model_dump_json()
470
+
471
+ # TextDeltaEvent uses standard data: format for OpenAI compatibility
472
+ if isinstance(event, TextDeltaEvent):
473
+ return f"data: {event_json}\n\n"
474
+
475
+ # DoneEvent uses special marker
476
+ if isinstance(event, DoneEvent):
477
+ return f"event: done\ndata: {event_json}\n\n"
478
+
479
+ # All other events use named event format
480
+ event_type = event.type
481
+ return f"event: {event_type}\ndata: {event_json}\n\n"
482
+
483
+
484
+ def format_openai_sse_chunk(
485
+ request_id: str,
486
+ created: int,
487
+ model: str,
488
+ content: str | None = None,
489
+ role: str | None = None,
490
+ finish_reason: str | None = None,
491
+ ) -> str:
492
+ """
493
+ Format OpenAI-compatible SSE chunk.
494
+
495
+ Args:
496
+ request_id: Request/response ID
497
+ created: Unix timestamp
498
+ model: Model name
499
+ content: Delta content
500
+ role: Message role (usually 'assistant')
501
+ finish_reason: Finish reason (e.g., 'stop')
502
+
503
+ Returns:
504
+ Formatted SSE data line
505
+ """
506
+ import json
507
+
508
+ delta = {}
509
+ if role:
510
+ delta["role"] = role
511
+ if content is not None:
512
+ delta["content"] = content
513
+
514
+ chunk = {
515
+ "id": request_id,
516
+ "object": "chat.completion.chunk",
517
+ "created": created,
518
+ "model": model,
519
+ "choices": [{
520
+ "index": 0,
521
+ "delta": delta,
522
+ "finish_reason": finish_reason
523
+ }]
524
+ }
525
+
526
+ return f"data: {json.dumps(chunk)}\n\n"