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,502 @@
1
+ """
2
+ SSE Event Simulator Agent.
3
+
4
+ A programmatic simulator that generates rich SSE events for testing and
5
+ demonstrating the streaming protocol. NOT an LLM-based agent - this is
6
+ pure Python that emits scripted SSE events.
7
+
8
+ Usage:
9
+ from rem.agentic.agents.simulator import stream_simulator_events
10
+
11
+ async for event in stream_simulator_events("demo"):
12
+ yield format_sse_event(event)
13
+
14
+ The simulator demonstrates:
15
+ 1. Reasoning events (thinking process)
16
+ 2. Text deltas (streamed content)
17
+ 3. Progress indicators
18
+ 4. Tool call events
19
+ 5. Action solicitations (user interaction)
20
+ 6. Metadata events
21
+ 7. Done event
22
+
23
+ This is useful for:
24
+ - Frontend development without LLM costs
25
+ - Testing SSE parsing and rendering
26
+ - Demonstrating the full event protocol
27
+ - Load testing streaming infrastructure
28
+ """
29
+
30
+ import asyncio
31
+ import time
32
+ import uuid
33
+ from typing import AsyncGenerator
34
+
35
+ from rem.api.routers.chat.sse_events import (
36
+ ReasoningEvent,
37
+ ActionRequestEvent,
38
+ MetadataEvent,
39
+ ProgressEvent,
40
+ ToolCallEvent,
41
+ DoneEvent,
42
+ ActionRequestCard,
43
+ ActionSubmit,
44
+ ActionStyle,
45
+ InputText,
46
+ InputChoiceSet,
47
+ ActionDisplayStyle,
48
+ format_sse_event,
49
+ )
50
+ from rem.api.routers.chat.models import (
51
+ ChatCompletionStreamResponse,
52
+ ChatCompletionStreamChoice,
53
+ ChatCompletionMessageDelta,
54
+ )
55
+
56
+
57
+ # =============================================================================
58
+ # Demo Content
59
+ # =============================================================================
60
+
61
+ DEMO_REASONING_STEPS = [
62
+ "Analyzing the user's request...",
63
+ "Considering the best approach to demonstrate SSE events...",
64
+ "Planning a response that showcases all event types...",
65
+ "Preparing rich markdown content with examples...",
66
+ ]
67
+
68
+ DEMO_MARKDOWN_CONTENT = """# SSE Streaming Demo
69
+
70
+ This response demonstrates the **rich SSE event protocol** with multiple event types streamed in real-time.
71
+
72
+ ## What You're Seeing
73
+
74
+ 1. **Reasoning Events** - The "thinking" process shown in a collapsible section
75
+ 2. **Text Streaming** - This markdown content, streamed word by word
76
+ 3. **Progress Events** - Step indicators during processing
77
+ 4. **Tool Calls** - Simulated tool invocations
78
+ 5. **Action Requests** - Interactive UI elements for user input
79
+
80
+ ## Code Example
81
+
82
+ ```python
83
+ from rem.agentic.agents.simulator import stream_simulator_events
84
+
85
+ async def demo():
86
+ async for event in stream_simulator_events("demo"):
87
+ print(event.type, event)
88
+ ```
89
+
90
+ ## Features Table
91
+
92
+ | Event Type | Purpose | UI Display |
93
+ |------------|---------|------------|
94
+ | `reasoning` | Model thinking | Collapsible section |
95
+ | `text_delta` | Content chunks | Main response area |
96
+ | `progress` | Step indicators | Progress bar |
97
+ | `tool_call` | Tool invocations | Tool status panel |
98
+ | `action_request` | User input | Buttons/forms |
99
+ | `metadata` | System info | Hidden or badge |
100
+
101
+ ## Summary
102
+
103
+ The SSE protocol enables rich, interactive AI experiences beyond simple text streaming. Each event type serves a specific purpose in the UI.
104
+
105
+ """
106
+
107
+ DEMO_TOOL_CALLS = [
108
+ ("search_knowledge", {"query": "SSE streaming best practices"}),
109
+ ("format_response", {"style": "markdown", "include_examples": True}),
110
+ ]
111
+
112
+ DEMO_PROGRESS_STEPS = [
113
+ "Initializing response",
114
+ "Generating content",
115
+ "Formatting output",
116
+ "Preparing actions",
117
+ ]
118
+
119
+
120
+ # =============================================================================
121
+ # Simulator Functions
122
+ # =============================================================================
123
+
124
+ async def stream_simulator_events(
125
+ prompt: str,
126
+ delay_ms: int = 50,
127
+ include_reasoning: bool = True,
128
+ include_progress: bool = True,
129
+ include_tool_calls: bool = True,
130
+ include_actions: bool = True,
131
+ include_metadata: bool = True,
132
+ # Message correlation IDs
133
+ message_id: str | None = None,
134
+ in_reply_to: str | None = None,
135
+ session_id: str | None = None,
136
+ # Model info
137
+ model: str = "simulator-v1.0.0",
138
+ ) -> AsyncGenerator[str, None]:
139
+ """
140
+ Generate a sequence of SSE events simulating an AI response.
141
+
142
+ This is a programmatic simulator - no LLM calls are made.
143
+ Events are yielded in a realistic order with configurable delays.
144
+
145
+ Text content uses OpenAI-compatible format for consistency with real agents.
146
+ Other events (reasoning, progress, tool_call, metadata) use named SSE events.
147
+
148
+ Args:
149
+ prompt: User prompt (used to vary output slightly)
150
+ delay_ms: Delay between events in milliseconds
151
+ include_reasoning: Whether to emit reasoning events
152
+ include_progress: Whether to emit progress events
153
+ include_tool_calls: Whether to emit tool call events
154
+ include_actions: Whether to emit action request at end
155
+ include_metadata: Whether to emit metadata event
156
+ message_id: Database ID of the assistant message being streamed
157
+ in_reply_to: Database ID of the user message this responds to
158
+ session_id: Session ID for conversation correlation
159
+ model: Model name for response metadata
160
+
161
+ Yields:
162
+ SSE-formatted strings ready for HTTP streaming
163
+
164
+ Example:
165
+ ```python
166
+ async for sse_string in stream_simulator_events("demo"):
167
+ print(sse_string)
168
+ ```
169
+ """
170
+ delay = delay_ms / 1000.0
171
+ request_id = f"chatcmpl-{uuid.uuid4().hex[:24]}"
172
+ created_at = int(time.time())
173
+ is_first_chunk = True
174
+
175
+ # Phase 1: Reasoning events
176
+ if include_reasoning:
177
+ for i, step in enumerate(DEMO_REASONING_STEPS):
178
+ await asyncio.sleep(delay)
179
+ yield format_sse_event(ReasoningEvent(content=step + "\n", step=i + 1))
180
+
181
+ # Phase 2: Progress - Starting
182
+ if include_progress:
183
+ await asyncio.sleep(delay)
184
+ yield format_sse_event(ProgressEvent(
185
+ step=1,
186
+ total_steps=len(DEMO_PROGRESS_STEPS),
187
+ label=DEMO_PROGRESS_STEPS[0],
188
+ status="in_progress"
189
+ ))
190
+
191
+ # Phase 3: Tool calls
192
+ if include_tool_calls:
193
+ for tool_name, args in DEMO_TOOL_CALLS:
194
+ tool_id = f"call_{uuid.uuid4().hex[:8]}"
195
+
196
+ await asyncio.sleep(delay)
197
+ yield format_sse_event(ToolCallEvent(
198
+ tool_name=tool_name,
199
+ tool_id=tool_id,
200
+ status="started",
201
+ arguments=args
202
+ ))
203
+
204
+ await asyncio.sleep(delay * 3) # Simulate tool execution
205
+ yield format_sse_event(ToolCallEvent(
206
+ tool_name=tool_name,
207
+ tool_id=tool_id,
208
+ status="completed",
209
+ result=f"Retrieved data for {tool_name}"
210
+ ))
211
+
212
+ # Phase 4: Progress - Generating
213
+ if include_progress:
214
+ await asyncio.sleep(delay)
215
+ yield format_sse_event(ProgressEvent(
216
+ step=2,
217
+ total_steps=len(DEMO_PROGRESS_STEPS),
218
+ label=DEMO_PROGRESS_STEPS[1],
219
+ status="in_progress"
220
+ ))
221
+
222
+ # Phase 5: Stream text content in OpenAI format
223
+ words = DEMO_MARKDOWN_CONTENT.split(" ")
224
+ buffer = ""
225
+ for i, word in enumerate(words):
226
+ buffer += word + " "
227
+ # Emit every few words to simulate realistic streaming
228
+ if len(buffer) > 20 or i == len(words) - 1:
229
+ await asyncio.sleep(delay)
230
+ # OpenAI-compatible format
231
+ chunk = ChatCompletionStreamResponse(
232
+ id=request_id,
233
+ created=created_at,
234
+ model=model,
235
+ choices=[
236
+ ChatCompletionStreamChoice(
237
+ index=0,
238
+ delta=ChatCompletionMessageDelta(
239
+ role="assistant" if is_first_chunk else None,
240
+ content=buffer,
241
+ ),
242
+ finish_reason=None,
243
+ )
244
+ ],
245
+ )
246
+ is_first_chunk = False
247
+ yield f"data: {chunk.model_dump_json()}\n\n"
248
+ buffer = ""
249
+
250
+ # Phase 6: Progress - Formatting
251
+ if include_progress:
252
+ await asyncio.sleep(delay)
253
+ yield format_sse_event(ProgressEvent(
254
+ step=3,
255
+ total_steps=len(DEMO_PROGRESS_STEPS),
256
+ label=DEMO_PROGRESS_STEPS[2],
257
+ status="in_progress"
258
+ ))
259
+
260
+ # Phase 7: Metadata (includes message correlation IDs)
261
+ if include_metadata:
262
+ await asyncio.sleep(delay)
263
+ yield format_sse_event(MetadataEvent(
264
+ # Message correlation IDs
265
+ message_id=message_id,
266
+ in_reply_to=in_reply_to,
267
+ session_id=session_id,
268
+ # Session info
269
+ session_name="SSE Demo Session",
270
+ # Quality indicators
271
+ confidence=0.95,
272
+ sources=["rem/api/routers/chat/sse_events.py", "rem/agentic/agents/sse_simulator.py"],
273
+ # Model info
274
+ model_version=model,
275
+ # Performance metrics
276
+ latency_ms=int(len(words) * delay_ms),
277
+ token_count=len(words),
278
+ # System flags
279
+ flags=["demo_mode"],
280
+ hidden=False,
281
+ extra={"prompt_length": len(prompt)}
282
+ ))
283
+
284
+ # Phase 8: Progress - Preparing actions
285
+ if include_progress:
286
+ await asyncio.sleep(delay)
287
+ yield format_sse_event(ProgressEvent(
288
+ step=4,
289
+ total_steps=len(DEMO_PROGRESS_STEPS),
290
+ label=DEMO_PROGRESS_STEPS[3],
291
+ status="in_progress"
292
+ ))
293
+
294
+ # Phase 9: Action solicitation
295
+ if include_actions:
296
+ await asyncio.sleep(delay)
297
+ yield format_sse_event(ActionRequestEvent(
298
+ card=ActionRequestCard(
299
+ id=f"feedback-{uuid.uuid4().hex[:8]}",
300
+ prompt="Was this SSE demonstration helpful?",
301
+ display_style=ActionDisplayStyle.INLINE,
302
+ actions=[
303
+ ActionSubmit(
304
+ id="helpful-yes",
305
+ title="Yes, very helpful!",
306
+ style=ActionStyle.POSITIVE,
307
+ data={"rating": 5, "feedback": "positive"}
308
+ ),
309
+ ActionSubmit(
310
+ id="helpful-somewhat",
311
+ title="Somewhat",
312
+ style=ActionStyle.DEFAULT,
313
+ data={"rating": 3, "feedback": "neutral"}
314
+ ),
315
+ ActionSubmit(
316
+ id="helpful-no",
317
+ title="Not really",
318
+ style=ActionStyle.SECONDARY,
319
+ data={"rating": 1, "feedback": "negative"}
320
+ ),
321
+ ],
322
+ inputs=[
323
+ InputText(
324
+ id="comments",
325
+ label="Any comments?",
326
+ placeholder="Optional feedback...",
327
+ is_multiline=True,
328
+ max_length=500
329
+ ),
330
+ InputChoiceSet(
331
+ id="use_case",
332
+ label="What's your use case?",
333
+ choices=[
334
+ {"title": "Frontend development", "value": "frontend"},
335
+ {"title": "Testing", "value": "testing"},
336
+ {"title": "Learning", "value": "learning"},
337
+ {"title": "Other", "value": "other"},
338
+ ],
339
+ is_required=False
340
+ ),
341
+ ],
342
+ timeout_ms=60000,
343
+ fallback_text="Please provide feedback on this demo."
344
+ )
345
+ ))
346
+
347
+ # Phase 10: Mark all progress complete
348
+ if include_progress:
349
+ for i, label in enumerate(DEMO_PROGRESS_STEPS):
350
+ await asyncio.sleep(delay / 2)
351
+ yield format_sse_event(ProgressEvent(
352
+ step=i + 1,
353
+ total_steps=len(DEMO_PROGRESS_STEPS),
354
+ label=label,
355
+ status="completed"
356
+ ))
357
+
358
+ # Phase 11: Final chunk with finish_reason
359
+ final_chunk = ChatCompletionStreamResponse(
360
+ id=request_id,
361
+ created=created_at,
362
+ model=model,
363
+ choices=[
364
+ ChatCompletionStreamChoice(
365
+ index=0,
366
+ delta=ChatCompletionMessageDelta(),
367
+ finish_reason="stop",
368
+ )
369
+ ],
370
+ )
371
+ yield f"data: {final_chunk.model_dump_json()}\n\n"
372
+
373
+ # Phase 12: Done event
374
+ await asyncio.sleep(delay)
375
+ yield format_sse_event(DoneEvent(reason="stop"))
376
+
377
+ # Phase 13: OpenAI termination marker
378
+ yield "data: [DONE]\n\n"
379
+
380
+
381
+ async def stream_minimal_demo(
382
+ content: str = "Hello from the simulator!",
383
+ delay_ms: int = 30,
384
+ model: str = "simulator-v1.0.0",
385
+ ) -> AsyncGenerator[str, None]:
386
+ """
387
+ Generate a minimal SSE sequence with just text and done.
388
+
389
+ Useful for simple testing without all event types.
390
+ Uses OpenAI-compatible format for text content.
391
+
392
+ Args:
393
+ content: Text content to stream
394
+ delay_ms: Delay between chunks
395
+ model: Model name for response metadata
396
+
397
+ Yields:
398
+ SSE-formatted strings
399
+ """
400
+ delay = delay_ms / 1000.0
401
+ request_id = f"chatcmpl-{uuid.uuid4().hex[:24]}"
402
+ created_at = int(time.time())
403
+ is_first_chunk = True
404
+
405
+ # Stream content word by word in OpenAI format
406
+ words = content.split(" ")
407
+ for word in words:
408
+ await asyncio.sleep(delay)
409
+ chunk = ChatCompletionStreamResponse(
410
+ id=request_id,
411
+ created=created_at,
412
+ model=model,
413
+ choices=[
414
+ ChatCompletionStreamChoice(
415
+ index=0,
416
+ delta=ChatCompletionMessageDelta(
417
+ role="assistant" if is_first_chunk else None,
418
+ content=word + " ",
419
+ ),
420
+ finish_reason=None,
421
+ )
422
+ ],
423
+ )
424
+ is_first_chunk = False
425
+ yield f"data: {chunk.model_dump_json()}\n\n"
426
+
427
+ # Final chunk with finish_reason
428
+ final_chunk = ChatCompletionStreamResponse(
429
+ id=request_id,
430
+ created=created_at,
431
+ model=model,
432
+ choices=[
433
+ ChatCompletionStreamChoice(
434
+ index=0,
435
+ delta=ChatCompletionMessageDelta(),
436
+ finish_reason="stop",
437
+ )
438
+ ],
439
+ )
440
+ yield f"data: {final_chunk.model_dump_json()}\n\n"
441
+
442
+ await asyncio.sleep(delay)
443
+ yield format_sse_event(DoneEvent(reason="stop"))
444
+ yield "data: [DONE]\n\n"
445
+
446
+
447
+ async def stream_error_demo(
448
+ error_after_words: int = 10,
449
+ model: str = "simulator-v1.0.0",
450
+ ) -> AsyncGenerator[str, None]:
451
+ """
452
+ Generate an SSE sequence that ends with an error.
453
+
454
+ Useful for testing error handling in the frontend.
455
+ Uses OpenAI-compatible format for text content.
456
+
457
+ Args:
458
+ error_after_words: Number of words before error
459
+ model: Model name for response metadata
460
+
461
+ Yields:
462
+ SSE-formatted strings including an error event
463
+ """
464
+ from rem.api.routers.chat.sse_events import ErrorEvent
465
+
466
+ request_id = f"chatcmpl-{uuid.uuid4().hex[:24]}"
467
+ created_at = int(time.time())
468
+ is_first_chunk = True
469
+
470
+ content = "This is a demo that will encounter an error during streaming. Watch what happens when things go wrong..."
471
+ words = content.split(" ")
472
+
473
+ for i, word in enumerate(words[:error_after_words]):
474
+ await asyncio.sleep(0.03)
475
+ chunk = ChatCompletionStreamResponse(
476
+ id=request_id,
477
+ created=created_at,
478
+ model=model,
479
+ choices=[
480
+ ChatCompletionStreamChoice(
481
+ index=0,
482
+ delta=ChatCompletionMessageDelta(
483
+ role="assistant" if is_first_chunk else None,
484
+ content=word + " ",
485
+ ),
486
+ finish_reason=None,
487
+ )
488
+ ],
489
+ )
490
+ is_first_chunk = False
491
+ yield f"data: {chunk.model_dump_json()}\n\n"
492
+
493
+ await asyncio.sleep(0.1)
494
+ yield format_sse_event(ErrorEvent(
495
+ code="simulated_error",
496
+ message="This is a simulated error for testing purposes",
497
+ details={"words_sent": error_after_words, "demo": True},
498
+ recoverable=True
499
+ ))
500
+
501
+ yield format_sse_event(DoneEvent(reason="error"))
502
+ yield "data: [DONE]\n\n"