remdb 0.3.103__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.
- rem/agentic/context.py +28 -24
- rem/agentic/mcp/tool_wrapper.py +29 -3
- rem/agentic/otel/setup.py +92 -4
- rem/agentic/providers/pydantic_ai.py +88 -18
- rem/agentic/schema.py +358 -21
- rem/agentic/tools/rem_tools.py +3 -3
- rem/api/main.py +85 -16
- rem/api/mcp_router/resources.py +1 -1
- rem/api/mcp_router/server.py +18 -4
- rem/api/mcp_router/tools.py +383 -16
- rem/api/routers/admin.py +218 -1
- rem/api/routers/chat/completions.py +30 -3
- rem/api/routers/chat/streaming.py +143 -3
- rem/api/routers/feedback.py +12 -319
- rem/api/routers/query.py +360 -0
- rem/api/routers/shared_sessions.py +13 -13
- rem/cli/commands/README.md +237 -64
- rem/cli/commands/cluster.py +1300 -0
- rem/cli/commands/configure.py +1 -3
- rem/cli/commands/db.py +354 -143
- rem/cli/commands/process.py +14 -8
- rem/cli/commands/schema.py +92 -45
- rem/cli/main.py +27 -6
- rem/models/core/rem_query.py +5 -2
- rem/models/entities/shared_session.py +2 -28
- rem/registry.py +10 -4
- rem/services/content/service.py +30 -8
- rem/services/embeddings/api.py +4 -4
- rem/services/embeddings/worker.py +16 -16
- rem/services/postgres/README.md +151 -26
- rem/services/postgres/__init__.py +2 -1
- rem/services/postgres/diff_service.py +531 -0
- rem/services/postgres/pydantic_to_sqlalchemy.py +427 -129
- rem/services/postgres/schema_generator.py +205 -4
- rem/services/postgres/service.py +6 -6
- rem/services/rem/parser.py +44 -9
- rem/services/rem/service.py +36 -2
- rem/services/session/reload.py +1 -1
- rem/settings.py +56 -7
- rem/sql/background_indexes.sql +19 -24
- rem/sql/migrations/001_install.sql +252 -69
- rem/sql/migrations/002_install_models.sql +2171 -593
- rem/sql/migrations/003_optional_extensions.sql +326 -0
- rem/sql/migrations/004_cache_system.sql +548 -0
- rem/utils/__init__.py +18 -0
- rem/utils/date_utils.py +2 -2
- rem/utils/schema_loader.py +17 -13
- rem/utils/sql_paths.py +146 -0
- rem/workers/__init__.py +2 -1
- rem/workers/unlogged_maintainer.py +463 -0
- {remdb-0.3.103.dist-info → remdb-0.3.118.dist-info}/METADATA +149 -76
- {remdb-0.3.103.dist-info → remdb-0.3.118.dist-info}/RECORD +54 -48
- rem/sql/migrations/003_seed_default_user.sql +0 -48
- {remdb-0.3.103.dist-info → remdb-0.3.118.dist-info}/WHEEL +0 -0
- {remdb-0.3.103.dist-info → remdb-0.3.118.dist-info}/entry_points.txt +0 -0
rem/api/routers/feedback.py
CHANGED
|
@@ -1,35 +1,26 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
Message feedback endpoint.
|
|
3
3
|
|
|
4
|
-
Provides
|
|
5
|
-
- Submitting feedback on messages or sessions
|
|
6
|
-
- Listing feedback with filters
|
|
7
|
-
- Syncing feedback to Phoenix as annotations (async)
|
|
4
|
+
Provides endpoint for submitting feedback on messages.
|
|
8
5
|
|
|
9
6
|
Endpoints:
|
|
10
|
-
POST /api/v1/feedback
|
|
11
|
-
GET /api/v1/feedback - List feedback with filters
|
|
12
|
-
GET /api/v1/feedback/{id} - Get specific feedback
|
|
13
|
-
GET /api/v1/feedback/categories - List available categories
|
|
7
|
+
POST /api/v1/messages/feedback - Submit feedback on a message
|
|
14
8
|
|
|
15
9
|
Trace Integration:
|
|
16
10
|
- Feedback can reference trace_id/span_id for OTEL integration
|
|
17
|
-
- Phoenix sync attaches feedback as span annotations
|
|
11
|
+
- Phoenix sync attaches feedback as span annotations (async)
|
|
18
12
|
"""
|
|
19
13
|
|
|
20
|
-
from
|
|
21
|
-
|
|
22
|
-
from fastapi import APIRouter, Depends, Header, HTTPException, Query, Request
|
|
14
|
+
from fastapi import APIRouter, Header, HTTPException, Request
|
|
23
15
|
from loguru import logger
|
|
24
16
|
from pydantic import BaseModel, Field
|
|
25
17
|
|
|
26
|
-
from ..deps import
|
|
27
|
-
from ...models.entities import Feedback,
|
|
18
|
+
from ..deps import get_user_id_from_request
|
|
19
|
+
from ...models.entities import Feedback, Message
|
|
28
20
|
from ...services.postgres import Repository
|
|
29
21
|
from ...settings import settings
|
|
30
|
-
from ...utils.date_utils import utc_now
|
|
31
22
|
|
|
32
|
-
router = APIRouter(prefix="/api/v1", tags=["
|
|
23
|
+
router = APIRouter(prefix="/api/v1", tags=["messages"])
|
|
33
24
|
|
|
34
25
|
|
|
35
26
|
# =============================================================================
|
|
@@ -51,10 +42,9 @@ class FeedbackCreateRequest(BaseModel):
|
|
|
51
42
|
description="Rating: -1 (thumbs down), 1 (thumbs up), or 1-5 scale",
|
|
52
43
|
)
|
|
53
44
|
categories: list[str] = Field(
|
|
54
|
-
default_factory=list, description="
|
|
45
|
+
default_factory=list, description="Feedback categories"
|
|
55
46
|
)
|
|
56
47
|
comment: str | None = Field(default=None, description="Free-text comment")
|
|
57
|
-
# Optional trace reference (can be auto-resolved from message)
|
|
58
48
|
trace_id: str | None = Field(
|
|
59
49
|
default=None, description="OTEL trace ID (auto-resolved if message has it)"
|
|
60
50
|
)
|
|
@@ -78,132 +68,12 @@ class FeedbackResponse(BaseModel):
|
|
|
78
68
|
created_at: str
|
|
79
69
|
|
|
80
70
|
|
|
81
|
-
class FeedbackListResponse(BaseModel):
|
|
82
|
-
"""Response for feedback list endpoint."""
|
|
83
|
-
|
|
84
|
-
object: Literal["list"] = "list"
|
|
85
|
-
data: list[Feedback]
|
|
86
|
-
total: int
|
|
87
|
-
has_more: bool
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
class CategoryInfo(BaseModel):
|
|
91
|
-
"""Information about a feedback category."""
|
|
92
|
-
|
|
93
|
-
value: str
|
|
94
|
-
label: str
|
|
95
|
-
description: str
|
|
96
|
-
sentiment: Literal["positive", "negative", "neutral"]
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
class CategoriesResponse(BaseModel):
|
|
100
|
-
"""Response for categories endpoint."""
|
|
101
|
-
|
|
102
|
-
categories: list[CategoryInfo]
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
# =============================================================================
|
|
106
|
-
# Category Definitions
|
|
107
71
|
# =============================================================================
|
|
108
|
-
|
|
109
|
-
CATEGORY_INFO: dict[str, CategoryInfo] = {
|
|
110
|
-
FeedbackCategory.INCOMPLETE.value: CategoryInfo(
|
|
111
|
-
value=FeedbackCategory.INCOMPLETE.value,
|
|
112
|
-
label="Incomplete",
|
|
113
|
-
description="Response lacks expected information",
|
|
114
|
-
sentiment="negative",
|
|
115
|
-
),
|
|
116
|
-
FeedbackCategory.INACCURATE.value: CategoryInfo(
|
|
117
|
-
value=FeedbackCategory.INACCURATE.value,
|
|
118
|
-
label="Inaccurate",
|
|
119
|
-
description="Response contains factual errors",
|
|
120
|
-
sentiment="negative",
|
|
121
|
-
),
|
|
122
|
-
FeedbackCategory.POOR_TONE.value: CategoryInfo(
|
|
123
|
-
value=FeedbackCategory.POOR_TONE.value,
|
|
124
|
-
label="Poor Tone",
|
|
125
|
-
description="Inappropriate or unprofessional tone",
|
|
126
|
-
sentiment="negative",
|
|
127
|
-
),
|
|
128
|
-
FeedbackCategory.OFF_TOPIC.value: CategoryInfo(
|
|
129
|
-
value=FeedbackCategory.OFF_TOPIC.value,
|
|
130
|
-
label="Off Topic",
|
|
131
|
-
description="Response doesn't address the question",
|
|
132
|
-
sentiment="negative",
|
|
133
|
-
),
|
|
134
|
-
FeedbackCategory.TOO_VERBOSE.value: CategoryInfo(
|
|
135
|
-
value=FeedbackCategory.TOO_VERBOSE.value,
|
|
136
|
-
label="Too Verbose",
|
|
137
|
-
description="Unnecessarily long response",
|
|
138
|
-
sentiment="negative",
|
|
139
|
-
),
|
|
140
|
-
FeedbackCategory.TOO_BRIEF.value: CategoryInfo(
|
|
141
|
-
value=FeedbackCategory.TOO_BRIEF.value,
|
|
142
|
-
label="Too Brief",
|
|
143
|
-
description="Insufficiently detailed response",
|
|
144
|
-
sentiment="negative",
|
|
145
|
-
),
|
|
146
|
-
FeedbackCategory.CONFUSING.value: CategoryInfo(
|
|
147
|
-
value=FeedbackCategory.CONFUSING.value,
|
|
148
|
-
label="Confusing",
|
|
149
|
-
description="Hard to understand or unclear",
|
|
150
|
-
sentiment="negative",
|
|
151
|
-
),
|
|
152
|
-
FeedbackCategory.UNSAFE.value: CategoryInfo(
|
|
153
|
-
value=FeedbackCategory.UNSAFE.value,
|
|
154
|
-
label="Unsafe",
|
|
155
|
-
description="Contains potentially harmful content",
|
|
156
|
-
sentiment="negative",
|
|
157
|
-
),
|
|
158
|
-
FeedbackCategory.HELPFUL.value: CategoryInfo(
|
|
159
|
-
value=FeedbackCategory.HELPFUL.value,
|
|
160
|
-
label="Helpful",
|
|
161
|
-
description="Response was useful and addressed the need",
|
|
162
|
-
sentiment="positive",
|
|
163
|
-
),
|
|
164
|
-
FeedbackCategory.EXCELLENT.value: CategoryInfo(
|
|
165
|
-
value=FeedbackCategory.EXCELLENT.value,
|
|
166
|
-
label="Excellent",
|
|
167
|
-
description="Exceptionally good response",
|
|
168
|
-
sentiment="positive",
|
|
169
|
-
),
|
|
170
|
-
FeedbackCategory.ACCURATE.value: CategoryInfo(
|
|
171
|
-
value=FeedbackCategory.ACCURATE.value,
|
|
172
|
-
label="Accurate",
|
|
173
|
-
description="Factually correct and precise",
|
|
174
|
-
sentiment="positive",
|
|
175
|
-
),
|
|
176
|
-
FeedbackCategory.WELL_WRITTEN.value: CategoryInfo(
|
|
177
|
-
value=FeedbackCategory.WELL_WRITTEN.value,
|
|
178
|
-
label="Well Written",
|
|
179
|
-
description="Clear, well-structured response",
|
|
180
|
-
sentiment="positive",
|
|
181
|
-
),
|
|
182
|
-
FeedbackCategory.OTHER.value: CategoryInfo(
|
|
183
|
-
value=FeedbackCategory.OTHER.value,
|
|
184
|
-
label="Other",
|
|
185
|
-
description="Other feedback not covered by categories",
|
|
186
|
-
sentiment="neutral",
|
|
187
|
-
),
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
|
|
72
|
+
# Feedback Endpoint
|
|
191
73
|
# =============================================================================
|
|
192
|
-
# Feedback Endpoints
|
|
193
|
-
# =============================================================================
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
@router.get("/feedback/categories", response_model=CategoriesResponse)
|
|
197
|
-
async def list_categories() -> CategoriesResponse:
|
|
198
|
-
"""
|
|
199
|
-
List available feedback categories.
|
|
200
|
-
|
|
201
|
-
Returns predefined categories with labels, descriptions, and sentiment.
|
|
202
|
-
"""
|
|
203
|
-
return CategoriesResponse(categories=list(CATEGORY_INFO.values()))
|
|
204
74
|
|
|
205
75
|
|
|
206
|
-
@router.post("/feedback", response_model=FeedbackResponse, status_code=201)
|
|
76
|
+
@router.post("/messages/feedback", response_model=FeedbackResponse, status_code=201)
|
|
207
77
|
async def submit_feedback(
|
|
208
78
|
request: Request,
|
|
209
79
|
request_body: FeedbackCreateRequest,
|
|
@@ -219,15 +89,12 @@ async def submit_feedback(
|
|
|
219
89
|
- Provided explicitly in the request
|
|
220
90
|
- Auto-resolved from the message if message_id is provided
|
|
221
91
|
|
|
222
|
-
Phoenix sync happens asynchronously after feedback is stored.
|
|
223
|
-
|
|
224
92
|
Returns:
|
|
225
93
|
Created feedback object
|
|
226
94
|
"""
|
|
227
95
|
if not settings.postgres.enabled:
|
|
228
96
|
raise HTTPException(status_code=503, detail="Database not enabled")
|
|
229
97
|
|
|
230
|
-
# Get effective user_id from auth or anonymous tracking
|
|
231
98
|
effective_user_id = get_user_id_from_request(request)
|
|
232
99
|
|
|
233
100
|
# Resolve trace_id/span_id from message if not provided
|
|
@@ -235,14 +102,12 @@ async def submit_feedback(
|
|
|
235
102
|
span_id = request_body.span_id
|
|
236
103
|
|
|
237
104
|
if request_body.message_id and (not trace_id or not span_id):
|
|
238
|
-
# Try to get trace info from the message
|
|
239
105
|
message_repo = Repository(Message, table_name="messages")
|
|
240
106
|
message = await message_repo.get_by_id(request_body.message_id, x_tenant_id)
|
|
241
107
|
if message:
|
|
242
108
|
trace_id = trace_id or message.trace_id
|
|
243
109
|
span_id = span_id or message.span_id
|
|
244
110
|
|
|
245
|
-
# Create feedback entity
|
|
246
111
|
feedback = Feedback(
|
|
247
112
|
session_id=request_body.session_id,
|
|
248
113
|
message_id=request_body.message_id,
|
|
@@ -257,22 +122,17 @@ async def submit_feedback(
|
|
|
257
122
|
tenant_id=x_tenant_id,
|
|
258
123
|
)
|
|
259
124
|
|
|
260
|
-
# Store feedback (table is "feedbacks" - plural)
|
|
261
125
|
repo = Repository(Feedback, table_name="feedbacks")
|
|
262
126
|
result = await repo.upsert(feedback)
|
|
263
127
|
|
|
264
128
|
logger.info(
|
|
265
129
|
f"Feedback submitted: session={request_body.session_id}, "
|
|
266
|
-
f"message={request_body.message_id}, rating={request_body.rating}
|
|
267
|
-
f"categories={request_body.categories}"
|
|
130
|
+
f"message={request_body.message_id}, rating={request_body.rating}"
|
|
268
131
|
)
|
|
269
132
|
|
|
270
133
|
# TODO: Async sync to Phoenix if trace_id/span_id available
|
|
271
|
-
# This would be done via a background task or queue
|
|
272
134
|
if trace_id and span_id:
|
|
273
135
|
logger.debug(f"Feedback has trace info: trace={trace_id}, span={span_id}")
|
|
274
|
-
# TODO: Queue for Phoenix annotation sync
|
|
275
|
-
# await sync_feedback_to_phoenix(feedback)
|
|
276
136
|
|
|
277
137
|
return FeedbackResponse(
|
|
278
138
|
id=str(result.id),
|
|
@@ -286,170 +146,3 @@ async def submit_feedback(
|
|
|
286
146
|
phoenix_synced=result.phoenix_synced,
|
|
287
147
|
created_at=result.created_at.isoformat() if result.created_at else "",
|
|
288
148
|
)
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
@router.get("/feedback", response_model=FeedbackListResponse)
|
|
292
|
-
async def list_feedback(
|
|
293
|
-
request: Request,
|
|
294
|
-
session_id: str | None = Query(default=None, description="Filter by session ID"),
|
|
295
|
-
message_id: str | None = Query(default=None, description="Filter by message ID"),
|
|
296
|
-
rating: int | None = Query(default=None, description="Filter by rating"),
|
|
297
|
-
category: str | None = Query(default=None, description="Filter by category"),
|
|
298
|
-
phoenix_synced: bool | None = Query(
|
|
299
|
-
default=None, description="Filter by Phoenix sync status"
|
|
300
|
-
),
|
|
301
|
-
limit: int = Query(default=50, ge=1, le=100, description="Max results"),
|
|
302
|
-
offset: int = Query(default=0, ge=0, description="Offset for pagination"),
|
|
303
|
-
x_tenant_id: str = Header(alias="X-Tenant-Id", default="default"),
|
|
304
|
-
) -> FeedbackListResponse:
|
|
305
|
-
"""
|
|
306
|
-
List feedback with optional filters.
|
|
307
|
-
|
|
308
|
-
Access Control:
|
|
309
|
-
- Regular users: Only see feedback they submitted
|
|
310
|
-
- Admin users: Can see all feedback
|
|
311
|
-
|
|
312
|
-
Filters:
|
|
313
|
-
- session_id: Filter by session
|
|
314
|
-
- message_id: Filter by specific message
|
|
315
|
-
- rating: Filter by rating value
|
|
316
|
-
- category: Filter by category (checks if category in list)
|
|
317
|
-
- phoenix_synced: Filter by sync status
|
|
318
|
-
"""
|
|
319
|
-
if not settings.postgres.enabled:
|
|
320
|
-
raise HTTPException(status_code=503, detail="Database not enabled")
|
|
321
|
-
|
|
322
|
-
repo = Repository(Feedback, table_name="feedbacks")
|
|
323
|
-
|
|
324
|
-
# Build user-scoped filters (uses anon_id for anonymous users)
|
|
325
|
-
filters = await get_user_filter(request, x_tenant_id=x_tenant_id)
|
|
326
|
-
|
|
327
|
-
# Apply optional filters
|
|
328
|
-
if session_id:
|
|
329
|
-
filters["session_id"] = session_id
|
|
330
|
-
if message_id:
|
|
331
|
-
filters["message_id"] = message_id
|
|
332
|
-
if rating is not None:
|
|
333
|
-
filters["rating"] = rating
|
|
334
|
-
if phoenix_synced is not None:
|
|
335
|
-
filters["phoenix_synced"] = phoenix_synced
|
|
336
|
-
# TODO: category filter requires array contains query
|
|
337
|
-
|
|
338
|
-
feedback_list = await repo.find(
|
|
339
|
-
filters,
|
|
340
|
-
order_by="created_at DESC",
|
|
341
|
-
limit=limit + 1,
|
|
342
|
-
offset=offset,
|
|
343
|
-
)
|
|
344
|
-
|
|
345
|
-
# Filter by category in Python if specified (until Repository supports array contains)
|
|
346
|
-
if category:
|
|
347
|
-
feedback_list = [f for f in feedback_list if category in f.categories]
|
|
348
|
-
|
|
349
|
-
has_more = len(feedback_list) > limit
|
|
350
|
-
if has_more:
|
|
351
|
-
feedback_list = feedback_list[:limit]
|
|
352
|
-
|
|
353
|
-
total = await repo.count(filters)
|
|
354
|
-
|
|
355
|
-
return FeedbackListResponse(data=feedback_list, total=total, has_more=has_more)
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
@router.get("/feedback/{feedback_id}", response_model=Feedback)
|
|
359
|
-
async def get_feedback(
|
|
360
|
-
request: Request,
|
|
361
|
-
feedback_id: str,
|
|
362
|
-
x_tenant_id: str = Header(alias="X-Tenant-Id", default="default"),
|
|
363
|
-
) -> Feedback:
|
|
364
|
-
"""
|
|
365
|
-
Get specific feedback by ID.
|
|
366
|
-
|
|
367
|
-
Access Control:
|
|
368
|
-
- Regular users: Only access their own feedback
|
|
369
|
-
- Admin users: Can access any feedback
|
|
370
|
-
"""
|
|
371
|
-
if not settings.postgres.enabled:
|
|
372
|
-
raise HTTPException(status_code=503, detail="Database not enabled")
|
|
373
|
-
|
|
374
|
-
repo = Repository(Feedback, table_name="feedbacks")
|
|
375
|
-
feedback = await repo.get_by_id(feedback_id, x_tenant_id)
|
|
376
|
-
|
|
377
|
-
if not feedback:
|
|
378
|
-
raise HTTPException(status_code=404, detail=f"Feedback '{feedback_id}' not found")
|
|
379
|
-
|
|
380
|
-
# Check access
|
|
381
|
-
current_user = get_current_user(request)
|
|
382
|
-
if not is_admin(current_user):
|
|
383
|
-
user_id = current_user.get("id") if current_user else None
|
|
384
|
-
if feedback.user_id and feedback.user_id != user_id:
|
|
385
|
-
raise HTTPException(status_code=403, detail="Access denied: not owner")
|
|
386
|
-
|
|
387
|
-
return feedback
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
# =============================================================================
|
|
391
|
-
# Phoenix Sync (Stub - TODO: Implement background task)
|
|
392
|
-
# =============================================================================
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
async def sync_feedback_to_phoenix(feedback: Feedback) -> bool:
|
|
396
|
-
"""
|
|
397
|
-
Sync feedback to Phoenix as a span annotation.
|
|
398
|
-
|
|
399
|
-
TODO: Implement this as a background task.
|
|
400
|
-
|
|
401
|
-
This should:
|
|
402
|
-
1. Connect to Phoenix client
|
|
403
|
-
2. Resolve trace/span from feedback
|
|
404
|
-
3. Create annotation with feedback data
|
|
405
|
-
4. Update feedback.phoenix_synced = True
|
|
406
|
-
5. Store phoenix_annotation_id
|
|
407
|
-
|
|
408
|
-
Args:
|
|
409
|
-
feedback: Feedback entity to sync
|
|
410
|
-
|
|
411
|
-
Returns:
|
|
412
|
-
True if synced successfully
|
|
413
|
-
"""
|
|
414
|
-
if not feedback.span_id:
|
|
415
|
-
logger.warning(f"Cannot sync feedback {feedback.id}: no span_id")
|
|
416
|
-
return False
|
|
417
|
-
|
|
418
|
-
try:
|
|
419
|
-
# TODO: Import and use Phoenix client
|
|
420
|
-
# from ...services.phoenix import PhoenixClient
|
|
421
|
-
# client = PhoenixClient()
|
|
422
|
-
#
|
|
423
|
-
# # Build annotation from feedback
|
|
424
|
-
# label = None
|
|
425
|
-
# if feedback.categories:
|
|
426
|
-
# label = feedback.categories[0] # Primary category
|
|
427
|
-
#
|
|
428
|
-
# score = None
|
|
429
|
-
# if feedback.rating:
|
|
430
|
-
# # Normalize to 0-1 scale
|
|
431
|
-
# if feedback.rating == -1:
|
|
432
|
-
# score = 0.0
|
|
433
|
-
# elif feedback.rating >= 1 and feedback.rating <= 5:
|
|
434
|
-
# score = feedback.rating / 5.0
|
|
435
|
-
#
|
|
436
|
-
# client.add_span_feedback(
|
|
437
|
-
# span_id=feedback.span_id,
|
|
438
|
-
# annotation_name="user_feedback",
|
|
439
|
-
# annotator_kind=feedback.annotator_kind,
|
|
440
|
-
# label=label,
|
|
441
|
-
# score=score,
|
|
442
|
-
# explanation=feedback.comment,
|
|
443
|
-
# )
|
|
444
|
-
#
|
|
445
|
-
# # Update feedback record
|
|
446
|
-
# feedback.phoenix_synced = True
|
|
447
|
-
# repo = Repository(Feedback, table_name="feedbacks")
|
|
448
|
-
# await repo.update(feedback)
|
|
449
|
-
|
|
450
|
-
logger.info(f"TODO: Sync feedback {feedback.id} to Phoenix span {feedback.span_id}")
|
|
451
|
-
return True
|
|
452
|
-
|
|
453
|
-
except Exception as e:
|
|
454
|
-
logger.error(f"Failed to sync feedback to Phoenix: {e}")
|
|
455
|
-
return False
|