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