chainlit 1.3.2__py3-none-any.whl → 2.0.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 (82) hide show
  1. chainlit/__init__.py +58 -56
  2. chainlit/action.py +12 -10
  3. chainlit/{auth.py → auth/__init__.py} +24 -34
  4. chainlit/auth/cookie.py +123 -0
  5. chainlit/auth/jwt.py +37 -0
  6. chainlit/cache.py +4 -6
  7. chainlit/callbacks.py +65 -11
  8. chainlit/chat_context.py +2 -2
  9. chainlit/chat_settings.py +3 -1
  10. chainlit/cli/__init__.py +15 -2
  11. chainlit/config.py +46 -90
  12. chainlit/context.py +4 -3
  13. chainlit/copilot/dist/index.js +8608 -642
  14. chainlit/data/__init__.py +96 -8
  15. chainlit/data/acl.py +3 -2
  16. chainlit/data/base.py +1 -15
  17. chainlit/data/chainlit_data_layer.py +584 -0
  18. chainlit/data/dynamodb.py +7 -4
  19. chainlit/data/literalai.py +4 -6
  20. chainlit/data/sql_alchemy.py +9 -8
  21. chainlit/data/storage_clients/__init__.py +0 -0
  22. chainlit/data/{storage_clients.py → storage_clients/azure.py} +2 -33
  23. chainlit/data/storage_clients/azure_blob.py +80 -0
  24. chainlit/data/storage_clients/base.py +22 -0
  25. chainlit/data/storage_clients/gcs.py +78 -0
  26. chainlit/data/storage_clients/s3.py +49 -0
  27. chainlit/discord/__init__.py +4 -4
  28. chainlit/discord/app.py +2 -1
  29. chainlit/element.py +41 -9
  30. chainlit/emitter.py +37 -16
  31. chainlit/frontend/dist/assets/{DailyMotion-Bq4wFES6.js → DailyMotion-DgRzV5GZ.js} +1 -1
  32. chainlit/frontend/dist/assets/Dataframe-DVgwSMU2.js +22 -0
  33. chainlit/frontend/dist/assets/{Facebook-CHEgeJDe.js → Facebook-C0vx6HWv.js} +1 -1
  34. chainlit/frontend/dist/assets/{FilePlayer-BMFA6He5.js → FilePlayer-CdhzeHPP.js} +1 -1
  35. chainlit/frontend/dist/assets/{Kaltura-BS4Q0SKd.js → Kaltura-5iVmeUct.js} +1 -1
  36. chainlit/frontend/dist/assets/{Mixcloud-tLlgZy_i.js → Mixcloud-C2zi77Ex.js} +1 -1
  37. chainlit/frontend/dist/assets/{Mux-Bcz0qNhS.js → Mux-Vkebogdf.js} +1 -1
  38. chainlit/frontend/dist/assets/{Preview-RsJjlwJx.js → Preview-DwY_sEIl.js} +1 -1
  39. chainlit/frontend/dist/assets/{SoundCloud-B9UgR7Bk.js → SoundCloud-CREBXAWo.js} +1 -1
  40. chainlit/frontend/dist/assets/{Streamable-BOgIqbui.js → Streamable-B5Lu25uy.js} +1 -1
  41. chainlit/frontend/dist/assets/{Twitch-CBX_d6nV.js → Twitch-y9iKCcM1.js} +1 -1
  42. chainlit/frontend/dist/assets/{Vidyard-C5HPuozf.js → Vidyard-ClYvcuEu.js} +1 -1
  43. chainlit/frontend/dist/assets/{Vimeo-CHBmywi9.js → Vimeo-D6HvM2jt.js} +1 -1
  44. chainlit/frontend/dist/assets/Wistia-Cu4zZ2Ci.js +1 -0
  45. chainlit/frontend/dist/assets/{YouTube-CA7t0q0j.js → YouTube-D10tR6CJ.js} +1 -1
  46. chainlit/frontend/dist/assets/index-CI4qFOt5.js +8665 -0
  47. chainlit/frontend/dist/assets/index-CrrqM0nZ.css +1 -0
  48. chainlit/frontend/dist/assets/{react-plotly-Ba2Cl614.js → react-plotly-BpxUS-ab.js} +1 -1
  49. chainlit/frontend/dist/index.html +2 -2
  50. chainlit/haystack/callbacks.py +5 -4
  51. chainlit/input_widget.py +6 -4
  52. chainlit/langchain/callbacks.py +56 -47
  53. chainlit/langflow/__init__.py +1 -0
  54. chainlit/llama_index/callbacks.py +7 -7
  55. chainlit/message.py +8 -10
  56. chainlit/mistralai/__init__.py +3 -2
  57. chainlit/oauth_providers.py +70 -3
  58. chainlit/openai/__init__.py +3 -2
  59. chainlit/secret.py +1 -1
  60. chainlit/server.py +481 -182
  61. chainlit/session.py +7 -5
  62. chainlit/slack/__init__.py +3 -3
  63. chainlit/slack/app.py +3 -2
  64. chainlit/socket.py +89 -112
  65. chainlit/step.py +12 -12
  66. chainlit/sync.py +2 -1
  67. chainlit/teams/__init__.py +3 -3
  68. chainlit/teams/app.py +1 -0
  69. chainlit/translations/en-US.json +2 -1
  70. chainlit/translations/nl-NL.json +229 -0
  71. chainlit/types.py +24 -8
  72. chainlit/user.py +2 -1
  73. chainlit/utils.py +3 -2
  74. chainlit/version.py +3 -2
  75. {chainlit-1.3.2.dist-info → chainlit-2.0.0.dist-info}/METADATA +15 -35
  76. chainlit-2.0.0.dist-info/RECORD +106 -0
  77. chainlit/frontend/dist/assets/Wistia-1Gb23ljh.js +0 -1
  78. chainlit/frontend/dist/assets/index-CwmincdQ.css +0 -1
  79. chainlit/frontend/dist/assets/index-DnjoDoLU.js +0 -723
  80. chainlit-1.3.2.dist-info/RECORD +0 -96
  81. {chainlit-1.3.2.dist-info → chainlit-2.0.0.dist-info}/WHEEL +0 -0
  82. {chainlit-1.3.2.dist-info → chainlit-2.0.0.dist-info}/entry_points.txt +0 -0
chainlit/session.py CHANGED
@@ -6,6 +6,7 @@ import uuid
6
6
  from typing import TYPE_CHECKING, Any, Callable, Deque, Dict, Literal, Optional, Union
7
7
 
8
8
  import aiofiles
9
+
9
10
  from chainlit.logger import logger
10
11
  from chainlit.types import FileReference
11
12
 
@@ -17,9 +18,9 @@ ClientType = Literal["webapp", "copilot", "teams", "slack", "discord"]
17
18
 
18
19
 
19
20
  class JSONEncoderIgnoreNonSerializable(json.JSONEncoder):
20
- def default(self, obj):
21
+ def default(self, o):
21
22
  try:
22
- return super(JSONEncoderIgnoreNonSerializable, self).default(obj)
23
+ return super().default(o)
23
24
  except TypeError:
24
25
  return None
25
26
 
@@ -112,9 +113,10 @@ class BaseSession:
112
113
 
113
114
  if path:
114
115
  # Copy the file from the given path
115
- async with aiofiles.open(path, "rb") as src, aiofiles.open(
116
- file_path, "wb"
117
- ) as dst:
116
+ async with (
117
+ aiofiles.open(path, "rb") as src,
118
+ aiofiles.open(file_path, "wb") as dst,
119
+ ):
118
120
  await dst.write(await src.read())
119
121
  elif content:
120
122
  # Write the provided content to the file
@@ -1,6 +1,6 @@
1
- try:
2
- import slack_bolt
3
- except ModuleNotFoundError:
1
+ import importlib.util
2
+
3
+ if importlib.util.find_spec("slack_bolt") is None:
4
4
  raise ValueError(
5
5
  "The slack_bolt package is required to integrate Chainlit with a Slack app. Run `pip install slack_bolt --upgrade`"
6
6
  )
chainlit/slack/app.py CHANGED
@@ -7,6 +7,9 @@ from functools import partial
7
7
  from typing import Dict, List, Optional, Union
8
8
 
9
9
  import httpx
10
+ from slack_bolt.adapter.fastapi.async_handler import AsyncSlackRequestHandler
11
+ from slack_bolt.async_app import AsyncApp
12
+
10
13
  from chainlit.config import config
11
14
  from chainlit.context import ChainlitContext, HTTPSession, context, context_var
12
15
  from chainlit.data import get_data_layer
@@ -18,8 +21,6 @@ from chainlit.telemetry import trace
18
21
  from chainlit.types import Feedback
19
22
  from chainlit.user import PersistedUser, User
20
23
  from chainlit.user_session import user_session
21
- from slack_bolt.adapter.fastapi.async_handler import AsyncSlackRequestHandler
22
- from slack_bolt.async_app import AsyncApp
23
24
 
24
25
 
25
26
  class SlackEmitter(BaseChainlitEmitter):
chainlit/socket.py CHANGED
@@ -1,30 +1,28 @@
1
1
  import asyncio
2
2
  import json
3
3
  import time
4
- import uuid
5
- from typing import Any, Dict, Literal
4
+ from typing import Any, Dict, Literal, Optional, Tuple, Union
6
5
  from urllib.parse import unquote
7
6
 
8
- from chainlit.action import Action
7
+ from starlette.requests import cookie_parser
8
+ from typing_extensions import TypeAlias
9
+
9
10
  from chainlit.auth import get_current_user, require_login
10
11
  from chainlit.chat_context import chat_context
11
12
  from chainlit.config import config
12
13
  from chainlit.context import init_ws_context
13
14
  from chainlit.data import get_data_layer
14
- from chainlit.element import Element
15
15
  from chainlit.logger import logger
16
16
  from chainlit.message import ErrorMessage, Message
17
17
  from chainlit.server import sio
18
18
  from chainlit.session import WebsocketSession
19
19
  from chainlit.telemetry import trace_event
20
- from chainlit.types import (
21
- AudioChunk,
22
- AudioChunkPayload,
23
- AudioEndPayload,
24
- MessagePayload,
25
- )
20
+ from chainlit.types import InputAudioChunk, InputAudioChunkPayload, MessagePayload
21
+ from chainlit.user import PersistedUser, User
26
22
  from chainlit.user_session import user_sessions
27
23
 
24
+ WSGIEnvironment: TypeAlias = dict[str, Any]
25
+
28
26
 
29
27
  def restore_existing_session(sid, session_id, emit_fn, emit_call_fn):
30
28
  """Restore a session from the sessionId provided by the client."""
@@ -83,45 +81,44 @@ def load_user_env(user_env):
83
81
  return user_env
84
82
 
85
83
 
86
- def build_anon_user_identifier(environ):
87
- scope = environ.get("asgi.scope", {})
88
- client_ip, _ = scope.get("client")
89
- ip = environ.get("HTTP_X_FORWARDED_FOR", client_ip)
84
+ def _get_token_from_cookie(environ: WSGIEnvironment) -> Optional[str]:
85
+ if cookie_header := environ.get("HTTP_COOKIE", None):
86
+ cookies = cookie_parser(cookie_header)
87
+ return cookies.get("access_token", None)
90
88
 
91
- try:
92
- headers = scope.get("headers", {})
93
- user_agent = next(
94
- (v.decode("utf-8") for k, v in headers if k.decode("utf-8") == "user-agent")
95
- )
96
- return str(uuid.uuid5(uuid.NAMESPACE_DNS, user_agent + ip))
97
-
98
- except StopIteration:
99
- return str(uuid.uuid5(uuid.NAMESPACE_DNS, ip))
100
-
101
-
102
- @sio.on("connect")
103
- async def connect(sid, environ):
104
- if (
105
- not config.code.on_chat_start
106
- and not config.code.on_message
107
- and not config.code.on_audio_chunk
108
- ):
109
- logger.warning(
110
- "You need to configure at least one of on_chat_start, on_message or on_audio_chunk callback"
111
- )
112
- return False
113
- user = None
114
- token = None
115
- login_required = require_login()
116
- try:
117
- # Check if the authentication is required
118
- if login_required:
119
- authorization_header = environ.get("HTTP_AUTHORIZATION")
120
- token = authorization_header.split(" ")[1] if authorization_header else None
121
- user = await get_current_user(token=token)
122
- except Exception:
123
- logger.info("Authentication failed")
124
- return False
89
+ return None
90
+
91
+
92
+ def _get_token(environ: WSGIEnvironment, auth: dict) -> Optional[str]:
93
+ """Take WSGI environ, return access token."""
94
+ return _get_token_from_cookie(environ)
95
+
96
+
97
+ async def _authenticate_connection(
98
+ environ,
99
+ auth,
100
+ ) -> Union[Tuple[Union[User, PersistedUser], str], Tuple[None, None]]:
101
+ if token := _get_token(environ, auth):
102
+ user = await get_current_user(token=token)
103
+ if user:
104
+ return user, token
105
+
106
+ return None, None
107
+
108
+
109
+ @sio.on("connect") # pyright: ignore [reportOptionalCall]
110
+ async def connect(sid, environ, auth):
111
+ user = token = None
112
+
113
+ if require_login():
114
+ try:
115
+ user, token = await _authenticate_connection(environ, auth)
116
+ except Exception as e:
117
+ logger.exception("Exception authenticating connection: %s", e)
118
+
119
+ if not user:
120
+ logger.error("Authentication failed in websocket connect.")
121
+ raise ConnectionRefusedError("authentication failed")
125
122
 
126
123
  # Session scoped function to emit to the client
127
124
  def emit_fn(event, data):
@@ -131,16 +128,16 @@ async def connect(sid, environ):
131
128
  def emit_call_fn(event: Literal["ask", "call_fn"], data, timeout):
132
129
  return sio.call(event, data, timeout=timeout, to=sid)
133
130
 
134
- session_id = environ.get("HTTP_X_CHAINLIT_SESSION_ID")
131
+ session_id = auth.get("sessionId")
135
132
  if restore_existing_session(sid, session_id, emit_fn, emit_call_fn):
136
133
  return True
137
134
 
138
- user_env_string = environ.get("HTTP_USER_ENV")
135
+ user_env_string = auth.get("userEnv")
139
136
  user_env = load_user_env(user_env_string)
140
137
 
141
- client_type = environ.get("HTTP_X_CHAINLIT_CLIENT_TYPE")
138
+ client_type = auth.get("clientType")
142
139
  http_referer = environ.get("HTTP_REFERER")
143
- url_encoded_chat_profile = environ.get("HTTP_X_CHAINLIT_CHAT_PROFILE")
140
+ url_encoded_chat_profile = auth.get("chatProfile")
144
141
  chat_profile = (
145
142
  unquote(url_encoded_chat_profile) if url_encoded_chat_profile else None
146
143
  )
@@ -155,7 +152,7 @@ async def connect(sid, environ):
155
152
  user=user,
156
153
  token=token,
157
154
  chat_profile=chat_profile,
158
- thread_id=environ.get("HTTP_X_CHAINLIT_THREAD_ID"),
155
+ thread_id=auth.get("threadId"),
159
156
  languages=environ.get("HTTP_ACCEPT_LANGUAGE"),
160
157
  http_referer=http_referer,
161
158
  )
@@ -164,17 +161,17 @@ async def connect(sid, environ):
164
161
  return True
165
162
 
166
163
 
167
- @sio.on("connection_successful")
164
+ @sio.on("connection_successful") # pyright: ignore [reportOptionalCall]
168
165
  async def connection_successful(sid):
169
166
  context = init_ws_context(sid)
170
167
 
171
- if context.session.restored:
172
- return
173
-
174
168
  await context.emitter.task_end()
175
169
  await context.emitter.clear("clear_ask")
176
170
  await context.emitter.clear("clear_call_fn")
177
171
 
172
+ if context.session.restored:
173
+ return
174
+
178
175
  if context.session.thread_id_to_resume and config.code.on_chat_resume:
179
176
  thread = await resume_thread(context.session)
180
177
  if thread:
@@ -197,14 +194,14 @@ async def connection_successful(sid):
197
194
  context.session.current_task = task
198
195
 
199
196
 
200
- @sio.on("clear_session")
197
+ @sio.on("clear_session") # pyright: ignore [reportOptionalCall]
201
198
  async def clean_session(sid):
202
199
  session = WebsocketSession.get(sid)
203
200
  if session:
204
201
  session.to_clear = True
205
202
 
206
203
 
207
- @sio.on("disconnect")
204
+ @sio.on("disconnect") # pyright: ignore [reportOptionalCall]
208
205
  async def disconnect(sid):
209
206
  session = WebsocketSession.get(sid)
210
207
 
@@ -238,7 +235,7 @@ async def disconnect(sid):
238
235
  asyncio.ensure_future(clear_on_timeout(sid))
239
236
 
240
237
 
241
- @sio.on("stop")
238
+ @sio.on("stop") # pyright: ignore [reportOptionalCall]
242
239
  async def stop(sid):
243
240
  if session := WebsocketSession.get(sid):
244
241
  trace_event("stop_task")
@@ -275,7 +272,7 @@ async def process_message(session: WebsocketSession, payload: MessagePayload):
275
272
  await context.emitter.task_end()
276
273
 
277
274
 
278
- @sio.on("edit_message")
275
+ @sio.on("edit_message") # pyright: ignore [reportOptionalCall]
279
276
  async def edit_message(sid, payload: MessagePayload):
280
277
  """Handle a message sent by the User."""
281
278
  session = WebsocketSession.require(sid)
@@ -305,7 +302,7 @@ async def edit_message(sid, payload: MessagePayload):
305
302
  await context.emitter.task_end()
306
303
 
307
304
 
308
- @sio.on("client_message")
305
+ @sio.on("client_message") # pyright: ignore [reportOptionalCall]
309
306
  async def message(sid, payload: MessagePayload):
310
307
  """Handle a message sent by the User."""
311
308
  session = WebsocketSession.require(sid)
@@ -314,19 +311,44 @@ async def message(sid, payload: MessagePayload):
314
311
  session.current_task = task
315
312
 
316
313
 
314
+ @sio.on("window_message") # pyright: ignore [reportOptionalCall]
315
+ async def window_message(sid, data):
316
+ """Handle a message send by the host window."""
317
+ session = WebsocketSession.require(sid)
318
+ init_ws_context(session)
319
+
320
+ if config.code.on_window_message:
321
+ try:
322
+ await config.code.on_window_message(data)
323
+ except asyncio.CancelledError:
324
+ pass
325
+
326
+
327
+ @sio.on("audio_start") # pyright: ignore [reportOptionalCall]
328
+ async def audio_start(sid):
329
+ """Handle audio init."""
330
+ session = WebsocketSession.require(sid)
331
+
332
+ context = init_ws_context(session)
333
+ if config.code.on_audio_start:
334
+ connected = bool(await config.code.on_audio_start())
335
+ connection_state = "on" if connected else "off"
336
+ await context.emitter.update_audio_connection(connection_state)
337
+
338
+
317
339
  @sio.on("audio_chunk")
318
- async def audio_chunk(sid, payload: AudioChunkPayload):
340
+ async def audio_chunk(sid, payload: InputAudioChunkPayload):
319
341
  """Handle an audio chunk sent by the user."""
320
342
  session = WebsocketSession.require(sid)
321
343
 
322
344
  init_ws_context(session)
323
345
 
324
346
  if config.code.on_audio_chunk:
325
- asyncio.create_task(config.code.on_audio_chunk(AudioChunk(**payload)))
347
+ asyncio.create_task(config.code.on_audio_chunk(InputAudioChunk(**payload)))
326
348
 
327
349
 
328
350
  @sio.on("audio_end")
329
- async def audio_end(sid, payload: AudioEndPayload):
351
+ async def audio_end(sid):
330
352
  """Handle the end of the audio stream."""
331
353
  session = WebsocketSession.require(sid)
332
354
  try:
@@ -337,18 +359,9 @@ async def audio_end(sid, payload: AudioEndPayload):
337
359
  session.has_first_interaction = True
338
360
  asyncio.create_task(context.emitter.init_thread("audio"))
339
361
 
340
- file_elements = []
341
362
  if config.code.on_audio_end:
342
- file_refs = payload.get("fileReferences")
343
- if file_refs:
344
- files = [
345
- session.files[file["id"]]
346
- for file in file_refs
347
- if file["id"] in session.files
348
- ]
349
- file_elements = [Element.from_dict(file) for file in files]
350
-
351
- await config.code.on_audio_end(file_elements)
363
+ await config.code.on_audio_end()
364
+
352
365
  except asyncio.CancelledError:
353
366
  pass
354
367
  except Exception as e:
@@ -360,42 +373,6 @@ async def audio_end(sid, payload: AudioEndPayload):
360
373
  await context.emitter.task_end()
361
374
 
362
375
 
363
- async def process_action(action: Action):
364
- callback = config.code.action_callbacks.get(action.name)
365
- if callback:
366
- res = await callback(action)
367
- return res
368
- else:
369
- logger.warning("No callback found for action %s", action.name)
370
-
371
-
372
- @sio.on("action_call")
373
- async def call_action(sid, action):
374
- """Handle an action call from the UI."""
375
- context = init_ws_context(sid)
376
-
377
- action = Action(**action)
378
-
379
- try:
380
- if not context.session.has_first_interaction:
381
- context.session.has_first_interaction = True
382
- asyncio.create_task(context.emitter.init_thread(action.name))
383
- res = await process_action(action)
384
- await context.emitter.send_action_response(
385
- id=action.id, status=True, response=res if isinstance(res, str) else None
386
- )
387
-
388
- except asyncio.CancelledError:
389
- await context.emitter.send_action_response(
390
- id=action.id, status=False, response="Action interrupted by the user"
391
- )
392
- except Exception as e:
393
- logger.exception(e)
394
- await context.emitter.send_action_response(
395
- id=action.id, status=False, response="An error occurred"
396
- )
397
-
398
-
399
376
  @sio.on("chat_settings_change")
400
377
  async def change_settings(sid, settings: Dict[str, Any]):
401
378
  """Handle change settings submit from the UI."""
chainlit/step.py CHANGED
@@ -7,6 +7,10 @@ from copy import deepcopy
7
7
  from functools import wraps
8
8
  from typing import Callable, Dict, List, Optional, TypedDict, Union
9
9
 
10
+ from literalai import BaseGeneration
11
+ from literalai.helper import utc_now
12
+ from literalai.observability.step import StepType, TrueStepType
13
+
10
14
  from chainlit.config import config
11
15
  from chainlit.context import CL_RUN_NAMES, context, local_steps
12
16
  from chainlit.data import get_data_layer
@@ -14,9 +18,6 @@ from chainlit.element import Element
14
18
  from chainlit.logger import logger
15
19
  from chainlit.telemetry import trace_event
16
20
  from chainlit.types import FeedbackDict
17
- from literalai import BaseGeneration
18
- from literalai.helper import utc_now
19
- from literalai.observability.step import StepType, TrueStepType
20
21
 
21
22
 
22
23
  def check_add_step_in_cot(step: "Step"):
@@ -61,7 +62,6 @@ class StepDict(TypedDict, total=False):
61
62
  generation: Optional[Dict]
62
63
  showInput: Optional[Union[bool, str]]
63
64
  language: Optional[str]
64
- indent: Optional[int]
65
65
  feedback: Optional[FeedbackDict]
66
66
 
67
67
 
@@ -107,8 +107,8 @@ def step(
107
107
  ) as step:
108
108
  try:
109
109
  step.input = flatten_args_kwargs(func, args, kwargs)
110
- except:
111
- pass
110
+ except Exception as e:
111
+ logger.exception(e)
112
112
  result = await func(*args, **kwargs)
113
113
  try:
114
114
  if result and not step.output:
@@ -134,13 +134,13 @@ def step(
134
134
  ) as step:
135
135
  try:
136
136
  step.input = flatten_args_kwargs(func, args, kwargs)
137
- except:
138
- pass
137
+ except Exception as e:
138
+ logger.exception(e)
139
139
  result = func(*args, **kwargs)
140
140
  try:
141
141
  if result and not step.output:
142
142
  step.output = result
143
- except:
143
+ except Exception as e:
144
144
  step.is_error = True
145
145
  step.output = str(e)
146
146
  return result
@@ -317,7 +317,7 @@ class Step:
317
317
  except Exception as e:
318
318
  if self.fail_on_persist_error:
319
319
  raise e
320
- logger.error(f"Failed to persist step update: {str(e)}")
320
+ logger.error(f"Failed to persist step update: {e!s}")
321
321
 
322
322
  tasks = [el.send(for_id=self.id) for el in self.elements]
323
323
  await asyncio.gather(*tasks)
@@ -344,7 +344,7 @@ class Step:
344
344
  except Exception as e:
345
345
  if self.fail_on_persist_error:
346
346
  raise e
347
- logger.error(f"Failed to persist step deletion: {str(e)}")
347
+ logger.error(f"Failed to persist step deletion: {e!s}")
348
348
 
349
349
  await context.emitter.delete_step(step_dict)
350
350
 
@@ -371,7 +371,7 @@ class Step:
371
371
  except Exception as e:
372
372
  if self.fail_on_persist_error:
373
373
  raise e
374
- logger.error(f"Failed to persist step creation: {str(e)}")
374
+ logger.error(f"Failed to persist step creation: {e!s}")
375
375
 
376
376
  tasks = [el.send(for_id=self.id) for el in self.elements]
377
377
  await asyncio.gather(*tasks)
chainlit/sync.py CHANGED
@@ -10,9 +10,10 @@ import asyncio
10
10
  import threading
11
11
 
12
12
  from asyncer import asyncify
13
- from chainlit.context import context_var
14
13
  from syncer import sync
15
14
 
15
+ from chainlit.context import context_var
16
+
16
17
  make_async = asyncify
17
18
 
18
19
  T_Retval = TypeVar("T_Retval")
@@ -1,6 +1,6 @@
1
- try:
2
- import botbuilder
3
- except ModuleNotFoundError:
1
+ import importlib.util
2
+
3
+ if importlib.util.find_spec("botbuilder") is None:
4
4
  raise ValueError(
5
5
  "The botbuilder-core package is required to integrate Chainlit with a Slack app. Run `pip install botbuilder-core --upgrade`"
6
6
  )
chainlit/teams/app.py CHANGED
@@ -28,6 +28,7 @@ from botbuilder.schema import (
28
28
  ChannelAccount,
29
29
  HeroCard,
30
30
  )
31
+
31
32
  from chainlit.config import config
32
33
  from chainlit.context import ChainlitContext, HTTPSession, context, context_var
33
34
  from chainlit.data import get_data_layer
@@ -124,7 +124,8 @@
124
124
  },
125
125
  "speechButton": {
126
126
  "start": "Start recording",
127
- "stop": "Stop recording"
127
+ "stop": "Stop recording",
128
+ "loading": "Connecting"
128
129
  },
129
130
  "SubmitButton": {
130
131
  "sendMessage": "Send message",