chainlit 2.7.0__py3-none-any.whl → 2.7.1__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.
- {chainlit-2.7.0.dist-info → chainlit-2.7.1.dist-info}/METADATA +1 -1
- chainlit-2.7.1.dist-info/RECORD +4 -0
- chainlit/__init__.py +0 -207
- chainlit/__main__.py +0 -4
- chainlit/_utils.py +0 -8
- chainlit/action.py +0 -33
- chainlit/auth/__init__.py +0 -95
- chainlit/auth/cookie.py +0 -197
- chainlit/auth/jwt.py +0 -42
- chainlit/cache.py +0 -45
- chainlit/callbacks.py +0 -433
- chainlit/chat_context.py +0 -64
- chainlit/chat_settings.py +0 -34
- chainlit/cli/__init__.py +0 -235
- chainlit/config.py +0 -621
- chainlit/context.py +0 -112
- chainlit/data/__init__.py +0 -111
- chainlit/data/acl.py +0 -19
- chainlit/data/base.py +0 -107
- chainlit/data/chainlit_data_layer.py +0 -687
- chainlit/data/dynamodb.py +0 -616
- chainlit/data/literalai.py +0 -501
- chainlit/data/sql_alchemy.py +0 -741
- chainlit/data/storage_clients/__init__.py +0 -0
- chainlit/data/storage_clients/azure.py +0 -84
- chainlit/data/storage_clients/azure_blob.py +0 -94
- chainlit/data/storage_clients/base.py +0 -28
- chainlit/data/storage_clients/gcs.py +0 -101
- chainlit/data/storage_clients/s3.py +0 -88
- chainlit/data/utils.py +0 -29
- chainlit/discord/__init__.py +0 -6
- chainlit/discord/app.py +0 -364
- chainlit/element.py +0 -454
- chainlit/emitter.py +0 -450
- chainlit/hello.py +0 -12
- chainlit/input_widget.py +0 -182
- chainlit/langchain/__init__.py +0 -6
- chainlit/langchain/callbacks.py +0 -682
- chainlit/langflow/__init__.py +0 -25
- chainlit/llama_index/__init__.py +0 -6
- chainlit/llama_index/callbacks.py +0 -206
- chainlit/logger.py +0 -16
- chainlit/markdown.py +0 -57
- chainlit/mcp.py +0 -99
- chainlit/message.py +0 -619
- chainlit/mistralai/__init__.py +0 -50
- chainlit/oauth_providers.py +0 -835
- chainlit/openai/__init__.py +0 -53
- chainlit/py.typed +0 -0
- chainlit/secret.py +0 -9
- chainlit/semantic_kernel/__init__.py +0 -111
- chainlit/server.py +0 -1616
- chainlit/session.py +0 -304
- chainlit/sidebar.py +0 -55
- chainlit/slack/__init__.py +0 -6
- chainlit/slack/app.py +0 -427
- chainlit/socket.py +0 -381
- chainlit/step.py +0 -490
- chainlit/sync.py +0 -43
- chainlit/teams/__init__.py +0 -6
- chainlit/teams/app.py +0 -348
- chainlit/translations/bn.json +0 -214
- chainlit/translations/el-GR.json +0 -214
- chainlit/translations/en-US.json +0 -214
- chainlit/translations/fr-FR.json +0 -214
- chainlit/translations/gu.json +0 -214
- chainlit/translations/he-IL.json +0 -214
- chainlit/translations/hi.json +0 -214
- chainlit/translations/ja.json +0 -214
- chainlit/translations/kn.json +0 -214
- chainlit/translations/ml.json +0 -214
- chainlit/translations/mr.json +0 -214
- chainlit/translations/nl.json +0 -214
- chainlit/translations/ta.json +0 -214
- chainlit/translations/te.json +0 -214
- chainlit/translations/zh-CN.json +0 -214
- chainlit/translations.py +0 -60
- chainlit/types.py +0 -334
- chainlit/user.py +0 -43
- chainlit/user_session.py +0 -153
- chainlit/utils.py +0 -173
- chainlit/version.py +0 -8
- chainlit-2.7.0.dist-info/RECORD +0 -84
- {chainlit-2.7.0.dist-info → chainlit-2.7.1.dist-info}/WHEEL +0 -0
- {chainlit-2.7.0.dist-info → chainlit-2.7.1.dist-info}/entry_points.txt +0 -0
chainlit/session.py
DELETED
|
@@ -1,304 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
import json
|
|
3
|
-
import mimetypes
|
|
4
|
-
import shutil
|
|
5
|
-
import uuid
|
|
6
|
-
from contextlib import AsyncExitStack
|
|
7
|
-
from typing import TYPE_CHECKING, Any, Callable, Deque, Dict, Literal, Optional, Union
|
|
8
|
-
|
|
9
|
-
import aiofiles
|
|
10
|
-
|
|
11
|
-
from chainlit.logger import logger
|
|
12
|
-
from chainlit.types import AskFileSpec, FileReference
|
|
13
|
-
|
|
14
|
-
if TYPE_CHECKING:
|
|
15
|
-
from mcp import ClientSession
|
|
16
|
-
|
|
17
|
-
from chainlit.types import FileDict
|
|
18
|
-
from chainlit.user import PersistedUser, User
|
|
19
|
-
|
|
20
|
-
ClientType = Literal["webapp", "copilot", "teams", "slack", "discord"]
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class JSONEncoderIgnoreNonSerializable(json.JSONEncoder):
|
|
24
|
-
def default(self, o):
|
|
25
|
-
try:
|
|
26
|
-
return super().default(o)
|
|
27
|
-
except TypeError:
|
|
28
|
-
return None
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def clean_metadata(metadata: Dict, max_size: int = 1048576):
|
|
32
|
-
cleaned_metadata = json.loads(
|
|
33
|
-
json.dumps(metadata, cls=JSONEncoderIgnoreNonSerializable, ensure_ascii=False)
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
metadata_size = len(json.dumps(cleaned_metadata).encode("utf-8"))
|
|
37
|
-
if metadata_size > max_size:
|
|
38
|
-
# Redact the metadata if it exceeds the maximum size
|
|
39
|
-
cleaned_metadata = {
|
|
40
|
-
"message": f"Metadata size exceeds the limit of {max_size} bytes. Redacted."
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return cleaned_metadata
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
class BaseSession:
|
|
47
|
-
"""Base object."""
|
|
48
|
-
|
|
49
|
-
thread_id_to_resume: Optional[str] = None
|
|
50
|
-
client_type: ClientType
|
|
51
|
-
current_task: Optional[asyncio.Task] = None
|
|
52
|
-
|
|
53
|
-
def __init__(
|
|
54
|
-
self,
|
|
55
|
-
# Id of the session
|
|
56
|
-
id: str,
|
|
57
|
-
client_type: ClientType,
|
|
58
|
-
# Thread id
|
|
59
|
-
thread_id: Optional[str],
|
|
60
|
-
# Logged-in user information
|
|
61
|
-
user: Optional[Union["User", "PersistedUser"]],
|
|
62
|
-
# Logged-in user token
|
|
63
|
-
token: Optional[str],
|
|
64
|
-
# User specific environment variables. Empty if no user environment variables are required.
|
|
65
|
-
user_env: Optional[Dict[str, str]],
|
|
66
|
-
# WSGI environment variables for the connection request
|
|
67
|
-
environ: Optional[dict[str, Any]] = None,
|
|
68
|
-
# Chat profile selected before the session was created
|
|
69
|
-
chat_profile: Optional[str] = None,
|
|
70
|
-
):
|
|
71
|
-
if thread_id:
|
|
72
|
-
self.thread_id_to_resume = thread_id
|
|
73
|
-
self.thread_id = thread_id or str(uuid.uuid4())
|
|
74
|
-
self.user = user
|
|
75
|
-
self.client_type = client_type
|
|
76
|
-
self.token = token
|
|
77
|
-
self.has_first_interaction = False
|
|
78
|
-
self.user_env = user_env or {}
|
|
79
|
-
self.environ = environ or {}
|
|
80
|
-
self.chat_profile = chat_profile
|
|
81
|
-
|
|
82
|
-
self.files: Dict[str, FileDict] = {}
|
|
83
|
-
self.files_spec: Dict[str, AskFileSpec] = {}
|
|
84
|
-
|
|
85
|
-
self.id = id
|
|
86
|
-
|
|
87
|
-
self.chat_settings: Dict[str, Any] = {}
|
|
88
|
-
|
|
89
|
-
@property
|
|
90
|
-
def files_dir(self):
|
|
91
|
-
from chainlit.config import FILES_DIRECTORY
|
|
92
|
-
|
|
93
|
-
return FILES_DIRECTORY / self.id
|
|
94
|
-
|
|
95
|
-
async def persist_file(
|
|
96
|
-
self,
|
|
97
|
-
name: str,
|
|
98
|
-
mime: str,
|
|
99
|
-
path: Optional[str] = None,
|
|
100
|
-
content: Optional[Union[bytes, str]] = None,
|
|
101
|
-
) -> FileReference:
|
|
102
|
-
if not path and not content:
|
|
103
|
-
raise ValueError(
|
|
104
|
-
"Either path or content must be provided to persist a file"
|
|
105
|
-
)
|
|
106
|
-
|
|
107
|
-
self.files_dir.mkdir(exist_ok=True)
|
|
108
|
-
|
|
109
|
-
file_id = str(uuid.uuid4())
|
|
110
|
-
|
|
111
|
-
file_path = self.files_dir / file_id
|
|
112
|
-
|
|
113
|
-
file_extension = mimetypes.guess_extension(mime)
|
|
114
|
-
|
|
115
|
-
if file_extension:
|
|
116
|
-
file_path = file_path.with_suffix(file_extension)
|
|
117
|
-
|
|
118
|
-
if path:
|
|
119
|
-
# Copy the file from the given path
|
|
120
|
-
async with (
|
|
121
|
-
aiofiles.open(path, "rb") as src,
|
|
122
|
-
aiofiles.open(file_path, "wb") as dst,
|
|
123
|
-
):
|
|
124
|
-
await dst.write(await src.read())
|
|
125
|
-
elif content:
|
|
126
|
-
# Write the provided content to the file
|
|
127
|
-
async with aiofiles.open(file_path, "wb") as buffer:
|
|
128
|
-
if isinstance(content, str):
|
|
129
|
-
content = content.encode("utf-8")
|
|
130
|
-
await buffer.write(content)
|
|
131
|
-
|
|
132
|
-
# Get the file size
|
|
133
|
-
file_size = file_path.stat().st_size
|
|
134
|
-
# Store the file content in memory
|
|
135
|
-
self.files[file_id] = {
|
|
136
|
-
"id": file_id,
|
|
137
|
-
"path": file_path,
|
|
138
|
-
"name": name,
|
|
139
|
-
"type": mime,
|
|
140
|
-
"size": file_size,
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
return {"id": file_id}
|
|
144
|
-
|
|
145
|
-
def to_persistable(self) -> Dict:
|
|
146
|
-
from chainlit.user_session import user_sessions
|
|
147
|
-
|
|
148
|
-
user_session = user_sessions.get(self.id) or {} # type: Dict
|
|
149
|
-
user_session["chat_settings"] = self.chat_settings
|
|
150
|
-
user_session["chat_profile"] = self.chat_profile
|
|
151
|
-
user_session["client_type"] = self.client_type
|
|
152
|
-
metadata = clean_metadata(user_session)
|
|
153
|
-
return metadata
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
class HTTPSession(BaseSession):
|
|
157
|
-
"""Internal HTTP session object. Used to consume Chainlit through API (no websocket)."""
|
|
158
|
-
|
|
159
|
-
def __init__(
|
|
160
|
-
self,
|
|
161
|
-
# Id of the session
|
|
162
|
-
id: str,
|
|
163
|
-
client_type: ClientType,
|
|
164
|
-
# Thread id
|
|
165
|
-
thread_id: Optional[str] = None,
|
|
166
|
-
# Logged-in user information
|
|
167
|
-
user: Optional[Union["User", "PersistedUser"]] = None,
|
|
168
|
-
# Logged-in user token
|
|
169
|
-
token: Optional[str] = None,
|
|
170
|
-
user_env: Optional[Dict[str, str]] = None,
|
|
171
|
-
# WSGI environment variables for the connection request
|
|
172
|
-
environ: Optional[dict[str, Any]] = None,
|
|
173
|
-
):
|
|
174
|
-
super().__init__(
|
|
175
|
-
id=id,
|
|
176
|
-
thread_id=thread_id,
|
|
177
|
-
user=user,
|
|
178
|
-
token=token,
|
|
179
|
-
client_type=client_type,
|
|
180
|
-
user_env=user_env,
|
|
181
|
-
environ=environ,
|
|
182
|
-
)
|
|
183
|
-
|
|
184
|
-
async def delete(self):
|
|
185
|
-
"""Delete the session."""
|
|
186
|
-
if self.files_dir.is_dir():
|
|
187
|
-
shutil.rmtree(self.files_dir)
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
ThreadQueue = Deque[tuple[Callable, object, tuple, Dict]]
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
class WebsocketSession(BaseSession):
|
|
194
|
-
"""Internal web socket session object.
|
|
195
|
-
|
|
196
|
-
A socket id is an ephemeral id that can't be used as a session id
|
|
197
|
-
(as it is for instance regenerated after each reconnection).
|
|
198
|
-
|
|
199
|
-
The Session object store an internal mapping between socket id and
|
|
200
|
-
a server generated session id, allowing to persists session
|
|
201
|
-
between socket reconnection but also retrieving a session by
|
|
202
|
-
socket id for convenience.
|
|
203
|
-
"""
|
|
204
|
-
|
|
205
|
-
to_clear: bool = False
|
|
206
|
-
|
|
207
|
-
mcp_sessions: dict[str, tuple["ClientSession", AsyncExitStack]]
|
|
208
|
-
|
|
209
|
-
def __init__(
|
|
210
|
-
self,
|
|
211
|
-
# Id from the session cookie
|
|
212
|
-
id: str,
|
|
213
|
-
# Associated socket id
|
|
214
|
-
socket_id: str,
|
|
215
|
-
# Function to emit to the client
|
|
216
|
-
emit: Callable[[str, Any], None],
|
|
217
|
-
# Function to emit to the client and wait for a response
|
|
218
|
-
emit_call: Callable[[Literal["ask", "call_fn"], Any, Optional[int]], Any],
|
|
219
|
-
# User specific environment variables. Empty if no user environment variables are required.
|
|
220
|
-
user_env: Dict[str, str],
|
|
221
|
-
client_type: ClientType,
|
|
222
|
-
# WSGI environment variables for the connection request
|
|
223
|
-
environ: Optional[dict[str, Any]] = None,
|
|
224
|
-
# Thread id
|
|
225
|
-
thread_id: Optional[str] = None,
|
|
226
|
-
# Logged-in user information
|
|
227
|
-
user: Optional[Union["User", "PersistedUser"]] = None,
|
|
228
|
-
# Logged-in user token
|
|
229
|
-
token: Optional[str] = None,
|
|
230
|
-
# Chat profile selected before the session was created
|
|
231
|
-
chat_profile: Optional[str] = None,
|
|
232
|
-
):
|
|
233
|
-
super().__init__(
|
|
234
|
-
id=id,
|
|
235
|
-
thread_id=thread_id,
|
|
236
|
-
user=user,
|
|
237
|
-
token=token,
|
|
238
|
-
user_env=user_env,
|
|
239
|
-
client_type=client_type,
|
|
240
|
-
chat_profile=chat_profile,
|
|
241
|
-
environ=environ,
|
|
242
|
-
)
|
|
243
|
-
|
|
244
|
-
self.socket_id = socket_id
|
|
245
|
-
self.emit_call = emit_call
|
|
246
|
-
self.emit = emit
|
|
247
|
-
|
|
248
|
-
self.restored = False
|
|
249
|
-
|
|
250
|
-
self.thread_queues: Dict[str, ThreadQueue] = {}
|
|
251
|
-
self.mcp_sessions = {}
|
|
252
|
-
|
|
253
|
-
ws_sessions_id[self.id] = self
|
|
254
|
-
ws_sessions_sid[socket_id] = self
|
|
255
|
-
|
|
256
|
-
def restore(self, new_socket_id: str):
|
|
257
|
-
"""Associate a new socket id to the session."""
|
|
258
|
-
ws_sessions_sid.pop(self.socket_id, None)
|
|
259
|
-
ws_sessions_sid[new_socket_id] = self
|
|
260
|
-
self.socket_id = new_socket_id
|
|
261
|
-
self.restored = True
|
|
262
|
-
|
|
263
|
-
async def delete(self):
|
|
264
|
-
"""Delete the session."""
|
|
265
|
-
if self.files_dir.is_dir():
|
|
266
|
-
shutil.rmtree(self.files_dir)
|
|
267
|
-
ws_sessions_sid.pop(self.socket_id, None)
|
|
268
|
-
ws_sessions_id.pop(self.id, None)
|
|
269
|
-
|
|
270
|
-
for _, exit_stack in self.mcp_sessions.values():
|
|
271
|
-
try:
|
|
272
|
-
await exit_stack.aclose()
|
|
273
|
-
except Exception:
|
|
274
|
-
pass
|
|
275
|
-
|
|
276
|
-
async def flush_method_queue(self):
|
|
277
|
-
for method_name, queue in self.thread_queues.items():
|
|
278
|
-
while queue:
|
|
279
|
-
method, self, args, kwargs = queue.popleft()
|
|
280
|
-
try:
|
|
281
|
-
await method(self, *args, **kwargs)
|
|
282
|
-
except Exception as e:
|
|
283
|
-
logger.error(f"Error while flushing {method_name}: {e}")
|
|
284
|
-
|
|
285
|
-
@classmethod
|
|
286
|
-
def get(cls, socket_id: str):
|
|
287
|
-
"""Get session by socket id."""
|
|
288
|
-
return ws_sessions_sid.get(socket_id)
|
|
289
|
-
|
|
290
|
-
@classmethod
|
|
291
|
-
def get_by_id(cls, session_id: str):
|
|
292
|
-
"""Get session by session id."""
|
|
293
|
-
return ws_sessions_id.get(session_id)
|
|
294
|
-
|
|
295
|
-
@classmethod
|
|
296
|
-
def require(cls, socket_id: str):
|
|
297
|
-
"""Throws an exception if the session is not found."""
|
|
298
|
-
if session := cls.get(socket_id):
|
|
299
|
-
return session
|
|
300
|
-
raise ValueError("Session not found")
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
ws_sessions_sid: Dict[str, WebsocketSession] = {}
|
|
304
|
-
ws_sessions_id: Dict[str, WebsocketSession] = {}
|
chainlit/sidebar.py
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
from typing import List, Optional
|
|
3
|
-
|
|
4
|
-
from chainlit.context import context
|
|
5
|
-
from chainlit.element import ElementBased
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class ElementSidebar:
|
|
9
|
-
"""Helper class to open/close the element sidebar server side.
|
|
10
|
-
The element sidebar accepts a title and list of elements."""
|
|
11
|
-
|
|
12
|
-
@staticmethod
|
|
13
|
-
async def set_title(title: str):
|
|
14
|
-
"""
|
|
15
|
-
Sets the title of the element sidebar and opens it.
|
|
16
|
-
|
|
17
|
-
The sidebar will automatically open when a title is set using this method.
|
|
18
|
-
|
|
19
|
-
Args:
|
|
20
|
-
title (str): The title to display at the top of the sidebar.
|
|
21
|
-
|
|
22
|
-
Returns:
|
|
23
|
-
None: This method does not return anything.
|
|
24
|
-
"""
|
|
25
|
-
await context.emitter.emit("set_sidebar_title", title)
|
|
26
|
-
|
|
27
|
-
@staticmethod
|
|
28
|
-
async def set_elements(elements: List[ElementBased], key: Optional[str] = None):
|
|
29
|
-
"""
|
|
30
|
-
Sets the elements to display in the sidebar and controls sidebar visibility.
|
|
31
|
-
|
|
32
|
-
This method sends all provided elements to the client and updates the sidebar.
|
|
33
|
-
Passing an empty list will close the sidebar, while passing at least one element
|
|
34
|
-
will open it.
|
|
35
|
-
|
|
36
|
-
Args:
|
|
37
|
-
elements (List[ElementBased]): A list of ElementBased objects to display in the sidebar.
|
|
38
|
-
key (Optional[str], optional): If the sidebar is already opened with the same key, elements will not be replaced.
|
|
39
|
-
|
|
40
|
-
Returns:
|
|
41
|
-
None: This method does not return anything.
|
|
42
|
-
|
|
43
|
-
Note:
|
|
44
|
-
This method first sends each element separately using their send() method,
|
|
45
|
-
then emits an event with all element dictionaries and the optional key.
|
|
46
|
-
"""
|
|
47
|
-
coros = [
|
|
48
|
-
element.send(for_id=element.for_id or "", persist=False)
|
|
49
|
-
for element in elements
|
|
50
|
-
]
|
|
51
|
-
await asyncio.gather(*coros)
|
|
52
|
-
await context.emitter.emit(
|
|
53
|
-
"set_sidebar_elements",
|
|
54
|
-
{"elements": [el.to_dict() for el in elements], "key": key},
|
|
55
|
-
)
|