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,276 @@
1
+ """
2
+ Message feedback endpoint.
3
+
4
+ Provides endpoint for submitting feedback on messages.
5
+
6
+ Endpoints:
7
+ POST /api/v1/messages/feedback - Submit feedback on a message
8
+
9
+ Trace Integration:
10
+ - Feedback auto-resolves trace_id/span_id from the message in the database
11
+ - Phoenix sync attaches feedback as span annotations when trace info is available
12
+
13
+ HTTP Status Codes:
14
+ - 201: Feedback saved AND synced to Phoenix as annotation (phoenix_synced=true)
15
+ - 200: Feedback accepted and saved to DB, but NOT synced to Phoenix
16
+ (missing trace_id/span_id, Phoenix disabled, or sync failed)
17
+
18
+ IMPORTANT - Testing Requirements:
19
+ ╔════════════════════════════════════════════════════════════════════════════════════════════════════╗
20
+ ║ 1. Use 'rem' agent (NOT 'simulator') - only real agents capture traces ║
21
+ ║ 2. Session IDs MUST be UUIDs - use python3 -c "import uuid; print(uuid.uuid4())" ║
22
+ ║ 3. Port-forward OTEL collector: kubectl port-forward -n observability ║
23
+ ║ svc/otel-collector-collector 4318:4318 ║
24
+ ║ 4. Port-forward Phoenix: kubectl port-forward -n siggy svc/phoenix 6006:6006 ║
25
+ ║ 5. Set environment variables when starting the API: ║
26
+ ║ OTEL__ENABLED=true PHOENIX__ENABLED=true PHOENIX_API_KEY=<jwt> uvicorn ... ║
27
+ ║ 6. Get PHOENIX_API_KEY: ║
28
+ ║ kubectl get secret -n siggy rem-phoenix-api-key -o jsonpath='{.data.PHOENIX_API_KEY}' ║
29
+ ║ | base64 -d ║
30
+ ╚════════════════════════════════════════════════════════════════════════════════════════════════════╝
31
+
32
+ Usage:
33
+ # 1. Send a chat message with X-Session-Id header (MUST be UUID!)
34
+ SESSION_ID=$(python3 -c "import uuid; print(uuid.uuid4())")
35
+ curl -X POST http://localhost:8000/api/v1/chat/completions \\
36
+ -H "Content-Type: application/json" \\
37
+ -H "X-Session-Id: $SESSION_ID" \\
38
+ -H "X-Agent-Schema: rem" \\
39
+ -d '{"messages": [{"role": "user", "content": "hello"}], "stream": true}'
40
+
41
+ # 2. Extract message_id from the 'metadata' SSE event:
42
+ # event: metadata
43
+ # data: {"message_id": "728882f8-...", "trace_id": "e53c701c...", ...}
44
+
45
+ # 3. Submit feedback referencing that message (trace_id auto-resolved from DB)
46
+ curl -X POST http://localhost:8000/api/v1/messages/feedback \\
47
+ -H "Content-Type: application/json" \\
48
+ -H "X-Tenant-Id: default" \\
49
+ -d '{
50
+ "session_id": "'$SESSION_ID'",
51
+ "message_id": "<message-id-from-metadata>",
52
+ "rating": 1,
53
+ "categories": ["helpful"],
54
+ "comment": "Great response!"
55
+ }'
56
+
57
+ # 4. Check response:
58
+ # - 201 + phoenix_synced=true = annotation synced to Phoenix (check Phoenix UI at :6006)
59
+ # - 200 + phoenix_synced=false = feedback saved but not synced (missing trace info)
60
+ """
61
+
62
+ from fastapi import APIRouter, Header, HTTPException, Request, Response
63
+ from loguru import logger
64
+ from pydantic import BaseModel, Field
65
+
66
+ from .common import ErrorResponse
67
+
68
+ from ..deps import get_user_id_from_request
69
+ from ...models.entities import Feedback
70
+ from ...services.postgres import Repository
71
+ from ...settings import settings
72
+
73
+ router = APIRouter(prefix="/api/v1", tags=["messages"])
74
+
75
+
76
+ # =============================================================================
77
+ # Request/Response Models
78
+ # =============================================================================
79
+
80
+
81
+ class FeedbackCreateRequest(BaseModel):
82
+ """Request to submit feedback."""
83
+
84
+ session_id: str = Field(description="Session ID this feedback relates to")
85
+ message_id: str | None = Field(
86
+ default=None, description="Specific message ID (null for session-level)"
87
+ )
88
+ rating: int | None = Field(
89
+ default=None,
90
+ ge=-1,
91
+ le=5,
92
+ description="Rating: -1 (thumbs down), 1 (thumbs up), or 1-5 scale",
93
+ )
94
+ categories: list[str] = Field(
95
+ default_factory=list, description="Feedback categories"
96
+ )
97
+ comment: str | None = Field(default=None, description="Free-text comment")
98
+ trace_id: str | None = Field(
99
+ default=None, description="OTEL trace ID (auto-resolved if message has it)"
100
+ )
101
+ span_id: str | None = Field(
102
+ default=None, description="OTEL span ID (auto-resolved if message has it)"
103
+ )
104
+
105
+
106
+ class FeedbackResponse(BaseModel):
107
+ """Response after submitting feedback."""
108
+
109
+ id: str
110
+ session_id: str
111
+ message_id: str | None
112
+ rating: int | None
113
+ categories: list[str]
114
+ comment: str | None
115
+ trace_id: str | None
116
+ span_id: str | None
117
+ phoenix_synced: bool
118
+ created_at: str
119
+
120
+
121
+ # =============================================================================
122
+ # Feedback Endpoint
123
+ # =============================================================================
124
+
125
+
126
+ @router.post(
127
+ "/messages/feedback",
128
+ response_model=FeedbackResponse,
129
+ responses={
130
+ 503: {"model": ErrorResponse, "description": "Database not enabled"},
131
+ },
132
+ )
133
+ async def submit_feedback(
134
+ request: Request,
135
+ response: Response,
136
+ request_body: FeedbackCreateRequest,
137
+ x_tenant_id: str = Header(alias="X-Tenant-Id", default="default"),
138
+ ) -> FeedbackResponse:
139
+ """
140
+ Submit feedback on a message or session.
141
+
142
+ If message_id is provided, feedback is attached to that specific message.
143
+ If only session_id is provided, feedback applies to the entire session.
144
+
145
+ Trace IDs (trace_id, span_id) can be:
146
+ - Provided explicitly in the request
147
+ - Auto-resolved from the message if message_id is provided
148
+
149
+ HTTP Status Codes:
150
+ - 201: Feedback saved AND synced to Phoenix (phoenix_synced=true)
151
+ - 200: Feedback accepted but NOT synced (missing trace info, disabled, or failed)
152
+
153
+ Returns:
154
+ Created feedback object with phoenix_synced indicating sync status
155
+ """
156
+ if not settings.postgres.enabled:
157
+ raise HTTPException(status_code=503, detail="Database not enabled")
158
+
159
+ effective_user_id = get_user_id_from_request(request)
160
+
161
+ # Resolve trace_id/span_id from message if not provided
162
+ trace_id = request_body.trace_id
163
+ span_id = request_body.span_id
164
+
165
+ if request_body.message_id and (not trace_id or not span_id):
166
+ # Look up message by ID to get trace context
167
+ # Note: Messages are stored with tenant_id=user_id (not x_tenant_id header)
168
+ # so we query by ID only - UUIDs are globally unique
169
+ from ...services.postgres import PostgresService
170
+ import uuid
171
+
172
+ logger.info(f"Looking up trace context for message_id={request_body.message_id}")
173
+
174
+ # Convert message_id string to UUID for database query
175
+ try:
176
+ message_uuid = uuid.UUID(request_body.message_id)
177
+ except ValueError as e:
178
+ logger.warning(f"Invalid message_id format '{request_body.message_id}': {e}")
179
+ message_uuid = None
180
+
181
+ if message_uuid:
182
+ db = PostgresService()
183
+ # Ensure connection (same pattern as Repository)
184
+ if not db.pool:
185
+ await db.connect()
186
+
187
+ if db.pool:
188
+ query = """
189
+ SELECT trace_id, span_id FROM messages
190
+ WHERE id = $1 AND deleted_at IS NULL
191
+ LIMIT 1
192
+ """
193
+ async with db.pool.acquire() as conn:
194
+ row = await conn.fetchrow(query, message_uuid)
195
+ logger.info(f"Database query result for message {request_body.message_id}: row={row}")
196
+ if row:
197
+ trace_id = trace_id or row["trace_id"]
198
+ span_id = span_id or row["span_id"]
199
+ logger.info(f"Found trace context for message {request_body.message_id}: trace_id={trace_id}, span_id={span_id}")
200
+ else:
201
+ logger.warning(f"No message found in database with id={request_body.message_id}")
202
+ else:
203
+ logger.warning(f"Database pool not available for message lookup after connect attempt")
204
+
205
+ feedback = Feedback(
206
+ session_id=request_body.session_id,
207
+ message_id=request_body.message_id,
208
+ rating=request_body.rating,
209
+ categories=request_body.categories,
210
+ comment=request_body.comment,
211
+ trace_id=trace_id,
212
+ span_id=span_id,
213
+ phoenix_synced=False,
214
+ annotator_kind="HUMAN",
215
+ user_id=effective_user_id,
216
+ tenant_id=x_tenant_id,
217
+ )
218
+
219
+ repo = Repository(Feedback, table_name="feedbacks")
220
+ result = await repo.upsert(feedback)
221
+
222
+ logger.info(
223
+ f"Feedback submitted: session={request_body.session_id}, "
224
+ f"message={request_body.message_id}, rating={request_body.rating}"
225
+ )
226
+
227
+ # Sync to Phoenix if trace_id/span_id available and Phoenix is enabled
228
+ phoenix_synced = False
229
+ phoenix_annotation_id = None
230
+
231
+ if trace_id and span_id and settings.phoenix.enabled:
232
+ try:
233
+ from ...services.phoenix import PhoenixClient
234
+
235
+ phoenix_client = PhoenixClient()
236
+ phoenix_annotation_id = phoenix_client.sync_user_feedback(
237
+ span_id=span_id,
238
+ rating=request_body.rating,
239
+ categories=request_body.categories,
240
+ comment=request_body.comment,
241
+ feedback_id=str(result.id),
242
+ trace_id=trace_id,
243
+ )
244
+
245
+ if phoenix_annotation_id:
246
+ phoenix_synced = True
247
+ # Update the feedback record with sync status
248
+ result.phoenix_synced = True
249
+ result.phoenix_annotation_id = phoenix_annotation_id
250
+ await repo.upsert(result)
251
+ logger.info(f"Feedback synced to Phoenix: annotation_id={phoenix_annotation_id}")
252
+ else:
253
+ logger.warning(f"Phoenix sync returned no annotation ID for feedback {result.id}")
254
+
255
+ except Exception as e:
256
+ logger.error(f"Failed to sync feedback to Phoenix: {e}")
257
+ # Don't fail the request if Phoenix sync fails
258
+ elif trace_id and span_id:
259
+ logger.debug(f"Feedback has trace info but Phoenix disabled: trace={trace_id}, span={span_id}")
260
+
261
+ # Set HTTP status code based on Phoenix sync result
262
+ # 201 = synced to Phoenix, 200 = accepted but not synced
263
+ response.status_code = 201 if phoenix_synced else 200
264
+
265
+ return FeedbackResponse(
266
+ id=str(result.id),
267
+ session_id=result.session_id,
268
+ message_id=result.message_id,
269
+ rating=result.rating,
270
+ categories=result.categories,
271
+ comment=result.comment,
272
+ trace_id=result.trace_id,
273
+ span_id=result.span_id,
274
+ phoenix_synced=result.phoenix_synced,
275
+ created_at=result.created_at.isoformat() if result.created_at else "",
276
+ )