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.

Files changed (235) hide show
  1. rem/__init__.py +129 -0
  2. rem/agentic/README.md +760 -0
  3. rem/agentic/__init__.py +54 -0
  4. rem/agentic/agents/README.md +155 -0
  5. rem/agentic/agents/__init__.py +38 -0
  6. rem/agentic/agents/agent_manager.py +311 -0
  7. rem/agentic/agents/sse_simulator.py +502 -0
  8. rem/agentic/context.py +425 -0
  9. rem/agentic/context_builder.py +360 -0
  10. rem/agentic/llm_provider_models.py +301 -0
  11. rem/agentic/mcp/__init__.py +0 -0
  12. rem/agentic/mcp/tool_wrapper.py +273 -0
  13. rem/agentic/otel/__init__.py +5 -0
  14. rem/agentic/otel/setup.py +240 -0
  15. rem/agentic/providers/phoenix.py +926 -0
  16. rem/agentic/providers/pydantic_ai.py +854 -0
  17. rem/agentic/query.py +117 -0
  18. rem/agentic/query_helper.py +89 -0
  19. rem/agentic/schema.py +737 -0
  20. rem/agentic/serialization.py +245 -0
  21. rem/agentic/tools/__init__.py +5 -0
  22. rem/agentic/tools/rem_tools.py +242 -0
  23. rem/api/README.md +657 -0
  24. rem/api/deps.py +253 -0
  25. rem/api/main.py +460 -0
  26. rem/api/mcp_router/prompts.py +182 -0
  27. rem/api/mcp_router/resources.py +820 -0
  28. rem/api/mcp_router/server.py +243 -0
  29. rem/api/mcp_router/tools.py +1605 -0
  30. rem/api/middleware/tracking.py +172 -0
  31. rem/api/routers/admin.py +520 -0
  32. rem/api/routers/auth.py +898 -0
  33. rem/api/routers/chat/__init__.py +5 -0
  34. rem/api/routers/chat/child_streaming.py +394 -0
  35. rem/api/routers/chat/completions.py +702 -0
  36. rem/api/routers/chat/json_utils.py +76 -0
  37. rem/api/routers/chat/models.py +202 -0
  38. rem/api/routers/chat/otel_utils.py +33 -0
  39. rem/api/routers/chat/sse_events.py +546 -0
  40. rem/api/routers/chat/streaming.py +950 -0
  41. rem/api/routers/chat/streaming_utils.py +327 -0
  42. rem/api/routers/common.py +18 -0
  43. rem/api/routers/dev.py +87 -0
  44. rem/api/routers/feedback.py +276 -0
  45. rem/api/routers/messages.py +620 -0
  46. rem/api/routers/models.py +86 -0
  47. rem/api/routers/query.py +362 -0
  48. rem/api/routers/shared_sessions.py +422 -0
  49. rem/auth/README.md +258 -0
  50. rem/auth/__init__.py +36 -0
  51. rem/auth/jwt.py +367 -0
  52. rem/auth/middleware.py +318 -0
  53. rem/auth/providers/__init__.py +16 -0
  54. rem/auth/providers/base.py +376 -0
  55. rem/auth/providers/email.py +215 -0
  56. rem/auth/providers/google.py +163 -0
  57. rem/auth/providers/microsoft.py +237 -0
  58. rem/cli/README.md +517 -0
  59. rem/cli/__init__.py +8 -0
  60. rem/cli/commands/README.md +299 -0
  61. rem/cli/commands/__init__.py +3 -0
  62. rem/cli/commands/ask.py +549 -0
  63. rem/cli/commands/cluster.py +1808 -0
  64. rem/cli/commands/configure.py +495 -0
  65. rem/cli/commands/db.py +828 -0
  66. rem/cli/commands/dreaming.py +324 -0
  67. rem/cli/commands/experiments.py +1698 -0
  68. rem/cli/commands/mcp.py +66 -0
  69. rem/cli/commands/process.py +388 -0
  70. rem/cli/commands/query.py +109 -0
  71. rem/cli/commands/scaffold.py +47 -0
  72. rem/cli/commands/schema.py +230 -0
  73. rem/cli/commands/serve.py +106 -0
  74. rem/cli/commands/session.py +453 -0
  75. rem/cli/dreaming.py +363 -0
  76. rem/cli/main.py +123 -0
  77. rem/config.py +244 -0
  78. rem/mcp_server.py +41 -0
  79. rem/models/core/__init__.py +49 -0
  80. rem/models/core/core_model.py +70 -0
  81. rem/models/core/engram.py +333 -0
  82. rem/models/core/experiment.py +672 -0
  83. rem/models/core/inline_edge.py +132 -0
  84. rem/models/core/rem_query.py +246 -0
  85. rem/models/entities/__init__.py +68 -0
  86. rem/models/entities/domain_resource.py +38 -0
  87. rem/models/entities/feedback.py +123 -0
  88. rem/models/entities/file.py +57 -0
  89. rem/models/entities/image_resource.py +88 -0
  90. rem/models/entities/message.py +64 -0
  91. rem/models/entities/moment.py +123 -0
  92. rem/models/entities/ontology.py +181 -0
  93. rem/models/entities/ontology_config.py +131 -0
  94. rem/models/entities/resource.py +95 -0
  95. rem/models/entities/schema.py +87 -0
  96. rem/models/entities/session.py +84 -0
  97. rem/models/entities/shared_session.py +180 -0
  98. rem/models/entities/subscriber.py +175 -0
  99. rem/models/entities/user.py +93 -0
  100. rem/py.typed +0 -0
  101. rem/registry.py +373 -0
  102. rem/schemas/README.md +507 -0
  103. rem/schemas/__init__.py +6 -0
  104. rem/schemas/agents/README.md +92 -0
  105. rem/schemas/agents/core/agent-builder.yaml +235 -0
  106. rem/schemas/agents/core/moment-builder.yaml +178 -0
  107. rem/schemas/agents/core/rem-query-agent.yaml +226 -0
  108. rem/schemas/agents/core/resource-affinity-assessor.yaml +99 -0
  109. rem/schemas/agents/core/simple-assistant.yaml +19 -0
  110. rem/schemas/agents/core/user-profile-builder.yaml +163 -0
  111. rem/schemas/agents/examples/contract-analyzer.yaml +317 -0
  112. rem/schemas/agents/examples/contract-extractor.yaml +134 -0
  113. rem/schemas/agents/examples/cv-parser.yaml +263 -0
  114. rem/schemas/agents/examples/hello-world.yaml +37 -0
  115. rem/schemas/agents/examples/query.yaml +54 -0
  116. rem/schemas/agents/examples/simple.yaml +21 -0
  117. rem/schemas/agents/examples/test.yaml +29 -0
  118. rem/schemas/agents/rem.yaml +132 -0
  119. rem/schemas/evaluators/hello-world/default.yaml +77 -0
  120. rem/schemas/evaluators/rem/faithfulness.yaml +219 -0
  121. rem/schemas/evaluators/rem/lookup-correctness.yaml +182 -0
  122. rem/schemas/evaluators/rem/retrieval-precision.yaml +199 -0
  123. rem/schemas/evaluators/rem/retrieval-recall.yaml +211 -0
  124. rem/schemas/evaluators/rem/search-correctness.yaml +192 -0
  125. rem/services/__init__.py +18 -0
  126. rem/services/audio/INTEGRATION.md +308 -0
  127. rem/services/audio/README.md +376 -0
  128. rem/services/audio/__init__.py +15 -0
  129. rem/services/audio/chunker.py +354 -0
  130. rem/services/audio/transcriber.py +259 -0
  131. rem/services/content/README.md +1269 -0
  132. rem/services/content/__init__.py +5 -0
  133. rem/services/content/providers.py +760 -0
  134. rem/services/content/service.py +762 -0
  135. rem/services/dreaming/README.md +230 -0
  136. rem/services/dreaming/__init__.py +53 -0
  137. rem/services/dreaming/affinity_service.py +322 -0
  138. rem/services/dreaming/moment_service.py +251 -0
  139. rem/services/dreaming/ontology_service.py +54 -0
  140. rem/services/dreaming/user_model_service.py +297 -0
  141. rem/services/dreaming/utils.py +39 -0
  142. rem/services/email/__init__.py +10 -0
  143. rem/services/email/service.py +522 -0
  144. rem/services/email/templates.py +360 -0
  145. rem/services/embeddings/__init__.py +11 -0
  146. rem/services/embeddings/api.py +127 -0
  147. rem/services/embeddings/worker.py +435 -0
  148. rem/services/fs/README.md +662 -0
  149. rem/services/fs/__init__.py +62 -0
  150. rem/services/fs/examples.py +206 -0
  151. rem/services/fs/examples_paths.py +204 -0
  152. rem/services/fs/git_provider.py +935 -0
  153. rem/services/fs/local_provider.py +760 -0
  154. rem/services/fs/parsing-hooks-examples.md +172 -0
  155. rem/services/fs/paths.py +276 -0
  156. rem/services/fs/provider.py +460 -0
  157. rem/services/fs/s3_provider.py +1042 -0
  158. rem/services/fs/service.py +186 -0
  159. rem/services/git/README.md +1075 -0
  160. rem/services/git/__init__.py +17 -0
  161. rem/services/git/service.py +469 -0
  162. rem/services/phoenix/EXPERIMENT_DESIGN.md +1146 -0
  163. rem/services/phoenix/README.md +453 -0
  164. rem/services/phoenix/__init__.py +46 -0
  165. rem/services/phoenix/client.py +960 -0
  166. rem/services/phoenix/config.py +88 -0
  167. rem/services/phoenix/prompt_labels.py +477 -0
  168. rem/services/postgres/README.md +757 -0
  169. rem/services/postgres/__init__.py +49 -0
  170. rem/services/postgres/diff_service.py +599 -0
  171. rem/services/postgres/migration_service.py +427 -0
  172. rem/services/postgres/programmable_diff_service.py +635 -0
  173. rem/services/postgres/pydantic_to_sqlalchemy.py +562 -0
  174. rem/services/postgres/register_type.py +353 -0
  175. rem/services/postgres/repository.py +481 -0
  176. rem/services/postgres/schema_generator.py +661 -0
  177. rem/services/postgres/service.py +802 -0
  178. rem/services/postgres/sql_builder.py +355 -0
  179. rem/services/rate_limit.py +113 -0
  180. rem/services/rem/README.md +318 -0
  181. rem/services/rem/__init__.py +23 -0
  182. rem/services/rem/exceptions.py +71 -0
  183. rem/services/rem/executor.py +293 -0
  184. rem/services/rem/parser.py +180 -0
  185. rem/services/rem/queries.py +196 -0
  186. rem/services/rem/query.py +371 -0
  187. rem/services/rem/service.py +608 -0
  188. rem/services/session/README.md +374 -0
  189. rem/services/session/__init__.py +13 -0
  190. rem/services/session/compression.py +488 -0
  191. rem/services/session/pydantic_messages.py +310 -0
  192. rem/services/session/reload.py +85 -0
  193. rem/services/user_service.py +130 -0
  194. rem/settings.py +1877 -0
  195. rem/sql/background_indexes.sql +52 -0
  196. rem/sql/migrations/001_install.sql +983 -0
  197. rem/sql/migrations/002_install_models.sql +3157 -0
  198. rem/sql/migrations/003_optional_extensions.sql +326 -0
  199. rem/sql/migrations/004_cache_system.sql +282 -0
  200. rem/sql/migrations/005_schema_update.sql +145 -0
  201. rem/sql/migrations/migrate_session_id_to_uuid.sql +45 -0
  202. rem/utils/AGENTIC_CHUNKING.md +597 -0
  203. rem/utils/README.md +628 -0
  204. rem/utils/__init__.py +61 -0
  205. rem/utils/agentic_chunking.py +622 -0
  206. rem/utils/batch_ops.py +343 -0
  207. rem/utils/chunking.py +108 -0
  208. rem/utils/clip_embeddings.py +276 -0
  209. rem/utils/constants.py +97 -0
  210. rem/utils/date_utils.py +228 -0
  211. rem/utils/dict_utils.py +98 -0
  212. rem/utils/embeddings.py +436 -0
  213. rem/utils/examples/embeddings_example.py +305 -0
  214. rem/utils/examples/sql_types_example.py +202 -0
  215. rem/utils/files.py +323 -0
  216. rem/utils/markdown.py +16 -0
  217. rem/utils/mime_types.py +158 -0
  218. rem/utils/model_helpers.py +492 -0
  219. rem/utils/schema_loader.py +649 -0
  220. rem/utils/sql_paths.py +146 -0
  221. rem/utils/sql_types.py +350 -0
  222. rem/utils/user_id.py +81 -0
  223. rem/utils/vision.py +325 -0
  224. rem/workers/README.md +506 -0
  225. rem/workers/__init__.py +7 -0
  226. rem/workers/db_listener.py +579 -0
  227. rem/workers/db_maintainer.py +74 -0
  228. rem/workers/dreaming.py +502 -0
  229. rem/workers/engram_processor.py +312 -0
  230. rem/workers/sqs_file_processor.py +193 -0
  231. rem/workers/unlogged_maintainer.py +463 -0
  232. remdb-0.3.242.dist-info/METADATA +1632 -0
  233. remdb-0.3.242.dist-info/RECORD +235 -0
  234. remdb-0.3.242.dist-info/WHEEL +4 -0
  235. remdb-0.3.242.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,546 @@
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 top-level agent schema (e.g., 'siggy', 'rem')"
325
+ )
326
+ responding_agent: str | None = Field(
327
+ default=None,
328
+ description="Name of the agent that produced this response (may differ from agent_schema if delegated via ask_agent)"
329
+ )
330
+
331
+ # Session info
332
+ session_name: str | None = Field(
333
+ default=None,
334
+ description="Short 1-3 phrase name for the session topic (e.g., 'Prescription Drug Questions', 'AWS Setup Help')"
335
+ )
336
+
337
+ # Quality indicators
338
+ confidence: float | None = Field(
339
+ default=None, ge=0, le=1,
340
+ description="Confidence score (0-1)"
341
+ )
342
+ sources: list[str] | None = Field(
343
+ default=None,
344
+ description="Referenced sources/citations"
345
+ )
346
+
347
+ # Model info
348
+ model_version: str | None = Field(
349
+ default=None,
350
+ description="Model version used"
351
+ )
352
+
353
+ # Performance metrics
354
+ latency_ms: int | None = Field(
355
+ default=None,
356
+ description="Response latency in milliseconds"
357
+ )
358
+ token_count: int | None = Field(
359
+ default=None,
360
+ description="Token count for this response"
361
+ )
362
+
363
+ # Trace context for observability (deterministic, captured from OTEL)
364
+ trace_id: str | None = Field(
365
+ default=None,
366
+ description="OTEL trace ID for correlating with Phoenix/observability systems"
367
+ )
368
+ span_id: str | None = Field(
369
+ default=None,
370
+ description="OTEL span ID for correlating with Phoenix/observability systems"
371
+ )
372
+
373
+ # System flags
374
+ flags: list[str] | None = Field(
375
+ default=None,
376
+ description="System flags (e.g., 'uncertain', 'needs_review')"
377
+ )
378
+ hidden: bool = Field(
379
+ default=False,
380
+ description="If true, should not be displayed to user"
381
+ )
382
+ extra: dict[str, Any] | None = Field(
383
+ default=None,
384
+ description="Additional metadata"
385
+ )
386
+
387
+
388
+ class ProgressEvent(BaseModel):
389
+ """Progress indicator event."""
390
+
391
+ type: Literal["progress"] = "progress"
392
+ step: int = Field(description="Current step number")
393
+ total_steps: int = Field(description="Total number of steps")
394
+ label: str = Field(description="Step description")
395
+ status: Literal["pending", "in_progress", "completed", "failed"] = Field(
396
+ description="Step status"
397
+ )
398
+
399
+
400
+ class ToolCallEvent(BaseModel):
401
+ """Tool invocation event."""
402
+
403
+ type: Literal["tool_call"] = "tool_call"
404
+ tool_name: str = Field(description="Name of tool being called")
405
+ tool_id: str | None = Field(
406
+ default=None,
407
+ description="Unique call identifier"
408
+ )
409
+ status: Literal["started", "completed", "failed"] = Field(
410
+ description="Tool call status"
411
+ )
412
+ arguments: dict[str, Any] | None = Field(
413
+ default=None,
414
+ description="Tool arguments (for 'started' status)"
415
+ )
416
+ result: str | dict[str, Any] | None = Field(
417
+ default=None,
418
+ description="Tool result - full dict for finalize_intake, summary string for others"
419
+ )
420
+ error: str | None = Field(
421
+ default=None,
422
+ description="Error message (for 'failed' status)"
423
+ )
424
+
425
+
426
+ class ErrorEvent(BaseModel):
427
+ """Error notification event."""
428
+
429
+ type: Literal["error"] = "error"
430
+ code: str = Field(description="Error code")
431
+ message: str = Field(description="Human-readable error message")
432
+ details: dict[str, Any] | None = Field(
433
+ default=None,
434
+ description="Additional error details"
435
+ )
436
+ recoverable: bool = Field(
437
+ default=True,
438
+ description="Whether error is recoverable"
439
+ )
440
+
441
+
442
+ class DoneEvent(BaseModel):
443
+ """Stream completion event."""
444
+
445
+ type: Literal["done"] = "done"
446
+ reason: Literal["stop", "length", "error", "cancelled"] = Field(
447
+ default="stop",
448
+ description="Completion reason"
449
+ )
450
+
451
+
452
+ # Union type for all SSE events
453
+ SSEEvent = (
454
+ TextDeltaEvent
455
+ | ReasoningEvent
456
+ | ActionRequestEvent
457
+ | MetadataEvent
458
+ | ProgressEvent
459
+ | ToolCallEvent
460
+ | ErrorEvent
461
+ | DoneEvent
462
+ )
463
+
464
+
465
+ # =============================================================================
466
+ # SSE Formatting Helpers
467
+ # =============================================================================
468
+
469
+ def format_sse_event(event: SSEEvent) -> str:
470
+ """
471
+ Format an SSE event for transmission.
472
+
473
+ Standard data: format for text_delta (OpenAI compatibility).
474
+ Named event: format for other event types.
475
+
476
+ Args:
477
+ event: SSE event to format
478
+
479
+ Returns:
480
+ Formatted SSE string ready for transmission
481
+
482
+ Example:
483
+ >>> event = ReasoningEvent(content="Analyzing...")
484
+ >>> format_sse_event(event)
485
+ 'event: reasoning\\ndata: {"type": "reasoning", "content": "Analyzing..."}\\n\\n'
486
+ """
487
+ import json
488
+
489
+ event_json = event.model_dump_json()
490
+
491
+ # TextDeltaEvent uses standard data: format for OpenAI compatibility
492
+ if isinstance(event, TextDeltaEvent):
493
+ return f"data: {event_json}\n\n"
494
+
495
+ # DoneEvent uses special marker
496
+ if isinstance(event, DoneEvent):
497
+ return f"event: done\ndata: {event_json}\n\n"
498
+
499
+ # All other events use named event format
500
+ event_type = event.type
501
+ return f"event: {event_type}\ndata: {event_json}\n\n"
502
+
503
+
504
+ def format_openai_sse_chunk(
505
+ request_id: str,
506
+ created: int,
507
+ model: str,
508
+ content: str | None = None,
509
+ role: str | None = None,
510
+ finish_reason: str | None = None,
511
+ ) -> str:
512
+ """
513
+ Format OpenAI-compatible SSE chunk.
514
+
515
+ Args:
516
+ request_id: Request/response ID
517
+ created: Unix timestamp
518
+ model: Model name
519
+ content: Delta content
520
+ role: Message role (usually 'assistant')
521
+ finish_reason: Finish reason (e.g., 'stop')
522
+
523
+ Returns:
524
+ Formatted SSE data line
525
+ """
526
+ import json
527
+
528
+ delta = {}
529
+ if role:
530
+ delta["role"] = role
531
+ if content is not None:
532
+ delta["content"] = content
533
+
534
+ chunk = {
535
+ "id": request_id,
536
+ "object": "chat.completion.chunk",
537
+ "created": created,
538
+ "model": model,
539
+ "choices": [{
540
+ "index": 0,
541
+ "delta": delta,
542
+ "finish_reason": finish_reason
543
+ }]
544
+ }
545
+
546
+ return f"data: {json.dumps(chunk)}\n\n"