chainlit 1.0.401__py3-none-any.whl → 2.0.3__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 (112) hide show
  1. chainlit/__init__.py +98 -279
  2. chainlit/_utils.py +8 -0
  3. chainlit/action.py +12 -10
  4. chainlit/{auth.py → auth/__init__.py} +28 -36
  5. chainlit/auth/cookie.py +122 -0
  6. chainlit/auth/jwt.py +39 -0
  7. chainlit/cache.py +4 -6
  8. chainlit/callbacks.py +362 -0
  9. chainlit/chat_context.py +64 -0
  10. chainlit/chat_settings.py +3 -1
  11. chainlit/cli/__init__.py +77 -8
  12. chainlit/config.py +181 -101
  13. chainlit/context.py +42 -13
  14. chainlit/copilot/dist/index.js +8750 -903
  15. chainlit/data/__init__.py +101 -416
  16. chainlit/data/acl.py +6 -2
  17. chainlit/data/base.py +107 -0
  18. chainlit/data/chainlit_data_layer.py +608 -0
  19. chainlit/data/dynamodb.py +590 -0
  20. chainlit/data/literalai.py +500 -0
  21. chainlit/data/sql_alchemy.py +721 -0
  22. chainlit/data/storage_clients/__init__.py +0 -0
  23. chainlit/data/storage_clients/azure.py +81 -0
  24. chainlit/data/storage_clients/azure_blob.py +89 -0
  25. chainlit/data/storage_clients/base.py +26 -0
  26. chainlit/data/storage_clients/gcs.py +88 -0
  27. chainlit/data/storage_clients/s3.py +75 -0
  28. chainlit/data/utils.py +29 -0
  29. chainlit/discord/__init__.py +6 -0
  30. chainlit/discord/app.py +354 -0
  31. chainlit/element.py +91 -33
  32. chainlit/emitter.py +80 -29
  33. chainlit/frontend/dist/assets/DailyMotion-C_XC7xJI.js +1 -0
  34. chainlit/frontend/dist/assets/Dataframe-Cs4l4hA1.js +22 -0
  35. chainlit/frontend/dist/assets/Facebook-CUeCH7hk.js +1 -0
  36. chainlit/frontend/dist/assets/FilePlayer-CB-fYkx8.js +1 -0
  37. chainlit/frontend/dist/assets/Kaltura-YX6qaq72.js +1 -0
  38. chainlit/frontend/dist/assets/Mixcloud-DGV0ldjP.js +1 -0
  39. chainlit/frontend/dist/assets/Mux-CmRss5oc.js +1 -0
  40. chainlit/frontend/dist/assets/Preview-DBVJn7-H.js +1 -0
  41. chainlit/frontend/dist/assets/SoundCloud-qLUb18oY.js +1 -0
  42. chainlit/frontend/dist/assets/Streamable-BvYP7bFp.js +1 -0
  43. chainlit/frontend/dist/assets/Twitch-CTHt-sGZ.js +1 -0
  44. chainlit/frontend/dist/assets/Vidyard-B-0mCJbm.js +1 -0
  45. chainlit/frontend/dist/assets/Vimeo-Dnp7ri8q.js +1 -0
  46. chainlit/frontend/dist/assets/Wistia-DW0x_UBn.js +1 -0
  47. chainlit/frontend/dist/assets/YouTube--98FipvA.js +1 -0
  48. chainlit/frontend/dist/assets/index-D71nZ46o.js +8665 -0
  49. chainlit/frontend/dist/assets/index-g8LTJwwr.css +1 -0
  50. chainlit/frontend/dist/assets/react-plotly-Cn_BQTQw.js +3484 -0
  51. chainlit/frontend/dist/index.html +2 -4
  52. chainlit/haystack/callbacks.py +4 -7
  53. chainlit/input_widget.py +8 -4
  54. chainlit/langchain/callbacks.py +103 -68
  55. chainlit/langflow/__init__.py +1 -0
  56. chainlit/llama_index/callbacks.py +65 -40
  57. chainlit/markdown.py +22 -6
  58. chainlit/message.py +54 -56
  59. chainlit/mistralai/__init__.py +50 -0
  60. chainlit/oauth_providers.py +266 -8
  61. chainlit/openai/__init__.py +10 -18
  62. chainlit/secret.py +1 -1
  63. chainlit/server.py +789 -228
  64. chainlit/session.py +108 -90
  65. chainlit/slack/__init__.py +6 -0
  66. chainlit/slack/app.py +397 -0
  67. chainlit/socket.py +199 -116
  68. chainlit/step.py +141 -89
  69. chainlit/sync.py +2 -1
  70. chainlit/teams/__init__.py +6 -0
  71. chainlit/teams/app.py +338 -0
  72. chainlit/translations/bn.json +235 -0
  73. chainlit/translations/en-US.json +83 -4
  74. chainlit/translations/gu.json +235 -0
  75. chainlit/translations/he-IL.json +235 -0
  76. chainlit/translations/hi.json +235 -0
  77. chainlit/translations/kn.json +235 -0
  78. chainlit/translations/ml.json +235 -0
  79. chainlit/translations/mr.json +235 -0
  80. chainlit/translations/nl-NL.json +233 -0
  81. chainlit/translations/ta.json +235 -0
  82. chainlit/translations/te.json +235 -0
  83. chainlit/translations/zh-CN.json +233 -0
  84. chainlit/translations.py +60 -0
  85. chainlit/types.py +133 -28
  86. chainlit/user.py +14 -3
  87. chainlit/user_session.py +6 -3
  88. chainlit/utils.py +52 -5
  89. chainlit/version.py +3 -2
  90. {chainlit-1.0.401.dist-info → chainlit-2.0.3.dist-info}/METADATA +48 -50
  91. chainlit-2.0.3.dist-info/RECORD +106 -0
  92. chainlit/cli/utils.py +0 -24
  93. chainlit/frontend/dist/assets/index-9711593e.js +0 -723
  94. chainlit/frontend/dist/assets/index-d088547c.css +0 -1
  95. chainlit/frontend/dist/assets/react-plotly-d8762cc2.js +0 -3602
  96. chainlit/playground/__init__.py +0 -2
  97. chainlit/playground/config.py +0 -40
  98. chainlit/playground/provider.py +0 -108
  99. chainlit/playground/providers/__init__.py +0 -13
  100. chainlit/playground/providers/anthropic.py +0 -118
  101. chainlit/playground/providers/huggingface.py +0 -75
  102. chainlit/playground/providers/langchain.py +0 -89
  103. chainlit/playground/providers/openai.py +0 -408
  104. chainlit/playground/providers/vertexai.py +0 -171
  105. chainlit/translations/pt-BR.json +0 -155
  106. chainlit-1.0.401.dist-info/RECORD +0 -66
  107. /chainlit/copilot/dist/assets/{logo_dark-2a3cf740.svg → logo_dark-IkGJ_IwC.svg} +0 -0
  108. /chainlit/copilot/dist/assets/{logo_light-b078e7bc.svg → logo_light-Bb_IPh6r.svg} +0 -0
  109. /chainlit/frontend/dist/assets/{logo_dark-2a3cf740.svg → logo_dark-IkGJ_IwC.svg} +0 -0
  110. /chainlit/frontend/dist/assets/{logo_light-b078e7bc.svg → logo_light-Bb_IPh6r.svg} +0 -0
  111. {chainlit-1.0.401.dist-info → chainlit-2.0.3.dist-info}/WHEEL +0 -0
  112. {chainlit-1.0.401.dist-info → chainlit-2.0.3.dist-info}/entry_points.txt +0 -0
chainlit/slack/app.py ADDED
@@ -0,0 +1,397 @@
1
+ import asyncio
2
+ import os
3
+ import re
4
+ import uuid
5
+ from datetime import datetime
6
+ from functools import partial
7
+ from typing import Dict, List, Optional, Union
8
+
9
+ import httpx
10
+ from slack_bolt.adapter.fastapi.async_handler import AsyncSlackRequestHandler
11
+ from slack_bolt.async_app import AsyncApp
12
+
13
+ from chainlit.config import config
14
+ from chainlit.context import ChainlitContext, HTTPSession, context, context_var
15
+ from chainlit.data import get_data_layer
16
+ from chainlit.element import Element, ElementDict
17
+ from chainlit.emitter import BaseChainlitEmitter
18
+ from chainlit.logger import logger
19
+ from chainlit.message import Message, StepDict
20
+ from chainlit.telemetry import trace
21
+ from chainlit.types import Feedback
22
+ from chainlit.user import PersistedUser, User
23
+ from chainlit.user_session import user_session
24
+
25
+
26
+ class SlackEmitter(BaseChainlitEmitter):
27
+ def __init__(
28
+ self,
29
+ session: HTTPSession,
30
+ app: AsyncApp,
31
+ channel_id: str,
32
+ say,
33
+ thread_ts: Optional[str] = None,
34
+ ):
35
+ super().__init__(session)
36
+ self.app = app
37
+ self.channel_id = channel_id
38
+ self.say = say
39
+ self.thread_ts = thread_ts
40
+
41
+ async def send_element(self, element_dict: ElementDict):
42
+ if element_dict.get("display") != "inline":
43
+ return
44
+
45
+ persisted_file = self.session.files.get(element_dict.get("chainlitKey") or "")
46
+ file: Optional[Union[bytes, str]] = None
47
+
48
+ if persisted_file:
49
+ file = str(persisted_file["path"])
50
+ elif file_url := element_dict.get("url"):
51
+ async with httpx.AsyncClient() as client:
52
+ response = await client.get(file_url)
53
+ if response.status_code == 200:
54
+ file = response.content
55
+
56
+ if not file:
57
+ return
58
+
59
+ await self.app.client.files_upload_v2(
60
+ channel=self.channel_id,
61
+ thread_ts=self.thread_ts,
62
+ file=file,
63
+ title=element_dict.get("name"),
64
+ )
65
+
66
+ async def send_step(self, step_dict: StepDict):
67
+ step_type = step_dict.get("type")
68
+ is_assistant_message = step_type == "assistant_message"
69
+ is_empty_output = not step_dict.get("output")
70
+
71
+ if is_empty_output or not is_assistant_message:
72
+ return
73
+
74
+ enable_feedback = get_data_layer()
75
+ blocks: List[Dict] = [
76
+ {
77
+ "type": "section",
78
+ "text": {"type": "mrkdwn", "text": step_dict["output"]},
79
+ }
80
+ ]
81
+ if enable_feedback:
82
+ current_run = context.current_run
83
+ scorable_id = current_run.id if current_run else step_dict.get("id")
84
+ blocks.append(
85
+ {
86
+ "type": "actions",
87
+ "elements": [
88
+ {
89
+ "action_id": "thumbdown",
90
+ "type": "button",
91
+ "text": {
92
+ "type": "plain_text",
93
+ "emoji": True,
94
+ "text": ":thumbsdown:",
95
+ },
96
+ "value": scorable_id,
97
+ },
98
+ {
99
+ "action_id": "thumbup",
100
+ "type": "button",
101
+ "text": {
102
+ "type": "plain_text",
103
+ "emoji": True,
104
+ "text": ":thumbsup:",
105
+ },
106
+ "value": scorable_id,
107
+ },
108
+ ],
109
+ }
110
+ )
111
+ await self.say(
112
+ text=step_dict["output"], blocks=blocks, thread_ts=self.thread_ts
113
+ )
114
+
115
+ async def update_step(self, step_dict: StepDict):
116
+ is_assistant_message = step_dict["type"] == "assistant_message"
117
+
118
+ if not is_assistant_message:
119
+ return
120
+
121
+ await self.send_step(step_dict)
122
+
123
+
124
+ slack_app = AsyncApp(
125
+ token=os.environ.get("SLACK_BOT_TOKEN"),
126
+ signing_secret=os.environ.get("SLACK_SIGNING_SECRET"),
127
+ )
128
+
129
+
130
+ @trace
131
+ def init_slack_context(
132
+ session: HTTPSession,
133
+ slack_channel_id: str,
134
+ event,
135
+ say,
136
+ thread_ts: Optional[str] = None,
137
+ ) -> ChainlitContext:
138
+ emitter = SlackEmitter(
139
+ session=session,
140
+ app=slack_app,
141
+ channel_id=slack_channel_id,
142
+ say=say,
143
+ thread_ts=thread_ts,
144
+ )
145
+ context = ChainlitContext(session=session, emitter=emitter)
146
+ context_var.set(context)
147
+ user_session.set("slack_event", event)
148
+ user_session.set(
149
+ "fetch_slack_message_history",
150
+ partial(
151
+ fetch_message_history, channel_id=slack_channel_id, thread_ts=thread_ts
152
+ ),
153
+ )
154
+ return context
155
+
156
+
157
+ slack_app_handler = AsyncSlackRequestHandler(slack_app)
158
+
159
+ users_by_slack_id: Dict[str, Union[User, PersistedUser]] = {}
160
+
161
+ USER_PREFIX = "slack_"
162
+
163
+
164
+ def clean_content(message: str):
165
+ cleaned_text = re.sub(r"<@[\w]+>", "", message).strip()
166
+ return cleaned_text
167
+
168
+
169
+ async def get_user(slack_user_id: str):
170
+ if slack_user_id in users_by_slack_id:
171
+ return users_by_slack_id[slack_user_id]
172
+
173
+ slack_user = await slack_app.client.users_info(user=slack_user_id)
174
+ slack_user_profile = slack_user["user"]["profile"]
175
+
176
+ user_identifier = slack_user_profile.get("email") or slack_user_id
177
+ user = User(identifier=USER_PREFIX + user_identifier, metadata=slack_user_profile)
178
+
179
+ users_by_slack_id[slack_user_id] = user
180
+
181
+ if data_layer := get_data_layer():
182
+ try:
183
+ persisted_user = await data_layer.create_user(user)
184
+ if persisted_user:
185
+ users_by_slack_id[slack_user_id] = persisted_user
186
+ except Exception as e:
187
+ logger.error(f"Error creating user: {e}")
188
+
189
+ return users_by_slack_id[slack_user_id]
190
+
191
+
192
+ async def fetch_message_history(
193
+ channel_id: str, thread_ts: Optional[str] = None, limit=30
194
+ ):
195
+ if not thread_ts:
196
+ result = await slack_app.client.conversations_history(
197
+ channel=channel_id, limit=limit
198
+ )
199
+ else:
200
+ result = await slack_app.client.conversations_replies(
201
+ channel=channel_id, ts=thread_ts, limit=limit
202
+ )
203
+ if result["ok"]:
204
+ messages = result["messages"]
205
+ return messages
206
+ else:
207
+ raise Exception(f"Failed to fetch messages: {result['error']}")
208
+
209
+
210
+ async def download_slack_file(url, token):
211
+ headers = {"Authorization": f"Bearer {token}"}
212
+ async with httpx.AsyncClient() as client:
213
+ response = await client.get(url, headers=headers)
214
+ if response.status_code == 200:
215
+ return response.content
216
+ else:
217
+ return None
218
+
219
+
220
+ async def download_slack_files(session: HTTPSession, files, token):
221
+ download_coros = [
222
+ download_slack_file(file.get("url_private"), token) for file in files
223
+ ]
224
+ file_bytes_list = await asyncio.gather(*download_coros)
225
+ file_refs = []
226
+ for idx, file_bytes in enumerate(file_bytes_list):
227
+ if file_bytes:
228
+ name = files[idx].get("name")
229
+ mime_type = files[idx].get("mimetype")
230
+ file_ref = await session.persist_file(
231
+ name=name, mime=mime_type, content=file_bytes
232
+ )
233
+ file_refs.append(file_ref)
234
+
235
+ files_dicts = [
236
+ session.files[file["id"]] for file in file_refs if file["id"] in session.files
237
+ ]
238
+
239
+ file_elements = [Element.from_dict(file_dict) for file_dict in files_dicts]
240
+
241
+ return file_elements
242
+
243
+
244
+ async def process_slack_message(
245
+ event,
246
+ say,
247
+ thread_id: str,
248
+ thread_name: Optional[str] = None,
249
+ bind_thread_to_user=False,
250
+ thread_ts: Optional[str] = None,
251
+ ):
252
+ user = await get_user(event["user"])
253
+
254
+ channel_id = event["channel"]
255
+
256
+ text = event.get("text")
257
+ slack_files = event.get("files", [])
258
+
259
+ session_id = str(uuid.uuid4())
260
+ session = HTTPSession(
261
+ id=session_id,
262
+ thread_id=thread_id,
263
+ user=user,
264
+ client_type="slack",
265
+ )
266
+
267
+ ctx = init_slack_context(
268
+ session=session,
269
+ slack_channel_id=channel_id,
270
+ event=event,
271
+ say=say,
272
+ thread_ts=thread_ts,
273
+ )
274
+
275
+ file_elements = await download_slack_files(
276
+ session, slack_files, slack_app.client.token
277
+ )
278
+
279
+ if on_chat_start := config.code.on_chat_start:
280
+ await on_chat_start()
281
+
282
+ msg = Message(
283
+ content=clean_content(text),
284
+ elements=file_elements,
285
+ type="user_message",
286
+ author=user.metadata.get("real_name"),
287
+ )
288
+
289
+ await msg.send()
290
+
291
+ if on_message := config.code.on_message:
292
+ await on_message(msg)
293
+
294
+ if on_chat_end := config.code.on_chat_end:
295
+ await on_chat_end()
296
+
297
+ if data_layer := get_data_layer():
298
+ user_id = None
299
+ if isinstance(user, PersistedUser):
300
+ user_id = user.id if bind_thread_to_user else None
301
+
302
+ try:
303
+ await data_layer.update_thread(
304
+ thread_id=thread_id,
305
+ name=thread_name or msg.content,
306
+ metadata=ctx.session.to_persistable(),
307
+ user_id=user_id,
308
+ )
309
+ except Exception as e:
310
+ logger.error(f"Error updating thread: {e}")
311
+
312
+ ctx.session.delete()
313
+
314
+
315
+ @slack_app.event("app_home_opened")
316
+ async def handle_app_home_opened(event, say):
317
+ pass
318
+
319
+
320
+ @slack_app.event("app_mention")
321
+ async def handle_app_mentions(event, say):
322
+ thread_ts = event.get("thread_ts", event["ts"])
323
+ thread_id = str(uuid.uuid5(uuid.NAMESPACE_DNS, thread_ts))
324
+
325
+ await process_slack_message(event, say, thread_id=thread_id, thread_ts=thread_ts)
326
+
327
+
328
+ @slack_app.event("message")
329
+ async def handle_message(message, say):
330
+ thread_id = str(
331
+ uuid.uuid5(
332
+ uuid.NAMESPACE_DNS,
333
+ message["channel"] + datetime.today().strftime("%Y-%m-%d"),
334
+ )
335
+ )
336
+ thread_ts = message.get("thread_ts", message["ts"])
337
+ thread_id = str(uuid.uuid5(uuid.NAMESPACE_DNS, thread_ts))
338
+
339
+ await process_slack_message(
340
+ event=message,
341
+ say=say,
342
+ thread_id=thread_id,
343
+ bind_thread_to_user=True,
344
+ thread_ts=thread_ts,
345
+ )
346
+
347
+
348
+ @slack_app.block_action("thumbdown")
349
+ async def thumb_down(ack, context, body):
350
+ await ack()
351
+ step_id = body["actions"][0]["value"]
352
+
353
+ if data_layer := get_data_layer():
354
+ feedback = Feedback(forId=step_id, value=0)
355
+ await data_layer.upsert_feedback(feedback)
356
+
357
+ text = body["message"]["text"]
358
+ blocks = body["message"]["blocks"]
359
+ updated_blocks = [block for block in blocks if block["type"] != "actions"]
360
+ updated_blocks.append(
361
+ {
362
+ "type": "section",
363
+ "text": {"type": "mrkdwn", "text": ":thumbsdown: Feedback received."},
364
+ }
365
+ )
366
+ await context.client.chat_update(
367
+ channel=body["channel"]["id"],
368
+ ts=body["container"]["message_ts"],
369
+ text=text,
370
+ blocks=updated_blocks,
371
+ )
372
+
373
+
374
+ @slack_app.block_action("thumbup")
375
+ async def thumb_up(ack, context, body):
376
+ await ack()
377
+ step_id = body["actions"][0]["value"]
378
+
379
+ if data_layer := get_data_layer():
380
+ feedback = Feedback(forId=step_id, value=1)
381
+ await data_layer.upsert_feedback(feedback)
382
+
383
+ text = body["message"]["text"]
384
+ blocks = body["message"]["blocks"]
385
+ updated_blocks = [block for block in blocks if block["type"] != "actions"]
386
+ updated_blocks.append(
387
+ {
388
+ "type": "section",
389
+ "text": {"type": "mrkdwn", "text": ":thumbsup: Feedback received."},
390
+ }
391
+ )
392
+ await context.client.chat_update(
393
+ channel=body["channel"]["id"],
394
+ ts=body["container"]["message_ts"],
395
+ text=text,
396
+ blocks=updated_blocks,
397
+ )