chainlit 1.0.400__py3-none-any.whl → 1.0.500__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 chainlit might be problematic. Click here for more details.

chainlit/data/__init__.py CHANGED
@@ -2,7 +2,7 @@ import functools
2
2
  import json
3
3
  import os
4
4
  from collections import deque
5
- from typing import TYPE_CHECKING, Dict, List, Optional, Union
5
+ from typing import TYPE_CHECKING, Dict, List, Literal, Optional, Union, cast
6
6
 
7
7
  import aiofiles
8
8
  from chainlit.config import config
@@ -11,20 +11,16 @@ from chainlit.logger import logger
11
11
  from chainlit.session import WebsocketSession
12
12
  from chainlit.types import Feedback, Pagination, ThreadDict, ThreadFilter
13
13
  from chainlit.user import PersistedUser, User, UserDict
14
- from literalai import Attachment
15
- from literalai import Feedback as ClientFeedback
16
- from literalai import PageInfo, PaginatedResponse
17
- from literalai import Step as ClientStep
18
- from literalai.step import StepDict as ClientStepDict
19
- from literalai.thread import NumberListFilter, StringFilter, StringListFilter
20
- from literalai.thread import ThreadFilter as ClientThreadFilter
14
+ from literalai import Attachment, PageInfo, PaginatedResponse
15
+ from literalai import Score as LiteralScore
16
+ from literalai import Step as LiteralStep
17
+ from literalai.filter import threads_filters as LiteralThreadsFilters
18
+ from literalai.step import StepDict as LiteralStepDict
21
19
 
22
20
  if TYPE_CHECKING:
23
21
  from chainlit.element import Element, ElementDict
24
22
  from chainlit.step import FeedbackDict, StepDict
25
23
 
26
- _data_layer = None
27
-
28
24
 
29
25
  def queue_until_user_message():
30
26
  def decorator(method):
@@ -59,6 +55,12 @@ class BaseDataLayer:
59
55
  async def create_user(self, user: "User") -> Optional["PersistedUser"]:
60
56
  pass
61
57
 
58
+ async def delete_feedback(
59
+ self,
60
+ feedback_id: str,
61
+ ) -> bool:
62
+ return True
63
+
62
64
  async def upsert_feedback(
63
65
  self,
64
66
  feedback: Feedback,
@@ -66,7 +68,7 @@ class BaseDataLayer:
66
68
  return ""
67
69
 
68
70
  @queue_until_user_message()
69
- async def create_element(self, element_dict: "ElementDict"):
71
+ async def create_element(self, element: "Element"):
70
72
  pass
71
73
 
72
74
  async def get_element(
@@ -100,7 +102,8 @@ class BaseDataLayer:
100
102
  self, pagination: "Pagination", filters: "ThreadFilter"
101
103
  ) -> "PaginatedResponse[ThreadDict]":
102
104
  return PaginatedResponse(
103
- data=[], pageInfo=PageInfo(hasNextPage=False, endCursor=None)
105
+ data=[],
106
+ pageInfo=PageInfo(hasNextPage=False, startCursor=None, endCursor=None),
104
107
  )
105
108
 
106
109
  async def get_thread(self, thread_id: str) -> "Optional[ThreadDict]":
@@ -120,7 +123,10 @@ class BaseDataLayer:
120
123
  return True
121
124
 
122
125
 
123
- class ChainlitDataLayer:
126
+ _data_layer: Optional[BaseDataLayer] = None
127
+
128
+
129
+ class ChainlitDataLayer(BaseDataLayer):
124
130
  def __init__(self, api_key: str, server: Optional[str]):
125
131
  from literalai import LiteralClient
126
132
 
@@ -145,20 +151,19 @@ class ChainlitDataLayer:
145
151
  "threadId": attachment.thread_id,
146
152
  }
147
153
 
148
- def feedback_to_feedback_dict(
149
- self, feedback: Optional[ClientFeedback]
154
+ def score_to_feedback_dict(
155
+ self, score: Optional[LiteralScore]
150
156
  ) -> "Optional[FeedbackDict]":
151
- if not feedback:
157
+ if not score:
152
158
  return None
153
159
  return {
154
- "id": feedback.id or "",
155
- "forId": feedback.step_id or "",
156
- "value": feedback.value or 0, # type: ignore
157
- "comment": feedback.comment,
158
- "strategy": "BINARY",
160
+ "id": score.id or "",
161
+ "forId": score.step_id or "",
162
+ "value": cast(Literal[0, 1], score.value),
163
+ "comment": score.comment,
159
164
  }
160
165
 
161
- def step_to_step_dict(self, step: ClientStep) -> "StepDict":
166
+ def step_to_step_dict(self, step: LiteralStep) -> "StepDict":
162
167
  metadata = step.metadata or {}
163
168
  input = (step.input or {}).get("content") or (
164
169
  json.dumps(step.input) if step.input and step.input != {} else ""
@@ -166,12 +171,26 @@ class ChainlitDataLayer:
166
171
  output = (step.output or {}).get("content") or (
167
172
  json.dumps(step.output) if step.output and step.output != {} else ""
168
173
  )
174
+
175
+ user_feedback = (
176
+ next(
177
+ (
178
+ s
179
+ for s in step.scores
180
+ if s.type == "HUMAN" and s.name == "user-feedback"
181
+ ),
182
+ None,
183
+ )
184
+ if step.scores
185
+ else None
186
+ )
187
+
169
188
  return {
170
189
  "createdAt": step.created_at,
171
190
  "id": step.id or "",
172
191
  "threadId": step.thread_id or "",
173
192
  "parentId": step.parent_id,
174
- "feedback": self.feedback_to_feedback_dict(step.feedback),
193
+ "feedback": self.score_to_feedback_dict(user_feedback),
175
194
  "start": step.start_time,
176
195
  "end": step.end_time,
177
196
  "type": step.type or "undefined",
@@ -185,7 +204,6 @@ class ChainlitDataLayer:
185
204
  "language": metadata.get("language"),
186
205
  "isError": metadata.get("isError", False),
187
206
  "waitForAnswer": metadata.get("waitForAnswer", False),
188
- "feedback": self.feedback_to_feedback_dict(step.feedback),
189
207
  }
190
208
 
191
209
  async def get_user(self, identifier: str) -> Optional[PersistedUser]:
@@ -214,26 +232,37 @@ class ChainlitDataLayer:
214
232
  createdAt=_user.created_at or "",
215
233
  )
216
234
 
235
+ async def delete_feedback(
236
+ self,
237
+ feedback_id: str,
238
+ ):
239
+ if feedback_id:
240
+ await self.client.api.delete_score(
241
+ id=feedback_id,
242
+ )
243
+ return True
244
+ return False
245
+
217
246
  async def upsert_feedback(
218
247
  self,
219
248
  feedback: Feedback,
220
249
  ):
221
250
  if feedback.id:
222
- await self.client.api.update_feedback(
251
+ await self.client.api.update_score(
223
252
  id=feedback.id,
224
253
  update_params={
225
254
  "comment": feedback.comment,
226
- "strategy": feedback.strategy,
227
255
  "value": feedback.value,
228
256
  },
229
257
  )
230
258
  return feedback.id
231
259
  else:
232
- created = await self.client.api.create_feedback(
260
+ created = await self.client.api.create_score(
233
261
  step_id=feedback.forId,
234
262
  value=feedback.value,
235
263
  comment=feedback.comment,
236
- strategy=feedback.strategy,
264
+ name="user-feedback",
265
+ type="HUMAN",
237
266
  )
238
267
  return created.id or ""
239
268
 
@@ -306,7 +335,7 @@ class ChainlitDataLayer:
306
335
  "showInput": step_dict.get("showInput"),
307
336
  }
308
337
 
309
- step: ClientStepDict = {
338
+ step: LiteralStepDict = {
310
339
  "createdAt": step_dict.get("createdAt"),
311
340
  "startTime": step_dict.get("start"),
312
341
  "endTime": step_dict.get("end"),
@@ -337,10 +366,11 @@ class ChainlitDataLayer:
337
366
  thread = await self.get_thread(thread_id)
338
367
  if not thread:
339
368
  return ""
340
- user = thread.get("user")
341
- if not user:
369
+ user_identifier = thread.get("userIdentifier")
370
+ if not user_identifier:
342
371
  return ""
343
- return user.get("identifier") or ""
372
+
373
+ return user_identifier
344
374
 
345
375
  async def delete_thread(self, thread_id: str):
346
376
  await self.client.api.delete_thread(id=thread_id)
@@ -348,22 +378,42 @@ class ChainlitDataLayer:
348
378
  async def list_threads(
349
379
  self, pagination: "Pagination", filters: "ThreadFilter"
350
380
  ) -> "PaginatedResponse[ThreadDict]":
351
- if not filters.userIdentifier:
352
- raise ValueError("userIdentifier is required")
381
+ if not filters.userId:
382
+ raise ValueError("userId is required")
383
+
384
+ literal_filters: LiteralThreadsFilters = [
385
+ {
386
+ "field": "participantId",
387
+ "operator": "eq",
388
+ "value": filters.userId,
389
+ }
390
+ ]
353
391
 
354
- client_filters = ClientThreadFilter(
355
- participantsIdentifier=StringListFilter(
356
- operator="in", value=[filters.userIdentifier]
357
- ),
358
- )
359
392
  if filters.search:
360
- client_filters.search = StringFilter(operator="ilike", value=filters.search)
361
- if filters.feedback:
362
- client_filters.feedbacksValue = NumberListFilter(
363
- operator="in", value=[filters.feedback]
393
+ literal_filters.append(
394
+ {
395
+ "field": "stepOutput",
396
+ "operator": "ilike",
397
+ "value": filters.search,
398
+ "path": "content",
399
+ }
400
+ )
401
+
402
+ if filters.feedback is not None:
403
+ literal_filters.append(
404
+ {
405
+ "field": "scoreValue",
406
+ "operator": "eq",
407
+ "value": filters.feedback,
408
+ "path": "user-feedback",
409
+ }
364
410
  )
411
+
365
412
  return await self.client.api.list_threads(
366
- first=pagination.first, after=pagination.cursor, filters=client_filters
413
+ first=pagination.first,
414
+ after=pagination.cursor,
415
+ filters=literal_filters,
416
+ order_by={"column": "createdAt", "direction": "DESC"},
367
417
  )
368
418
 
369
419
  async def get_thread(self, thread_id: str) -> "Optional[ThreadDict]":
@@ -382,15 +432,6 @@ class ChainlitDataLayer:
382
432
  step.generation = None
383
433
  steps.append(self.step_to_step_dict(step))
384
434
 
385
- user = None # type: Optional["UserDict"]
386
-
387
- if thread.user:
388
- user = {
389
- "id": thread.user.id or "",
390
- "identifier": thread.user.identifier or "",
391
- "metadata": thread.user.metadata,
392
- }
393
-
394
435
  return {
395
436
  "createdAt": thread.created_at or "",
396
437
  "id": thread.id,
@@ -398,7 +439,8 @@ class ChainlitDataLayer:
398
439
  "steps": steps,
399
440
  "elements": elements,
400
441
  "metadata": thread.metadata,
401
- "user": user,
442
+ "userId": thread.participant_id,
443
+ "userIdentifier": thread.participant_identifier,
402
444
  "tags": thread.tags,
403
445
  }
404
446