chainlit 1.2.0rc0__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 (72) hide show
  1. chainlit/__init__.py +0 -10
  2. chainlit/auth.py +9 -7
  3. chainlit/callbacks.py +0 -32
  4. chainlit/cli/__init__.py +2 -0
  5. chainlit/config.py +7 -11
  6. chainlit/context.py +4 -6
  7. chainlit/copilot/dist/index.js +298 -307
  8. chainlit/data/__init__.py +11 -6
  9. chainlit/data/dynamodb.py +7 -3
  10. chainlit/data/literalai.py +168 -59
  11. chainlit/data/sql_alchemy.py +89 -15
  12. chainlit/element.py +2 -1
  13. chainlit/emitter.py +0 -7
  14. chainlit/frontend/dist/assets/DailyMotion-CwoOhIL8.js +1 -0
  15. chainlit/frontend/dist/assets/Facebook-BhnGXlzq.js +1 -0
  16. chainlit/frontend/dist/assets/FilePlayer-CPSVT6fz.js +1 -0
  17. chainlit/frontend/dist/assets/Kaltura-COYaLzsL.js +1 -0
  18. chainlit/frontend/dist/assets/Mixcloud-JdadNiQ5.js +1 -0
  19. chainlit/frontend/dist/assets/Mux-CBN7RO2u.js +1 -0
  20. chainlit/frontend/dist/assets/Preview-CxAFvvjV.js +1 -0
  21. chainlit/frontend/dist/assets/SoundCloud-JlgmASWm.js +1 -0
  22. chainlit/frontend/dist/assets/Streamable-CUWgr6Zw.js +1 -0
  23. chainlit/frontend/dist/assets/Twitch-BiN1HEDM.js +1 -0
  24. chainlit/frontend/dist/assets/Vidyard-qhPmrhDm.js +1 -0
  25. chainlit/frontend/dist/assets/Vimeo-CrZVSCaT.js +1 -0
  26. chainlit/frontend/dist/assets/Wistia-C891KrBP.js +1 -0
  27. chainlit/frontend/dist/assets/YouTube-DKjw5Hbn.js +1 -0
  28. chainlit/frontend/dist/assets/index-DLRdQOIx.js +723 -0
  29. chainlit/frontend/dist/assets/{react-plotly-55648373.js → react-plotly-Dpmqg5Sy.js} +94 -94
  30. chainlit/frontend/dist/index.html +2 -3
  31. chainlit/haystack/callbacks.py +1 -3
  32. chainlit/input_widget.py +0 -22
  33. chainlit/langchain/callbacks.py +1 -2
  34. chainlit/message.py +1 -1
  35. chainlit/mistralai/__init__.py +0 -1
  36. chainlit/oauth_providers.py +46 -3
  37. chainlit/openai/__init__.py +3 -5
  38. chainlit/server.py +20 -32
  39. chainlit/session.py +5 -36
  40. chainlit/socket.py +2 -51
  41. chainlit/step.py +13 -20
  42. chainlit/translations/en-US.json +0 -6
  43. chainlit/types.py +1 -1
  44. chainlit/user_session.py +0 -4
  45. {chainlit-1.2.0rc0.dist-info → chainlit-1.3.0.dist-info}/METADATA +17 -9
  46. chainlit-1.3.0.dist-info/RECORD +96 -0
  47. chainlit/assistant.py +0 -16
  48. chainlit/assistant_settings.py +0 -35
  49. chainlit/frontend/dist/assets/DailyMotion-aa368b7e.js +0 -1
  50. chainlit/frontend/dist/assets/Facebook-0335db46.js +0 -1
  51. chainlit/frontend/dist/assets/FilePlayer-8d04256c.js +0 -1
  52. chainlit/frontend/dist/assets/Kaltura-67c9dd31.js +0 -1
  53. chainlit/frontend/dist/assets/Mixcloud-6bbaccf5.js +0 -1
  54. chainlit/frontend/dist/assets/Mux-c2bcb757.js +0 -1
  55. chainlit/frontend/dist/assets/Preview-210f3955.js +0 -1
  56. chainlit/frontend/dist/assets/SoundCloud-a0276b84.js +0 -1
  57. chainlit/frontend/dist/assets/Streamable-a007323d.js +0 -1
  58. chainlit/frontend/dist/assets/Twitch-e6a88aa3.js +0 -1
  59. chainlit/frontend/dist/assets/Vidyard-dfb88a35.js +0 -1
  60. chainlit/frontend/dist/assets/Vimeo-3baa13d9.js +0 -1
  61. chainlit/frontend/dist/assets/Wistia-e52f7bef.js +0 -1
  62. chainlit/frontend/dist/assets/YouTube-1715f22b.js +0 -1
  63. chainlit/frontend/dist/assets/index-bfdd8585.js +0 -729
  64. chainlit/translations/fr-FR.json +0 -236
  65. chainlit-1.2.0rc0.dist-info/RECORD +0 -99
  66. /chainlit/copilot/dist/assets/{logo_dark-2a3cf740.svg → logo_dark-IkGJ_IwC.svg} +0 -0
  67. /chainlit/copilot/dist/assets/{logo_light-b078e7bc.svg → logo_light-Bb_IPh6r.svg} +0 -0
  68. /chainlit/frontend/dist/assets/{index-aaf974a9.css → index-CwmincdQ.css} +0 -0
  69. /chainlit/frontend/dist/assets/{logo_dark-2a3cf740.svg → logo_dark-IkGJ_IwC.svg} +0 -0
  70. /chainlit/frontend/dist/assets/{logo_light-b078e7bc.svg → logo_light-Bb_IPh6r.svg} +0 -0
  71. {chainlit-1.2.0rc0.dist-info → chainlit-1.3.0.dist-info}/WHEEL +0 -0
  72. {chainlit-1.2.0rc0.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-bfdd8585.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
 
chainlit/input_widget.py CHANGED
@@ -161,28 +161,6 @@ class NumberInput(InputWidget):
161
161
  "description": self.description,
162
162
  }
163
163
 
164
- @dataclass
165
- class FileUploadInput(InputWidget):
166
- """Useful to create a file upload input."""
167
-
168
- type: InputWidgetType = "fileupload"
169
- initial: Optional[str] = None
170
- placeholder: Optional[str] = None
171
- accept: List[str] = Field(default_factory=lambda: [])
172
- max_size_mb: Optional[int] = None
173
- max_files: Optional[int] = None
174
-
175
- def to_dict(self) -> Dict[str, Any]:
176
- return {
177
- "type": self.type,
178
- "id": self.id,
179
- "label": self.label,
180
- "initial": self.initial,
181
- "placeholder": self.placeholder,
182
- "tooltip": self.tooltip,
183
- "description": self.description,
184
- }
185
-
186
164
 
187
165
  @dataclass
188
166
  class Tags(InputWidget):
@@ -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,
@@ -228,6 +229,7 @@ app.mount(
228
229
  name="copilot",
229
230
  )
230
231
 
232
+
231
233
  # -------------------------------------------------------------------------------
232
234
  # SLACK HANDLER
233
235
  # -------------------------------------------------------------------------------
@@ -644,7 +646,7 @@ async def oauth_azure_hf_callback(
644
646
 
645
647
 
646
648
  _language_pattern = (
647
- "^[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})?$"
648
650
  )
649
651
 
650
652
 
@@ -838,11 +840,9 @@ async def delete_thread(
838
840
 
839
841
  @router.post("/project/file")
840
842
  async def upload_file(
843
+ current_user: Annotated[Union[User, PersistedUser], Depends(get_current_user)],
841
844
  session_id: str,
842
845
  file: UploadFile,
843
- current_user: Annotated[
844
- Union[None, User, PersistedUser], Depends(get_current_user)
845
- ],
846
846
  ):
847
847
  """Upload a file to the session files directory."""
848
848
 
@@ -867,17 +867,21 @@ async def upload_file(
867
867
 
868
868
  content = await file.read()
869
869
 
870
+ assert file.filename, "No filename for uploaded file"
871
+ assert file.content_type, "No content type for uploaded file"
872
+
870
873
  file_response = await session.persist_file(
871
874
  name=file.filename, content=content, mime=file.content_type
872
875
  )
873
876
 
874
- return JSONResponse(file_response)
877
+ return JSONResponse(content=file_response)
875
878
 
876
879
 
877
880
  @router.get("/project/file/{file_id}")
878
881
  async def get_file(
879
882
  file_id: str,
880
- session_id: Optional[str] = None,
883
+ session_id: str,
884
+ current_user: Annotated[Union[User, PersistedUser], Depends(get_current_user)],
881
885
  ):
882
886
  """Get a file from the session files directory."""
883
887
 
@@ -887,10 +891,17 @@ async def get_file(
887
891
 
888
892
  if not session:
889
893
  raise HTTPException(
890
- status_code=404,
891
- detail="Session not found",
894
+ status_code=401,
895
+ detail="Unauthorized",
892
896
  )
893
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
+
894
905
  if file_id in session.files:
895
906
  file = session.files[file_id]
896
907
  return FileResponse(file["path"], media_type=file["type"])
@@ -960,7 +971,7 @@ async def get_logo(theme: Optional[Theme] = Query(Theme.light)):
960
971
  @router.get("/avatars/{avatar_id:str}")
961
972
  async def get_avatar(avatar_id: str):
962
973
  """Get the avatar for the user based on the avatar_id."""
963
- if not re.match(r"^[a-zA-Z0-9_-]+$", avatar_id):
974
+ if not re.match(r"^[a-zA-Z0-9_ -]+$", avatar_id):
964
975
  raise HTTPException(status_code=400, detail="Invalid avatar_id")
965
976
 
966
977
  if avatar_id == "default":
@@ -984,29 +995,6 @@ async def get_avatar(avatar_id: str):
984
995
  return await get_favicon()
985
996
 
986
997
 
987
- # post avatar/{avatar_id} (only for authenticated users)
988
- @router.post("/avatars/{avatar_id}")
989
- async def upload_avatar(
990
- avatar_id: str,
991
- file: UploadFile,
992
- current_user: Annotated[
993
- Union[None, User, PersistedUser], Depends(get_current_user)
994
- ],
995
- ):
996
- try:
997
- avatar_path = os.path.join(APP_ROOT, "public", "avatars", avatar_id)
998
-
999
- # Ensure the avatars directory exists
1000
- os.makedirs(os.path.dirname(avatar_path), exist_ok=True)
1001
-
1002
- with open(avatar_path, "wb") as f:
1003
- f.write(await file.read())
1004
- except Exception as e:
1005
- raise HTTPException(status_code=500, detail=str(e))
1006
-
1007
- return {"id": avatar_id}
1008
-
1009
-
1010
998
  @router.head("/")
1011
999
  def status_check():
1012
1000
  """Check if the site is operational."""
chainlit/session.py CHANGED
@@ -3,26 +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
- from chainlit.assistant import Assistant
20
9
  from chainlit.logger import logger
10
+ from chainlit.types import FileReference
21
11
 
22
12
  if TYPE_CHECKING:
23
- from chainlit.message import Message
24
- from chainlit.step import Step
25
- from chainlit.types import FileDict, FileReference
13
+ from chainlit.types import FileDict
26
14
  from chainlit.user import PersistedUser, User
27
15
 
28
16
  ClientType = Literal["webapp", "copilot", "teams", "slack", "discord"]
@@ -73,12 +61,8 @@ class BaseSession:
73
61
  user_env: Optional[Dict[str, str]],
74
62
  # Chat profile selected before the session was created
75
63
  chat_profile: Optional[str] = None,
76
- # Selected assistant
77
- selected_assistant: Optional[Assistant] = None,
78
64
  # Origin of the request
79
65
  http_referer: Optional[str] = None,
80
- # assistant settings
81
- assistant_settings: Optional[Dict[str, Any]] = None,
82
66
  ):
83
67
  if thread_id:
84
68
  self.thread_id_to_resume = thread_id
@@ -91,13 +75,11 @@ class BaseSession:
91
75
  self.chat_profile = chat_profile
92
76
  self.http_referer = http_referer
93
77
 
94
- self.files = {} # type: Dict[str, "FileDict"]
78
+ self.files: Dict[str, FileDict] = {}
95
79
 
96
80
  self.id = id
97
81
 
98
- self.assistant_settings = assistant_settings
99
82
  self.chat_settings: Dict[str, Any] = {}
100
- self.selected_assistant = selected_assistant
101
83
 
102
84
  @property
103
85
  def files_dir(self):
@@ -111,7 +93,7 @@ class BaseSession:
111
93
  mime: str,
112
94
  path: Optional[str] = None,
113
95
  content: Optional[Union[bytes, str]] = None,
114
- ) -> "FileReference":
96
+ ) -> FileReference:
115
97
  if not path and not content:
116
98
  raise ValueError(
117
99
  "Either path or content must be provided to persist a file"
@@ -160,7 +142,6 @@ class BaseSession:
160
142
  user_session = user_sessions.get(self.id) or {} # type: Dict
161
143
  user_session["chat_settings"] = self.chat_settings
162
144
  user_session["chat_profile"] = self.chat_profile
163
- user_session["selected_assistant"] = self.selected_assistant
164
145
  user_session["http_referer"] = self.http_referer
165
146
  user_session["client_type"] = self.client_type
166
147
  metadata = clean_metadata(user_session)
@@ -184,10 +165,6 @@ class HTTPSession(BaseSession):
184
165
  user_env: Optional[Dict[str, str]] = None,
185
166
  # Origin of the request
186
167
  http_referer: Optional[str] = None,
187
- # assistant settings
188
- assistant_settings: Optional[Dict[str, Any]] = None,
189
- # selected assistant
190
- selected_assistant: Optional[Assistant] = None,
191
168
  ):
192
169
  super().__init__(
193
170
  id=id,
@@ -197,8 +174,6 @@ class HTTPSession(BaseSession):
197
174
  client_type=client_type,
198
175
  user_env=user_env,
199
176
  http_referer=http_referer,
200
- assistant_settings=assistant_settings,
201
- selected_assistant=selected_assistant,
202
177
  )
203
178
 
204
179
  def delete(self):
@@ -245,14 +220,10 @@ class WebsocketSession(BaseSession):
245
220
  token: Optional[str] = None,
246
221
  # Chat profile selected before the session was created
247
222
  chat_profile: Optional[str] = None,
248
- # Selected assistant
249
- selected_assistant: Optional[Assistant] = None,
250
223
  # Languages of the user's browser
251
224
  languages: Optional[str] = None,
252
225
  # Origin of the request
253
226
  http_referer: Optional[str] = None,
254
- # chat settings
255
- assistant_settings: Optional[Dict[str, Any]] = None,
256
227
  ):
257
228
  super().__init__(
258
229
  id=id,
@@ -262,9 +233,7 @@ class WebsocketSession(BaseSession):
262
233
  user_env=user_env,
263
234
  client_type=client_type,
264
235
  chat_profile=chat_profile,
265
- selected_assistant=selected_assistant,
266
236
  http_referer=http_referer,
267
- assistant_settings=assistant_settings,
268
237
  )
269
238
 
270
239
  self.socket_id = socket_id
chainlit/socket.py CHANGED
@@ -6,7 +6,6 @@ from typing import Any, Dict, Literal
6
6
  from urllib.parse import unquote
7
7
 
8
8
  from chainlit.action import Action
9
- from chainlit.assistant import Assistant
10
9
  from chainlit.auth import get_current_user, require_login
11
10
  from chainlit.chat_context import chat_context
12
11
  from chainlit.config import config
@@ -120,7 +119,7 @@ async def connect(sid, environ):
120
119
  authorization_header = environ.get("HTTP_AUTHORIZATION")
121
120
  token = authorization_header.split(" ")[1] if authorization_header else None
122
121
  user = await get_current_user(token=token)
123
- except Exception as e:
122
+ except Exception:
124
123
  logger.info("Authentication failed")
125
124
  return False
126
125
 
@@ -146,7 +145,7 @@ async def connect(sid, environ):
146
145
  unquote(url_encoded_chat_profile) if url_encoded_chat_profile else None
147
146
  )
148
147
 
149
- ws_session = WebsocketSession(
148
+ WebsocketSession(
150
149
  id=session_id,
151
150
  socket_id=sid,
152
151
  emit=emit_fn,
@@ -407,51 +406,3 @@ async def change_settings(sid, settings: Dict[str, Any]):
407
406
 
408
407
  if config.code.on_settings_update:
409
408
  await config.code.on_settings_update(settings)
410
-
411
-
412
- @sio.on("on_create_assistant")
413
- async def on_create_assistant(sid, options):
414
- context = init_ws_context(sid)
415
- logger.info(f"Received request to create assistant {options['settings_values']}")
416
- if not options["settings_values"].get("id"):
417
- options["settings_values"]["id"] = str(uuid.uuid4())
418
- if not options["settings_values"].get("created_by"):
419
- options["settings_values"]["created_by"] = context.session.user.identifier
420
- logger.info(
421
- f"Added id: {options['settings_values']['id']} and created_by: {options['settings_values']['created_by']} to the assistant options"
422
- )
423
- if config.code.on_create_assistant:
424
- new_assistant = Assistant(
425
- input_widgets=options["input_widgets"],
426
- settings_values=options["settings_values"],
427
- )
428
- await config.code.on_create_assistant(context.session.user, new_assistant)
429
- return new_assistant.to_dict()
430
- logger.info("Assistant creation process completed")
431
-
432
-
433
- @sio.on("on_list_assistants")
434
- async def on_list_assistants(sid):
435
- context = init_ws_context(sid)
436
- if config.code.on_list_assistants:
437
- assistants = await config.code.on_list_assistants(context.session.user)
438
- return [assistant.to_dict() for assistant in assistants]
439
-
440
-
441
- @sio.on("select_assistant")
442
- async def select_assistant(sid, selected_assistant: Dict):
443
- session = WebsocketSession.require(sid)
444
-
445
- new_assistant = Assistant(
446
- input_widgets=selected_assistant.get("input_widgets", []),
447
- settings_values=selected_assistant.get("settings_values", {}),
448
- )
449
-
450
- session.selected_assistant = new_assistant
451
-
452
- user_identifier = session.user.identifier if session.user else "Anonymous user"
453
- logger.info(
454
- f"{user_identifier} selected assistant {new_assistant.settings_values.get('name', 'Unknown')} for session with id {session.id}"
455
- )
456
-
457
- return new_assistant.to_dict()
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())
@@ -16,9 +16,6 @@
16
16
  "newChatButton": {
17
17
  "newChat": "New Chat"
18
18
  },
19
- "newAssistantButton": {
20
- "newAssistant": "New Assistant"
21
- },
22
19
  "tasklist": {
23
20
  "TaskList": {
24
21
  "title": "🗒️ Task List",
@@ -112,9 +109,6 @@
112
109
  }
113
110
  },
114
111
  "organisms": {
115
- "assistantCreationModal": {
116
- "title": "Create new assistant"
117
- },
118
112
  "chat": {
119
113
  "history": {
120
114
  "index": {