chainlit 1.2.0__py3-none-any.whl → 1.3.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 (60) hide show
  1. chainlit/auth.py +9 -7
  2. chainlit/cli/__init__.py +3 -1
  3. chainlit/context.py +4 -6
  4. chainlit/copilot/dist/index.js +252 -261
  5. chainlit/data/__init__.py +11 -6
  6. chainlit/data/dynamodb.py +10 -6
  7. chainlit/data/literalai.py +168 -59
  8. chainlit/data/sql_alchemy.py +91 -16
  9. chainlit/element.py +3 -2
  10. chainlit/frontend/dist/assets/DailyMotion-CwoOhIL8.js +1 -0
  11. chainlit/frontend/dist/assets/Facebook-BhnGXlzq.js +1 -0
  12. chainlit/frontend/dist/assets/FilePlayer-CPSVT6fz.js +1 -0
  13. chainlit/frontend/dist/assets/Kaltura-COYaLzsL.js +1 -0
  14. chainlit/frontend/dist/assets/Mixcloud-JdadNiQ5.js +1 -0
  15. chainlit/frontend/dist/assets/Mux-CBN7RO2u.js +1 -0
  16. chainlit/frontend/dist/assets/Preview-CxAFvvjV.js +1 -0
  17. chainlit/frontend/dist/assets/SoundCloud-JlgmASWm.js +1 -0
  18. chainlit/frontend/dist/assets/Streamable-CUWgr6Zw.js +1 -0
  19. chainlit/frontend/dist/assets/Twitch-BiN1HEDM.js +1 -0
  20. chainlit/frontend/dist/assets/Vidyard-qhPmrhDm.js +1 -0
  21. chainlit/frontend/dist/assets/Vimeo-CrZVSCaT.js +1 -0
  22. chainlit/frontend/dist/assets/Wistia-C891KrBP.js +1 -0
  23. chainlit/frontend/dist/assets/YouTube-DKjw5Hbn.js +1 -0
  24. chainlit/frontend/dist/assets/index-DLRdQOIx.js +723 -0
  25. chainlit/frontend/dist/assets/{react-plotly-f52a41eb.js → react-plotly-Dpmqg5Sy.js} +94 -94
  26. chainlit/frontend/dist/index.html +2 -3
  27. chainlit/haystack/callbacks.py +1 -3
  28. chainlit/langchain/callbacks.py +1 -2
  29. chainlit/message.py +1 -1
  30. chainlit/mistralai/__init__.py +0 -1
  31. chainlit/oauth_providers.py +46 -3
  32. chainlit/openai/__init__.py +3 -5
  33. chainlit/server.py +19 -9
  34. chainlit/session.py +8 -19
  35. chainlit/socket.py +3 -3
  36. chainlit/step.py +13 -20
  37. {chainlit-1.2.0.dist-info → chainlit-1.3.0.dist-info}/METADATA +24 -16
  38. {chainlit-1.2.0.dist-info → chainlit-1.3.0.dist-info}/RECORD +45 -45
  39. chainlit/frontend/dist/assets/DailyMotion-05f4fe48.js +0 -1
  40. chainlit/frontend/dist/assets/Facebook-f25411d1.js +0 -1
  41. chainlit/frontend/dist/assets/FilePlayer-40ff3414.js +0 -1
  42. chainlit/frontend/dist/assets/Kaltura-6cbf3897.js +0 -1
  43. chainlit/frontend/dist/assets/Mixcloud-34e7c912.js +0 -1
  44. chainlit/frontend/dist/assets/Mux-8aaff6ac.js +0 -1
  45. chainlit/frontend/dist/assets/Preview-2d3bf558.js +0 -1
  46. chainlit/frontend/dist/assets/SoundCloud-b835f90f.js +0 -1
  47. chainlit/frontend/dist/assets/Streamable-1293e4f3.js +0 -1
  48. chainlit/frontend/dist/assets/Twitch-c69660cd.js +0 -1
  49. chainlit/frontend/dist/assets/Vidyard-43bda599.js +0 -1
  50. chainlit/frontend/dist/assets/Vimeo-54150039.js +0 -1
  51. chainlit/frontend/dist/assets/Wistia-aa3c721b.js +0 -1
  52. chainlit/frontend/dist/assets/YouTube-dd0f3cc2.js +0 -1
  53. chainlit/frontend/dist/assets/index-cf48bedd.js +0 -729
  54. /chainlit/copilot/dist/assets/{logo_dark-2a3cf740.svg → logo_dark-IkGJ_IwC.svg} +0 -0
  55. /chainlit/copilot/dist/assets/{logo_light-b078e7bc.svg → logo_light-Bb_IPh6r.svg} +0 -0
  56. /chainlit/frontend/dist/assets/{index-aaf974a9.css → index-CwmincdQ.css} +0 -0
  57. /chainlit/frontend/dist/assets/{logo_dark-2a3cf740.svg → logo_dark-IkGJ_IwC.svg} +0 -0
  58. /chainlit/frontend/dist/assets/{logo_light-b078e7bc.svg → logo_light-Bb_IPh6r.svg} +0 -0
  59. {chainlit-1.2.0.dist-info → chainlit-1.3.0.dist-info}/WHEEL +0 -0
  60. {chainlit-1.2.0.dist-info → chainlit-1.3.0.dist-info}/entry_points.txt +0 -0
@@ -21,11 +21,10 @@
21
21
  <script>
22
22
  const global = globalThis;
23
23
  </script>
24
- <script type="module" crossorigin src="/assets/index-cf48bedd.js"></script>
25
- <link rel="stylesheet" href="/assets/index-aaf974a9.css">
24
+ <script type="module" crossorigin src="/assets/index-DLRdQOIx.js"></script>
25
+ <link rel="stylesheet" crossorigin href="/assets/index-CwmincdQ.css">
26
26
  </head>
27
27
  <body>
28
28
  <div id="root"></div>
29
-
30
29
  </body>
31
30
  </html>
@@ -1,15 +1,13 @@
1
1
  import re
2
2
  from typing import Any, Generic, List, Optional, TypeVar
3
3
 
4
- from chainlit.context import context
4
+ from chainlit import Message
5
5
  from chainlit.step import Step
6
6
  from chainlit.sync import run_sync
7
7
  from haystack.agents import Agent, Tool
8
8
  from haystack.agents.agent_step import AgentStep
9
9
  from literalai.helper import utc_now
10
10
 
11
- from chainlit import Message
12
-
13
11
  T = TypeVar("T")
14
12
 
15
13
 
@@ -9,11 +9,10 @@ from chainlit.step import Step
9
9
  from langchain.callbacks.tracers.base import BaseTracer
10
10
  from langchain.callbacks.tracers.schemas import Run
11
11
  from langchain.schema import BaseMessage
12
- from langchain.schema.output import ChatGenerationChunk, GenerationChunk
13
12
  from langchain_core.outputs import ChatGenerationChunk, GenerationChunk
14
13
  from literalai import ChatGeneration, CompletionGeneration, GenerationMessage
15
14
  from literalai.helper import utc_now
16
- from literalai.step import TrueStepType
15
+ from literalai.observability.step import TrueStepType
17
16
 
18
17
  DEFAULT_ANSWER_PREFIX_TOKENS = ["Final", "Answer", ":"]
19
18
 
chainlit/message.py CHANGED
@@ -23,7 +23,7 @@ from chainlit.types import (
23
23
  FileDict,
24
24
  )
25
25
  from literalai.helper import utc_now
26
- from literalai.step import MessageStepType
26
+ from literalai.observability.step import MessageStepType
27
27
 
28
28
 
29
29
  class MessageBase(ABC):
@@ -3,7 +3,6 @@ from typing import Union
3
3
 
4
4
  from chainlit.context import get_context
5
5
  from chainlit.step import Step
6
- from chainlit.utils import check_module_version
7
6
  from literalai import ChatGeneration, CompletionGeneration
8
7
  from literalai.helper import timestamp_utc
9
8
 
@@ -16,6 +16,7 @@ class OAuthProvider:
16
16
  client_secret: str
17
17
  authorize_url: str
18
18
  authorize_params: Dict[str, str]
19
+ default_prompt: Optional[str] = None
19
20
 
20
21
  def is_configured(self):
21
22
  return all([os.environ.get(env) for env in self.env])
@@ -26,6 +27,21 @@ class OAuthProvider:
26
27
  async def get_user_info(self, token: str) -> Tuple[Dict[str, str], User]:
27
28
  raise NotImplementedError()
28
29
 
30
+ def get_env_prefix(self) -> str:
31
+ """Return environment prefix, like AZURE_AD."""
32
+
33
+ return self.id.replace("-", "_").upper()
34
+
35
+ def get_prompt(self) -> Optional[str]:
36
+ """Return OAuth prompt param."""
37
+ if prompt := os.environ.get(f"OAUTH_{self.get_env_prefix()}_PROMPT"):
38
+ return prompt
39
+
40
+ if prompt := os.environ.get("OAUTH_PROMPT"):
41
+ return prompt
42
+
43
+ return self.default_prompt
44
+
29
45
 
30
46
  class GithubOAuthProvider(OAuthProvider):
31
47
  id = "github"
@@ -39,6 +55,9 @@ class GithubOAuthProvider(OAuthProvider):
39
55
  "scope": "user:email",
40
56
  }
41
57
 
58
+ if prompt := self.get_prompt():
59
+ self.authorize_params["prompt"] = prompt
60
+
42
61
  async def get_token(self, code: str, url: str):
43
62
  payload = {
44
63
  "client_id": self.client_id,
@@ -97,6 +116,9 @@ class GoogleOAuthProvider(OAuthProvider):
97
116
  "access_type": "offline",
98
117
  }
99
118
 
119
+ if prompt := self.get_prompt():
120
+ self.authorize_params["prompt"] = prompt
121
+
100
122
  async def get_token(self, code: str, url: str):
101
123
  payload = {
102
124
  "client_id": self.client_id,
@@ -164,6 +186,9 @@ class AzureADOAuthProvider(OAuthProvider):
164
186
  "response_mode": "query",
165
187
  }
166
188
 
189
+ if prompt := self.get_prompt():
190
+ self.authorize_params["prompt"] = prompt
191
+
167
192
  async def get_token(self, code: str, url: str):
168
193
  payload = {
169
194
  "client_id": self.client_id,
@@ -207,7 +232,7 @@ class AzureADOAuthProvider(OAuthProvider):
207
232
  azure_user["image"] = (
208
233
  f"data:{photo_response.headers['Content-Type']};base64,{base64_image.decode('utf-8')}"
209
234
  )
210
- except Exception as e:
235
+ except Exception:
211
236
  # Ignore errors getting the photo
212
237
  pass
213
238
 
@@ -248,6 +273,9 @@ class AzureADHybridOAuthProvider(OAuthProvider):
248
273
  "nonce": nonce,
249
274
  }
250
275
 
276
+ if prompt := self.get_prompt():
277
+ self.authorize_params["prompt"] = prompt
278
+
251
279
  async def get_token(self, code: str, url: str):
252
280
  payload = {
253
281
  "client_id": self.client_id,
@@ -291,7 +319,7 @@ class AzureADHybridOAuthProvider(OAuthProvider):
291
319
  azure_user["image"] = (
292
320
  f"data:{photo_response.headers['Content-Type']};base64,{base64_image.decode('utf-8')}"
293
321
  )
294
- except Exception as e:
322
+ except Exception:
295
323
  # Ignore errors getting the photo
296
324
  pass
297
325
 
@@ -327,6 +355,9 @@ class OktaOAuthProvider(OAuthProvider):
327
355
  "response_mode": "query",
328
356
  }
329
357
 
358
+ if prompt := self.get_prompt():
359
+ self.authorize_params["prompt"] = prompt
360
+
330
361
  def get_authorization_server_path(self):
331
362
  if not self.authorization_server_id:
332
363
  return "/default"
@@ -398,6 +429,9 @@ class Auth0OAuthProvider(OAuthProvider):
398
429
  "audience": f"{self.original_domain}/userinfo",
399
430
  }
400
431
 
432
+ if prompt := self.get_prompt():
433
+ self.authorize_params["prompt"] = prompt
434
+
401
435
  async def get_token(self, code: str, url: str):
402
436
  payload = {
403
437
  "client_id": self.client_id,
@@ -442,7 +476,7 @@ class DescopeOAuthProvider(OAuthProvider):
442
476
  id = "descope"
443
477
  env = ["OAUTH_DESCOPE_CLIENT_ID", "OAUTH_DESCOPE_CLIENT_SECRET"]
444
478
  # Ensure that the domain does not have a trailing slash
445
- domain = f"https://api.descope.com/oauth2/v1"
479
+ domain = "https://api.descope.com/oauth2/v1"
446
480
 
447
481
  authorize_url = f"{domain}/authorize"
448
482
 
@@ -455,6 +489,9 @@ class DescopeOAuthProvider(OAuthProvider):
455
489
  "audience": f"{self.domain}/userinfo",
456
490
  }
457
491
 
492
+ if prompt := self.get_prompt():
493
+ self.authorize_params["prompt"] = prompt
494
+
458
495
  async def get_token(self, code: str, url: str):
459
496
  payload = {
460
497
  "client_id": self.client_id,
@@ -513,6 +550,9 @@ class AWSCognitoOAuthProvider(OAuthProvider):
513
550
  "scope": "openid profile email",
514
551
  }
515
552
 
553
+ if prompt := self.get_prompt():
554
+ self.authorize_params["prompt"] = prompt
555
+
516
556
  async def get_token(self, code: str, url: str):
517
557
  payload = {
518
558
  "client_id": self.client_id,
@@ -581,6 +621,9 @@ class GitlabOAuthProvider(OAuthProvider):
581
621
  "response_type": "code",
582
622
  }
583
623
 
624
+ if prompt := self.get_prompt():
625
+ self.authorize_params["prompt"] = prompt
626
+
584
627
  async def get_token(self, code: str, url: str):
585
628
  payload = {
586
629
  "client_id": self.client_id,
@@ -1,7 +1,7 @@
1
1
  import asyncio
2
2
  from typing import Union
3
3
 
4
- from chainlit.context import get_context
4
+ from chainlit.context import local_steps
5
5
  from chainlit.step import Step
6
6
  from chainlit.utils import check_module_version
7
7
  from literalai import ChatGeneration, CompletionGeneration
@@ -19,11 +19,9 @@ def instrument_openai():
19
19
  def on_new_generation(
20
20
  generation: Union["ChatGeneration", "CompletionGeneration"], timing
21
21
  ):
22
- context = get_context()
22
+ previous_steps = local_steps.get()
23
23
 
24
- parent_id = None
25
- if context.current_step:
26
- parent_id = context.current_step.id
24
+ parent_id = previous_steps[-1].id if previous_steps else None
27
25
 
28
26
  step = Step(
29
27
  name=generation.model if generation.model else generation.provider,
chainlit/server.py CHANGED
@@ -41,6 +41,7 @@ from fastapi import (
41
41
  APIRouter,
42
42
  Depends,
43
43
  FastAPI,
44
+ File,
44
45
  Form,
45
46
  HTTPException,
46
47
  Query,
@@ -645,7 +646,7 @@ async def oauth_azure_hf_callback(
645
646
 
646
647
 
647
648
  _language_pattern = (
648
- "^[a-zA-Z]{2,3}(-[a-zA-Z]{2,3})?(-[a-zA-Z]{2,8})?(-x-[a-zA-Z0-9]{1,8})?$"
649
+ "^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,3})?(-[a-zA-Z0-9]{2,8})?(-x-[a-zA-Z0-9]{1,8})?$"
649
650
  )
650
651
 
651
652
 
@@ -839,11 +840,9 @@ async def delete_thread(
839
840
 
840
841
  @router.post("/project/file")
841
842
  async def upload_file(
843
+ current_user: Annotated[Union[User, PersistedUser], Depends(get_current_user)],
842
844
  session_id: str,
843
845
  file: UploadFile,
844
- current_user: Annotated[
845
- Union[None, User, PersistedUser], Depends(get_current_user)
846
- ],
847
846
  ):
848
847
  """Upload a file to the session files directory."""
849
848
 
@@ -868,17 +867,21 @@ async def upload_file(
868
867
 
869
868
  content = await file.read()
870
869
 
870
+ assert file.filename, "No filename for uploaded file"
871
+ assert file.content_type, "No content type for uploaded file"
872
+
871
873
  file_response = await session.persist_file(
872
874
  name=file.filename, content=content, mime=file.content_type
873
875
  )
874
876
 
875
- return JSONResponse(file_response)
877
+ return JSONResponse(content=file_response)
876
878
 
877
879
 
878
880
  @router.get("/project/file/{file_id}")
879
881
  async def get_file(
880
882
  file_id: str,
881
- session_id: Optional[str] = None,
883
+ session_id: str,
884
+ current_user: Annotated[Union[User, PersistedUser], Depends(get_current_user)],
882
885
  ):
883
886
  """Get a file from the session files directory."""
884
887
 
@@ -888,10 +891,17 @@ async def get_file(
888
891
 
889
892
  if not session:
890
893
  raise HTTPException(
891
- status_code=404,
892
- detail="Session not found",
894
+ status_code=401,
895
+ detail="Unauthorized",
893
896
  )
894
897
 
898
+ if current_user:
899
+ if not session.user or session.user.identifier != current_user.identifier:
900
+ raise HTTPException(
901
+ status_code=401,
902
+ detail="You are not authorized to download files from this session",
903
+ )
904
+
895
905
  if file_id in session.files:
896
906
  file = session.files[file_id]
897
907
  return FileResponse(file["path"], media_type=file["type"])
@@ -961,7 +971,7 @@ async def get_logo(theme: Optional[Theme] = Query(Theme.light)):
961
971
  @router.get("/avatars/{avatar_id:str}")
962
972
  async def get_avatar(avatar_id: str):
963
973
  """Get the avatar for the user based on the avatar_id."""
964
- if not re.match(r"^[a-zA-Z0-9_-]+$", avatar_id):
974
+ if not re.match(r"^[a-zA-Z0-9_ -]+$", avatar_id):
965
975
  raise HTTPException(status_code=400, detail="Invalid avatar_id")
966
976
 
967
977
  if avatar_id == "default":
chainlit/session.py CHANGED
@@ -3,25 +3,14 @@ import json
3
3
  import mimetypes
4
4
  import shutil
5
5
  import uuid
6
- from typing import (
7
- TYPE_CHECKING,
8
- Any,
9
- Callable,
10
- Deque,
11
- Dict,
12
- List,
13
- Literal,
14
- Optional,
15
- Union,
16
- )
6
+ from typing import TYPE_CHECKING, Any, Callable, Deque, Dict, Literal, Optional, Union
17
7
 
18
8
  import aiofiles
19
9
  from chainlit.logger import logger
10
+ from chainlit.types import FileReference
20
11
 
21
12
  if TYPE_CHECKING:
22
- from chainlit.message import Message
23
- from chainlit.step import Step
24
- from chainlit.types import FileDict, FileReference
13
+ from chainlit.types import FileDict
25
14
  from chainlit.user import PersistedUser, User
26
15
 
27
16
  ClientType = Literal["webapp", "copilot", "teams", "slack", "discord"]
@@ -64,7 +53,7 @@ class BaseSession:
64
53
  client_type: ClientType,
65
54
  # Thread id
66
55
  thread_id: Optional[str],
67
- # Logged-in user informations
56
+ # Logged-in user information
68
57
  user: Optional[Union["User", "PersistedUser"]],
69
58
  # Logged-in user token
70
59
  token: Optional[str],
@@ -86,7 +75,7 @@ class BaseSession:
86
75
  self.chat_profile = chat_profile
87
76
  self.http_referer = http_referer
88
77
 
89
- self.files = {} # type: Dict[str, "FileDict"]
78
+ self.files: Dict[str, FileDict] = {}
90
79
 
91
80
  self.id = id
92
81
 
@@ -104,7 +93,7 @@ class BaseSession:
104
93
  mime: str,
105
94
  path: Optional[str] = None,
106
95
  content: Optional[Union[bytes, str]] = None,
107
- ) -> "FileReference":
96
+ ) -> FileReference:
108
97
  if not path and not content:
109
98
  raise ValueError(
110
99
  "Either path or content must be provided to persist a file"
@@ -169,7 +158,7 @@ class HTTPSession(BaseSession):
169
158
  client_type: ClientType,
170
159
  # Thread id
171
160
  thread_id: Optional[str] = None,
172
- # Logged-in user informations
161
+ # Logged-in user information
173
162
  user: Optional[Union["User", "PersistedUser"]] = None,
174
163
  # Logged-in user token
175
164
  token: Optional[str] = None,
@@ -225,7 +214,7 @@ class WebsocketSession(BaseSession):
225
214
  client_type: ClientType,
226
215
  # Thread id
227
216
  thread_id: Optional[str] = None,
228
- # Logged-in user informations
217
+ # Logged-in user information
229
218
  user: Optional[Union["User", "PersistedUser"]] = None,
230
219
  # Logged-in user token
231
220
  token: Optional[str] = None,
chainlit/socket.py CHANGED
@@ -119,7 +119,7 @@ async def connect(sid, environ):
119
119
  authorization_header = environ.get("HTTP_AUTHORIZATION")
120
120
  token = authorization_header.split(" ")[1] if authorization_header else None
121
121
  user = await get_current_user(token=token)
122
- except Exception as e:
122
+ except Exception:
123
123
  logger.info("Authentication failed")
124
124
  return False
125
125
 
@@ -145,7 +145,7 @@ async def connect(sid, environ):
145
145
  unquote(url_encoded_chat_profile) if url_encoded_chat_profile else None
146
146
  )
147
147
 
148
- ws_session = WebsocketSession(
148
+ WebsocketSession(
149
149
  id=session_id,
150
150
  socket_id=sid,
151
151
  emit=emit_fn,
@@ -392,7 +392,7 @@ async def call_action(sid, action):
392
392
  except Exception as e:
393
393
  logger.exception(e)
394
394
  await context.emitter.send_action_response(
395
- id=action.id, status=False, response="An error occured"
395
+ id=action.id, status=False, response="An error occurred"
396
396
  )
397
397
 
398
398
 
chainlit/step.py CHANGED
@@ -16,7 +16,7 @@ from chainlit.telemetry import trace_event
16
16
  from chainlit.types import FeedbackDict
17
17
  from literalai import BaseGeneration
18
18
  from literalai.helper import utc_now
19
- from literalai.step import StepType, TrueStepType
19
+ from literalai.observability.step import StepType, TrueStepType
20
20
 
21
21
 
22
22
  def check_add_step_in_cot(step: "Step"):
@@ -30,7 +30,7 @@ def check_add_step_in_cot(step: "Step"):
30
30
  return True
31
31
 
32
32
 
33
- def stub_step(step: "Step"):
33
+ def stub_step(step: "Step") -> "StepDict":
34
34
  return {
35
35
  "type": step.type,
36
36
  "name": step.name,
@@ -65,7 +65,7 @@ class StepDict(TypedDict, total=False):
65
65
  feedback: Optional[FeedbackDict]
66
66
 
67
67
 
68
- def flatten_args_kwargs(func, *args, **kwargs):
68
+ def flatten_args_kwargs(func, args, kwargs):
69
69
  signature = inspect.signature(func)
70
70
  bound_arguments = signature.bind(*args, **kwargs)
71
71
  bound_arguments.apply_defaults()
@@ -189,12 +189,13 @@ class Step:
189
189
  tags: Optional[List[str]] = None,
190
190
  language: Optional[str] = None,
191
191
  show_input: Union[bool, str] = "json",
192
+ thread_id: Optional[str] = None,
192
193
  ):
193
194
  trace_event(f"init {self.__class__.__name__} {type}")
194
195
  time.sleep(0.001)
195
196
  self._input = ""
196
197
  self._output = ""
197
- self.thread_id = context.session.thread_id
198
+ self.thread_id = thread_id or context.session.thread_id
198
199
  self.name = name or ""
199
200
  self.type = type
200
201
  self.id = id or str(uuid.uuid4())
@@ -433,7 +434,6 @@ class Step:
433
434
  if not self.parent_id:
434
435
  if parent_step:
435
436
  self.parent_id = parent_step.id
436
- context.active_steps.append(self)
437
437
  local_steps.set(previous_steps + [self])
438
438
  await self.send()
439
439
  return self
@@ -445,13 +445,10 @@ class Step:
445
445
  self.output = str(exc_val)
446
446
  self.is_error = True
447
447
 
448
- if self in context.active_steps:
449
- context.active_steps.remove(self)
450
-
451
- local_active_steps = local_steps.get()
452
- if local_active_steps and self in local_active_steps:
453
- local_active_steps.remove(self)
454
- local_steps.set(local_active_steps)
448
+ current_steps = local_steps.get()
449
+ if current_steps and self in current_steps:
450
+ current_steps.remove(self)
451
+ local_steps.set(current_steps)
455
452
 
456
453
  await self.update()
457
454
 
@@ -464,7 +461,6 @@ class Step:
464
461
  if not self.parent_id:
465
462
  if parent_step:
466
463
  self.parent_id = parent_step.id
467
- context.active_steps.append(self)
468
464
  local_steps.set(previous_steps + [self])
469
465
 
470
466
  asyncio.create_task(self.send())
@@ -477,12 +473,9 @@ class Step:
477
473
  self.output = str(exc_val)
478
474
  self.is_error = True
479
475
 
480
- if self in context.active_steps:
481
- context.active_steps.remove(self)
482
-
483
- local_active_steps = local_steps.get()
484
- if local_active_steps and self in local_active_steps:
485
- local_active_steps.remove(self)
486
- local_steps.set(local_active_steps)
476
+ current_steps = local_steps.get()
477
+ if current_steps and self in current_steps:
478
+ current_steps.remove(self)
479
+ local_steps.set(current_steps)
487
480
 
488
481
  asyncio.create_task(self.update())
@@ -1,18 +1,25 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: chainlit
3
- Version: 1.2.0
3
+ Version: 1.3.0
4
4
  Summary: Build Conversational AI.
5
- Home-page: https://github.com/Chainlit/chainlit
6
- License: Apache-2.0 license
5
+ Home-page: https://chainlit.io/
6
+ License: Apache-2.0
7
7
  Keywords: LLM,Agents,gen ai,chat ui,chatbot ui,openai,copilot,langchain,conversational ai
8
- Author: Chainlit
8
+ Author: Willy Douhard
9
9
  Requires-Python: >=3.9,<4.0.0
10
+ Classifier: Environment :: Web Environment
11
+ Classifier: Framework :: FastAPI
10
12
  Classifier: License :: Other/Proprietary License
13
+ Classifier: Programming Language :: JavaScript
11
14
  Classifier: Programming Language :: Python :: 3
12
15
  Classifier: Programming Language :: Python :: 3.9
13
16
  Classifier: Programming Language :: Python :: 3.10
14
17
  Classifier: Programming Language :: Python :: 3.11
15
18
  Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Communications :: Chat
20
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Classifier: Topic :: Software Development :: User Interfaces
16
23
  Requires-Dist: aiofiles (>=23.1.0,<24.0.0)
17
24
  Requires-Dist: asyncer (>=0.0.7,<0.0.8)
18
25
  Requires-Dist: click (>=8.1.3,<9.0.0)
@@ -21,7 +28,7 @@ Requires-Dist: fastapi (>=0.110.1,<0.113)
21
28
  Requires-Dist: filetype (>=1.2.0,<2.0.0)
22
29
  Requires-Dist: httpx (>=0.23.0)
23
30
  Requires-Dist: lazify (>=0.4.0,<0.5.0)
24
- Requires-Dist: literalai (==0.0.607)
31
+ Requires-Dist: literalai (==0.0.623)
25
32
  Requires-Dist: nest-asyncio (>=1.6.0,<2.0.0)
26
33
  Requires-Dist: numpy (>=1.26,<2.0)
27
34
  Requires-Dist: packaging (>=23.1,<24.0)
@@ -36,6 +43,7 @@ Requires-Dist: tomli (>=2.0.1,<3.0.0)
36
43
  Requires-Dist: uptrace (>=1.22.0,<2.0.0)
37
44
  Requires-Dist: uvicorn (>=0.25.0,<0.26.0)
38
45
  Requires-Dist: watchfiles (>=0.20.0,<0.21.0)
46
+ Project-URL: Documentation, https://docs.chainlit.io/
39
47
  Project-URL: Repository, https://github.com/Chainlit/chainlit
40
48
  Description-Content-Type: text/markdown
41
49
 
@@ -60,20 +68,21 @@ Chainlit is an open-source async Python framework which allows developers to bui
60
68
  Full documentation is available [here](https://docs.chainlit.io). You can ask Chainlit related questions to [Chainlit Help](https://help.chainlit.io/), an app built using Chainlit!
61
69
 
62
70
  > [!NOTE]
63
- > Contact us [here](https://forms.gle/BX3UNBLmTF75KgZVA) for **Enterprise Support**.
64
71
  > Check out [Literal AI](https://literalai.com), our product to monitor and evaluate LLM applications! It works with any Python or TypeScript applications and [seamlessly](https://docs.chainlit.io/data-persistence/overview) with Chainlit by adding a `LITERAL_API_KEY` in your project.
72
+ >
73
+ > Chainlit is developed and maintained by the Literal AI team, which is currently focused on expanding the capabilities of Literal AI. While we continue to support and maintain Chainlit, we are also committed to enabling the community to contribute, particularly in areas like integrations and data layers.
65
74
 
66
75
  <p align="center">
67
- <img src="https://github.com/Chainlit/chainlit/assets/13104895/0c2cc7a9-766c-41d3-aae2-117a2d0eb8ed" width="80%" />
76
+ <img src="https://github.com/Chainlit/chainlit/assets/13104895/0c2cc7a9-766c-41d3-aae2-117a2d0eb8ed" alt="Chainlit user interface" width="80%"></img>
68
77
  </p>
69
78
 
70
79
  ## Installation
71
80
 
72
81
  Open a terminal and run:
73
82
 
74
- ```bash
75
- $ pip install chainlit
76
- $ chainlit hello
83
+ ```sh
84
+ pip install chainlit
85
+ chainlit hello
77
86
  ```
78
87
 
79
88
  If this opens the `hello app` in your browser, you're all set!
@@ -108,18 +117,17 @@ async def main(message: cl.Message):
108
117
  None.
109
118
  """
110
119
 
111
- final_answer = await cl.Message(content="").send()
112
120
 
113
121
  # Call the tool
114
- final_answer.content = await tool()
122
+ tool_res = await tool()
115
123
 
116
- await final_answer.update()
124
+ await cl.Message(content=tool_res).send()
117
125
  ```
118
126
 
119
127
  Now run it!
120
128
 
121
- ```
122
- $ chainlit run demo.py -w
129
+ ```sh
130
+ chainlit run demo.py -w
123
131
  ```
124
132
 
125
133
  <img src="/images/quick-start.png" alt="Quick Start"></img>
@@ -129,7 +137,7 @@ $ chainlit run demo.py -w
129
137
  Full documentation is available [here](https://docs.chainlit.io). Key features:
130
138
 
131
139
  - [💬 Multi Modal chats](https://docs.chainlit.io/advanced-features/multi-modal)
132
- - [💭 Chain of Thought visualisation](https://docs.chainlit.io/concepts/step)
140
+ - [💭 Chain of Thought visualization](https://docs.chainlit.io/concepts/step)
133
141
  - [💾 Data persistence + human feedback](https://docs.chainlit.io/data-persistence/overview)
134
142
  - [🐛 Debug Mode](https://docs.chainlit.io/data-persistence/enterprise#debug-mode)
135
143
  - [👤 Authentication](https://docs.chainlit.io/authentication/overview)