chainlit 1.1.404__py3-none-any.whl → 1.2.0__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.

Files changed (52) hide show
  1. chainlit/__init__.py +53 -305
  2. chainlit/_utils.py +8 -0
  3. chainlit/callbacks.py +308 -0
  4. chainlit/config.py +55 -29
  5. chainlit/copilot/dist/index.js +510 -629
  6. chainlit/data/__init__.py +6 -521
  7. chainlit/data/base.py +121 -0
  8. chainlit/data/dynamodb.py +2 -5
  9. chainlit/data/literalai.py +395 -0
  10. chainlit/data/sql_alchemy.py +10 -9
  11. chainlit/data/storage_clients.py +69 -15
  12. chainlit/data/utils.py +29 -0
  13. chainlit/frontend/dist/assets/{DailyMotion-e665b444.js → DailyMotion-05f4fe48.js} +1 -1
  14. chainlit/frontend/dist/assets/{Facebook-5207db92.js → Facebook-f25411d1.js} +1 -1
  15. chainlit/frontend/dist/assets/{FilePlayer-86937d6e.js → FilePlayer-40ff3414.js} +1 -1
  16. chainlit/frontend/dist/assets/{Kaltura-c96622c1.js → Kaltura-6cbf3897.js} +1 -1
  17. chainlit/frontend/dist/assets/{Mixcloud-57ae3e32.js → Mixcloud-34e7c912.js} +1 -1
  18. chainlit/frontend/dist/assets/{Mux-20373920.js → Mux-8aaff6ac.js} +1 -1
  19. chainlit/frontend/dist/assets/{Preview-c68c0613.js → Preview-2d3bf558.js} +1 -1
  20. chainlit/frontend/dist/assets/{SoundCloud-8a9e3eae.js → SoundCloud-b835f90f.js} +1 -1
  21. chainlit/frontend/dist/assets/{Streamable-1ed099af.js → Streamable-1293e4f3.js} +1 -1
  22. chainlit/frontend/dist/assets/{Twitch-6820039f.js → Twitch-c69660cd.js} +1 -1
  23. chainlit/frontend/dist/assets/{Vidyard-d39ab91d.js → Vidyard-43bda599.js} +1 -1
  24. chainlit/frontend/dist/assets/{Vimeo-017cd9a7.js → Vimeo-54150039.js} +1 -1
  25. chainlit/frontend/dist/assets/{Wistia-a509d9f2.js → Wistia-aa3c721b.js} +1 -1
  26. chainlit/frontend/dist/assets/{YouTube-42dfd82f.js → YouTube-dd0f3cc2.js} +1 -1
  27. chainlit/frontend/dist/assets/index-cf48bedd.js +729 -0
  28. chainlit/frontend/dist/assets/react-plotly-f52a41eb.js +3484 -0
  29. chainlit/frontend/dist/index.html +1 -1
  30. chainlit/langchain/callbacks.py +6 -1
  31. chainlit/llama_index/callbacks.py +20 -4
  32. chainlit/markdown.py +15 -9
  33. chainlit/message.py +0 -1
  34. chainlit/server.py +90 -36
  35. chainlit/session.py +4 -1
  36. chainlit/translations/bn.json +231 -0
  37. chainlit/translations/gu.json +231 -0
  38. chainlit/translations/he-IL.json +231 -0
  39. chainlit/translations/hi.json +231 -0
  40. chainlit/translations/kn.json +231 -0
  41. chainlit/translations/ml.json +231 -0
  42. chainlit/translations/mr.json +231 -0
  43. chainlit/translations/ta.json +231 -0
  44. chainlit/translations/te.json +231 -0
  45. chainlit/utils.py +1 -1
  46. {chainlit-1.1.404.dist-info → chainlit-1.2.0.dist-info}/METADATA +3 -3
  47. chainlit-1.2.0.dist-info/RECORD +96 -0
  48. chainlit/frontend/dist/assets/index-30df9b2b.js +0 -730
  49. chainlit/frontend/dist/assets/react-plotly-5bb34118.js +0 -3602
  50. chainlit-1.1.404.dist-info/RECORD +0 -82
  51. {chainlit-1.1.404.dist-info → chainlit-1.2.0.dist-info}/WHEEL +0 -0
  52. {chainlit-1.1.404.dist-info → chainlit-1.2.0.dist-info}/entry_points.txt +0 -0
chainlit/data/__init__.py CHANGED
@@ -1,534 +1,19 @@
1
- import functools
2
- import json
3
1
  import os
4
- from collections import deque
5
- from typing import (
6
- TYPE_CHECKING,
7
- Any,
8
- Dict,
9
- List,
10
- Literal,
11
- Optional,
12
- Protocol,
13
- Union,
14
- cast,
15
- )
2
+ from typing import Optional
16
3
 
17
- import aiofiles
18
- from chainlit.context import context
19
- from chainlit.logger import logger
20
- from chainlit.session import WebsocketSession
21
- from chainlit.types import (
22
- Feedback,
23
- PageInfo,
24
- PaginatedResponse,
25
- Pagination,
26
- ThreadDict,
27
- ThreadFilter,
4
+ from .base import BaseDataLayer
5
+ from .literalai import LiteralDataLayer
6
+ from .utils import (
7
+ queue_until_user_message as queue_until_user_message, # TODO: Consider deprecating re-export.; Redundant alias tells type checkers to STFU.
28
8
  )
29
- from chainlit.user import PersistedUser, User
30
- from httpx import HTTPStatusError, RequestError
31
- from literalai import Attachment
32
- from literalai import Score as LiteralScore
33
- from literalai import Step as LiteralStep
34
- from literalai.filter import threads_filters as LiteralThreadsFilters
35
- from literalai.step import StepDict as LiteralStepDict
36
-
37
- if TYPE_CHECKING:
38
- from chainlit.element import Element, ElementDict
39
- from chainlit.step import FeedbackDict, StepDict
40
-
41
-
42
- def queue_until_user_message():
43
- def decorator(method):
44
- @functools.wraps(method)
45
- async def wrapper(self, *args, **kwargs):
46
- if (
47
- isinstance(context.session, WebsocketSession)
48
- and not context.session.has_first_interaction
49
- ):
50
- # Queue the method invocation waiting for the first user message
51
- queues = context.session.thread_queues
52
- method_name = method.__name__
53
- if method_name not in queues:
54
- queues[method_name] = deque()
55
- queues[method_name].append((method, self, args, kwargs))
56
-
57
- else:
58
- # Otherwise, Execute the method immediately
59
- return await method(self, *args, **kwargs)
60
-
61
- return wrapper
62
-
63
- return decorator
64
-
65
-
66
- class BaseDataLayer:
67
- """Base class for data persistence."""
68
-
69
- async def get_user(self, identifier: str) -> Optional["PersistedUser"]:
70
- return None
71
-
72
- async def create_user(self, user: "User") -> Optional["PersistedUser"]:
73
- pass
74
-
75
- async def delete_feedback(
76
- self,
77
- feedback_id: str,
78
- ) -> bool:
79
- return True
80
-
81
- async def upsert_feedback(
82
- self,
83
- feedback: Feedback,
84
- ) -> str:
85
- return ""
86
-
87
- @queue_until_user_message()
88
- async def create_element(self, element: "Element"):
89
- pass
90
-
91
- async def get_element(
92
- self, thread_id: str, element_id: str
93
- ) -> Optional["ElementDict"]:
94
- pass
95
-
96
- @queue_until_user_message()
97
- async def delete_element(self, element_id: str, thread_id: Optional[str] = None):
98
- pass
99
-
100
- @queue_until_user_message()
101
- async def create_step(self, step_dict: "StepDict"):
102
- pass
103
-
104
- @queue_until_user_message()
105
- async def update_step(self, step_dict: "StepDict"):
106
- pass
107
-
108
- @queue_until_user_message()
109
- async def delete_step(self, step_id: str):
110
- pass
111
-
112
- async def get_thread_author(self, thread_id: str) -> str:
113
- return ""
114
-
115
- async def delete_thread(self, thread_id: str):
116
- pass
117
-
118
- async def list_threads(
119
- self, pagination: "Pagination", filters: "ThreadFilter"
120
- ) -> "PaginatedResponse[ThreadDict]":
121
- return PaginatedResponse(
122
- data=[],
123
- pageInfo=PageInfo(hasNextPage=False, startCursor=None, endCursor=None),
124
- )
125
-
126
- async def get_thread(self, thread_id: str) -> "Optional[ThreadDict]":
127
- return None
128
-
129
- async def update_thread(
130
- self,
131
- thread_id: str,
132
- name: Optional[str] = None,
133
- user_id: Optional[str] = None,
134
- metadata: Optional[Dict] = None,
135
- tags: Optional[List[str]] = None,
136
- ):
137
- pass
138
-
139
- async def delete_user_session(self, id: str) -> bool:
140
- return True
141
-
142
- async def build_debug_url(self) -> str:
143
- return ""
144
-
145
9
 
146
10
  _data_layer: Optional[BaseDataLayer] = None
147
11
 
148
12
 
149
- class ChainlitDataLayer(BaseDataLayer):
150
- def __init__(self, api_key: str, server: Optional[str]):
151
- from literalai import AsyncLiteralClient
152
-
153
- self.client = AsyncLiteralClient(api_key=api_key, url=server)
154
- logger.info("Chainlit data layer initialized")
155
-
156
- def attachment_to_element_dict(self, attachment: Attachment) -> "ElementDict":
157
- metadata = attachment.metadata or {}
158
- return {
159
- "chainlitKey": None,
160
- "display": metadata.get("display", "side"),
161
- "language": metadata.get("language"),
162
- "autoPlay": metadata.get("autoPlay", None),
163
- "playerConfig": metadata.get("playerConfig", None),
164
- "page": metadata.get("page"),
165
- "size": metadata.get("size"),
166
- "type": metadata.get("type", "file"),
167
- "forId": attachment.step_id,
168
- "id": attachment.id or "",
169
- "mime": attachment.mime,
170
- "name": attachment.name or "",
171
- "objectKey": attachment.object_key,
172
- "url": attachment.url,
173
- "threadId": attachment.thread_id,
174
- }
175
-
176
- def score_to_feedback_dict(
177
- self, score: Optional[LiteralScore]
178
- ) -> "Optional[FeedbackDict]":
179
- if not score:
180
- return None
181
- return {
182
- "id": score.id or "",
183
- "forId": score.step_id or "",
184
- "value": cast(Literal[0, 1], score.value),
185
- "comment": score.comment,
186
- }
187
-
188
- def step_to_step_dict(self, step: LiteralStep) -> "StepDict":
189
- metadata = step.metadata or {}
190
- input = (step.input or {}).get("content") or (
191
- json.dumps(step.input) if step.input and step.input != {} else ""
192
- )
193
- output = (step.output or {}).get("content") or (
194
- json.dumps(step.output) if step.output and step.output != {} else ""
195
- )
196
-
197
- user_feedback = (
198
- next(
199
- (
200
- s
201
- for s in step.scores
202
- if s.type == "HUMAN" and s.name == "user-feedback"
203
- ),
204
- None,
205
- )
206
- if step.scores
207
- else None
208
- )
209
-
210
- return {
211
- "createdAt": step.created_at,
212
- "id": step.id or "",
213
- "threadId": step.thread_id or "",
214
- "parentId": step.parent_id,
215
- "feedback": self.score_to_feedback_dict(user_feedback),
216
- "start": step.start_time,
217
- "end": step.end_time,
218
- "type": step.type or "undefined",
219
- "name": step.name or "",
220
- "generation": step.generation.to_dict() if step.generation else None,
221
- "input": input,
222
- "output": output,
223
- "showInput": metadata.get("showInput", False),
224
- "indent": metadata.get("indent"),
225
- "language": metadata.get("language"),
226
- "isError": bool(step.error),
227
- "waitForAnswer": metadata.get("waitForAnswer", False),
228
- }
229
-
230
- async def build_debug_url(self) -> str:
231
- try:
232
- project_id = await self.client.api.get_my_project_id()
233
- return f"{self.client.api.url}/projects/{project_id}/logs/threads/[thread_id]?currentStepId=[step_id]"
234
- except Exception as e:
235
- logger.error(f"Error building debug url: {e}")
236
- return ""
237
-
238
- async def get_user(self, identifier: str) -> Optional[PersistedUser]:
239
- user = await self.client.api.get_user(identifier=identifier)
240
- if not user:
241
- return None
242
- return PersistedUser(
243
- id=user.id or "",
244
- identifier=user.identifier or "",
245
- metadata=user.metadata,
246
- createdAt=user.created_at or "",
247
- )
248
-
249
- async def create_user(self, user: User) -> Optional[PersistedUser]:
250
- _user = await self.client.api.get_user(identifier=user.identifier)
251
- if not _user:
252
- _user = await self.client.api.create_user(
253
- identifier=user.identifier, metadata=user.metadata
254
- )
255
- elif _user.id:
256
- await self.client.api.update_user(id=_user.id, metadata=user.metadata)
257
- return PersistedUser(
258
- id=_user.id or "",
259
- identifier=_user.identifier or "",
260
- metadata=user.metadata,
261
- createdAt=_user.created_at or "",
262
- )
263
-
264
- async def delete_feedback(
265
- self,
266
- feedback_id: str,
267
- ):
268
- if feedback_id:
269
- await self.client.api.delete_score(
270
- id=feedback_id,
271
- )
272
- return True
273
- return False
274
-
275
- async def upsert_feedback(
276
- self,
277
- feedback: Feedback,
278
- ):
279
- if feedback.id:
280
- await self.client.api.update_score(
281
- id=feedback.id,
282
- update_params={
283
- "comment": feedback.comment,
284
- "value": feedback.value,
285
- },
286
- )
287
- return feedback.id
288
- else:
289
- created = await self.client.api.create_score(
290
- step_id=feedback.forId,
291
- value=feedback.value,
292
- comment=feedback.comment,
293
- name="user-feedback",
294
- type="HUMAN",
295
- )
296
- return created.id or ""
297
-
298
- async def safely_send_steps(self, steps):
299
- try:
300
- await self.client.api.send_steps(steps)
301
- except HTTPStatusError as e:
302
- logger.error(f"HTTP Request: error sending steps: {e.response.status_code}")
303
- except RequestError as e:
304
- logger.error(f"HTTP Request: error for {e.request.url!r}.")
305
-
306
- @queue_until_user_message()
307
- async def create_element(self, element: "Element"):
308
- metadata = {
309
- "size": element.size,
310
- "language": element.language,
311
- "display": element.display,
312
- "type": element.type,
313
- "page": getattr(element, "page", None),
314
- }
315
-
316
- if not element.for_id:
317
- return
318
-
319
- object_key = None
320
-
321
- if not element.url:
322
- if element.path:
323
- async with aiofiles.open(element.path, "rb") as f:
324
- content = await f.read() # type: Union[bytes, str]
325
- elif element.content:
326
- content = element.content
327
- else:
328
- raise ValueError("Either path or content must be provided")
329
- uploaded = await self.client.api.upload_file(
330
- content=content, mime=element.mime, thread_id=element.thread_id
331
- )
332
- object_key = uploaded["object_key"]
333
-
334
- await self.safely_send_steps(
335
- [
336
- {
337
- "id": element.for_id,
338
- "threadId": element.thread_id,
339
- "attachments": [
340
- {
341
- "id": element.id,
342
- "name": element.name,
343
- "metadata": metadata,
344
- "mime": element.mime,
345
- "url": element.url,
346
- "objectKey": object_key,
347
- }
348
- ],
349
- }
350
- ]
351
- )
352
-
353
- async def get_element(
354
- self, thread_id: str, element_id: str
355
- ) -> Optional["ElementDict"]:
356
- attachment = await self.client.api.get_attachment(id=element_id)
357
- if not attachment:
358
- return None
359
- return self.attachment_to_element_dict(attachment)
360
-
361
- @queue_until_user_message()
362
- async def delete_element(self, element_id: str, thread_id: Optional[str] = None):
363
- await self.client.api.delete_attachment(id=element_id)
364
-
365
- @queue_until_user_message()
366
- async def create_step(self, step_dict: "StepDict"):
367
- metadata = dict(
368
- step_dict.get("metadata", {}),
369
- **{
370
- "waitForAnswer": step_dict.get("waitForAnswer"),
371
- "language": step_dict.get("language"),
372
- "showInput": step_dict.get("showInput"),
373
- },
374
- )
375
-
376
- step: LiteralStepDict = {
377
- "createdAt": step_dict.get("createdAt"),
378
- "startTime": step_dict.get("start"),
379
- "endTime": step_dict.get("end"),
380
- "generation": step_dict.get("generation"),
381
- "id": step_dict.get("id"),
382
- "parentId": step_dict.get("parentId"),
383
- "name": step_dict.get("name"),
384
- "threadId": step_dict.get("threadId"),
385
- "type": step_dict.get("type"),
386
- "tags": step_dict.get("tags"),
387
- "metadata": metadata,
388
- }
389
- if step_dict.get("input"):
390
- step["input"] = {"content": step_dict.get("input")}
391
- if step_dict.get("output"):
392
- step["output"] = {"content": step_dict.get("output")}
393
- if step_dict.get("isError"):
394
- step["error"] = step_dict.get("output")
395
-
396
- await self.safely_send_steps([step])
397
-
398
- @queue_until_user_message()
399
- async def update_step(self, step_dict: "StepDict"):
400
- await self.create_step(step_dict)
401
-
402
- @queue_until_user_message()
403
- async def delete_step(self, step_id: str):
404
- await self.client.api.delete_step(id=step_id)
405
-
406
- async def get_thread_author(self, thread_id: str) -> str:
407
- thread = await self.get_thread(thread_id)
408
- if not thread:
409
- return ""
410
- user_identifier = thread.get("userIdentifier")
411
- if not user_identifier:
412
- return ""
413
-
414
- return user_identifier
415
-
416
- async def delete_thread(self, thread_id: str):
417
- await self.client.api.delete_thread(id=thread_id)
418
-
419
- async def list_threads(
420
- self, pagination: "Pagination", filters: "ThreadFilter"
421
- ) -> "PaginatedResponse[ThreadDict]":
422
- if not filters.userId:
423
- raise ValueError("userId is required")
424
-
425
- literal_filters: LiteralThreadsFilters = [
426
- {
427
- "field": "participantId",
428
- "operator": "eq",
429
- "value": filters.userId,
430
- }
431
- ]
432
-
433
- if filters.search:
434
- literal_filters.append(
435
- {
436
- "field": "stepOutput",
437
- "operator": "ilike",
438
- "value": filters.search,
439
- "path": "content",
440
- }
441
- )
442
-
443
- if filters.feedback is not None:
444
- literal_filters.append(
445
- {
446
- "field": "scoreValue",
447
- "operator": "eq",
448
- "value": filters.feedback,
449
- "path": "user-feedback",
450
- }
451
- )
452
-
453
- literal_response = await self.client.api.list_threads(
454
- first=pagination.first,
455
- after=pagination.cursor,
456
- filters=literal_filters,
457
- order_by={"column": "createdAt", "direction": "DESC"},
458
- )
459
- return PaginatedResponse(
460
- pageInfo=PageInfo(
461
- hasNextPage=literal_response.pageInfo.hasNextPage,
462
- startCursor=literal_response.pageInfo.startCursor,
463
- endCursor=literal_response.pageInfo.endCursor,
464
- ),
465
- data=literal_response.data,
466
- )
467
-
468
- async def get_thread(self, thread_id: str) -> "Optional[ThreadDict]":
469
- from chainlit.step import check_add_step_in_cot, stub_step
470
-
471
- thread = await self.client.api.get_thread(id=thread_id)
472
- if not thread:
473
- return None
474
- elements = [] # List[ElementDict]
475
- steps = [] # List[StepDict]
476
- if thread.steps:
477
- for step in thread.steps:
478
- for attachment in step.attachments:
479
- elements.append(self.attachment_to_element_dict(attachment))
480
-
481
- if check_add_step_in_cot(step):
482
- steps.append(self.step_to_step_dict(step))
483
- else:
484
- steps.append(stub_step(step))
485
-
486
- return {
487
- "createdAt": thread.created_at or "",
488
- "id": thread.id,
489
- "name": thread.name or None,
490
- "steps": steps,
491
- "elements": elements,
492
- "metadata": thread.metadata,
493
- "userId": thread.participant_id,
494
- "userIdentifier": thread.participant_identifier,
495
- "tags": thread.tags,
496
- }
497
-
498
- async def update_thread(
499
- self,
500
- thread_id: str,
501
- name: Optional[str] = None,
502
- user_id: Optional[str] = None,
503
- metadata: Optional[Dict] = None,
504
- tags: Optional[List[str]] = None,
505
- ):
506
- await self.client.api.upsert_thread(
507
- id=thread_id,
508
- name=name,
509
- participant_id=user_id,
510
- metadata=metadata,
511
- tags=tags,
512
- )
513
-
514
-
515
- class BaseStorageClient(Protocol):
516
- """Base class for non-text data persistence like Azure Data Lake, S3, Google Storage, etc."""
517
-
518
- async def upload_file(
519
- self,
520
- object_key: str,
521
- data: Union[bytes, str],
522
- mime: str = "application/octet-stream",
523
- overwrite: bool = True,
524
- ) -> Dict[str, Any]:
525
- pass
526
-
527
-
528
13
  if api_key := os.environ.get("LITERAL_API_KEY"):
529
14
  # support legacy LITERAL_SERVER variable as fallback
530
15
  server = os.environ.get("LITERAL_API_URL") or os.environ.get("LITERAL_SERVER")
531
- _data_layer = ChainlitDataLayer(api_key=api_key, server=server)
16
+ _data_layer = LiteralDataLayer(api_key=api_key, server=server)
532
17
 
533
18
 
534
19
  def get_data_layer():
chainlit/data/base.py ADDED
@@ -0,0 +1,121 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
3
+
4
+ from chainlit.types import (
5
+ Feedback,
6
+ PaginatedResponse,
7
+ Pagination,
8
+ ThreadDict,
9
+ ThreadFilter,
10
+ )
11
+
12
+ from .utils import queue_until_user_message
13
+
14
+ if TYPE_CHECKING:
15
+ from chainlit.element import Element, ElementDict
16
+ from chainlit.step import StepDict
17
+ from chainlit.user import PersistedUser, User
18
+
19
+
20
+ class BaseDataLayer(ABC):
21
+ """Base class for data persistence."""
22
+
23
+ @abstractmethod
24
+ async def get_user(self, identifier: str) -> Optional["PersistedUser"]:
25
+ pass
26
+
27
+ @abstractmethod
28
+ async def create_user(self, user: "User") -> Optional["PersistedUser"]:
29
+ pass
30
+
31
+ @abstractmethod
32
+ async def delete_feedback(
33
+ self,
34
+ feedback_id: str,
35
+ ) -> bool:
36
+ pass
37
+
38
+ @abstractmethod
39
+ async def upsert_feedback(
40
+ self,
41
+ feedback: Feedback,
42
+ ) -> str:
43
+ pass
44
+
45
+ @queue_until_user_message()
46
+ @abstractmethod
47
+ async def create_element(self, element: "Element"):
48
+ pass
49
+
50
+ @abstractmethod
51
+ async def get_element(
52
+ self, thread_id: str, element_id: str
53
+ ) -> Optional["ElementDict"]:
54
+ pass
55
+
56
+ @queue_until_user_message()
57
+ @abstractmethod
58
+ async def delete_element(self, element_id: str, thread_id: Optional[str] = None):
59
+ pass
60
+
61
+ @queue_until_user_message()
62
+ @abstractmethod
63
+ async def create_step(self, step_dict: "StepDict"):
64
+ pass
65
+
66
+ @queue_until_user_message()
67
+ @abstractmethod
68
+ async def update_step(self, step_dict: "StepDict"):
69
+ pass
70
+
71
+ @queue_until_user_message()
72
+ @abstractmethod
73
+ async def delete_step(self, step_id: str):
74
+ pass
75
+
76
+ @abstractmethod
77
+ async def get_thread_author(self, thread_id: str) -> str:
78
+ return ""
79
+
80
+ @abstractmethod
81
+ async def delete_thread(self, thread_id: str):
82
+ pass
83
+
84
+ @abstractmethod
85
+ async def list_threads(
86
+ self, pagination: "Pagination", filters: "ThreadFilter"
87
+ ) -> "PaginatedResponse[ThreadDict]":
88
+ pass
89
+
90
+ @abstractmethod
91
+ async def get_thread(self, thread_id: str) -> "Optional[ThreadDict]":
92
+ pass
93
+
94
+ @abstractmethod
95
+ async def update_thread(
96
+ self,
97
+ thread_id: str,
98
+ name: Optional[str] = None,
99
+ user_id: Optional[str] = None,
100
+ metadata: Optional[Dict] = None,
101
+ tags: Optional[List[str]] = None,
102
+ ):
103
+ pass
104
+
105
+ @abstractmethod
106
+ async def build_debug_url(self) -> str:
107
+ pass
108
+
109
+
110
+ class BaseStorageClient(ABC):
111
+ """Base class for non-text data persistence like Azure Data Lake, S3, Google Storage, etc."""
112
+
113
+ @abstractmethod
114
+ async def upload_file(
115
+ self,
116
+ object_key: str,
117
+ data: Union[bytes, str],
118
+ mime: str = "application/octet-stream",
119
+ overwrite: bool = True,
120
+ ) -> Dict[str, Any]:
121
+ pass
chainlit/data/dynamodb.py CHANGED
@@ -12,7 +12,8 @@ import aiohttp
12
12
  import boto3 # type: ignore
13
13
  from boto3.dynamodb.types import TypeDeserializer, TypeSerializer
14
14
  from chainlit.context import context
15
- from chainlit.data import BaseDataLayer, BaseStorageClient, queue_until_user_message
15
+ from chainlit.data.base import BaseDataLayer, BaseStorageClient
16
+ from chainlit.data.utils import queue_until_user_message
16
17
  from chainlit.element import ElementDict
17
18
  from chainlit.logger import logger
18
19
  from chainlit.step import StepDict
@@ -36,7 +37,6 @@ _logger.setLevel(logging.WARNING)
36
37
 
37
38
 
38
39
  class DynamoDBDataLayer(BaseDataLayer):
39
-
40
40
  def __init__(
41
41
  self,
42
42
  table_name: str,
@@ -579,8 +579,5 @@ class DynamoDBDataLayer(BaseDataLayer):
579
579
  updates=item,
580
580
  )
581
581
 
582
- async def delete_user_session(self, id: str) -> bool:
583
- return True # Not sure why documentation wants this
584
-
585
582
  async def build_debug_url(self) -> str:
586
583
  return ""