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/socket.py
DELETED
|
@@ -1,381 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
import json
|
|
3
|
-
from typing import Any, Dict, Literal, Optional, Tuple, Union
|
|
4
|
-
from urllib.parse import unquote
|
|
5
|
-
|
|
6
|
-
from starlette.requests import cookie_parser
|
|
7
|
-
from typing_extensions import TypeAlias
|
|
8
|
-
|
|
9
|
-
from chainlit.auth import (
|
|
10
|
-
get_current_user,
|
|
11
|
-
get_token_from_cookies,
|
|
12
|
-
require_login,
|
|
13
|
-
)
|
|
14
|
-
from chainlit.chat_context import chat_context
|
|
15
|
-
from chainlit.config import config
|
|
16
|
-
from chainlit.context import init_ws_context
|
|
17
|
-
from chainlit.data import get_data_layer
|
|
18
|
-
from chainlit.logger import logger
|
|
19
|
-
from chainlit.message import ErrorMessage, Message
|
|
20
|
-
from chainlit.server import sio
|
|
21
|
-
from chainlit.session import WebsocketSession
|
|
22
|
-
from chainlit.types import InputAudioChunk, InputAudioChunkPayload, MessagePayload
|
|
23
|
-
from chainlit.user import PersistedUser, User
|
|
24
|
-
from chainlit.user_session import user_sessions
|
|
25
|
-
|
|
26
|
-
WSGIEnvironment: TypeAlias = dict[str, Any]
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def restore_existing_session(sid, session_id, emit_fn, emit_call_fn):
|
|
30
|
-
"""Restore a session from the sessionId provided by the client."""
|
|
31
|
-
if session := WebsocketSession.get_by_id(session_id):
|
|
32
|
-
session.restore(new_socket_id=sid)
|
|
33
|
-
session.emit = emit_fn
|
|
34
|
-
session.emit_call = emit_call_fn
|
|
35
|
-
return True
|
|
36
|
-
return False
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
async def persist_user_session(thread_id: str, metadata: Dict):
|
|
40
|
-
if data_layer := get_data_layer():
|
|
41
|
-
await data_layer.update_thread(thread_id=thread_id, metadata=metadata)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
async def resume_thread(session: WebsocketSession):
|
|
45
|
-
data_layer = get_data_layer()
|
|
46
|
-
if not data_layer or not session.user or not session.thread_id_to_resume:
|
|
47
|
-
return
|
|
48
|
-
thread = await data_layer.get_thread(thread_id=session.thread_id_to_resume)
|
|
49
|
-
if not thread:
|
|
50
|
-
return
|
|
51
|
-
|
|
52
|
-
author = thread.get("userIdentifier")
|
|
53
|
-
user_is_author = author == session.user.identifier
|
|
54
|
-
|
|
55
|
-
if user_is_author:
|
|
56
|
-
metadata = thread.get("metadata") or {}
|
|
57
|
-
if isinstance(metadata, str):
|
|
58
|
-
metadata = json.loads(metadata)
|
|
59
|
-
user_sessions[session.id] = metadata.copy()
|
|
60
|
-
if chat_profile := metadata.get("chat_profile"):
|
|
61
|
-
session.chat_profile = chat_profile
|
|
62
|
-
if chat_settings := metadata.get("chat_settings"):
|
|
63
|
-
session.chat_settings = chat_settings
|
|
64
|
-
|
|
65
|
-
return thread
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
def load_user_env(user_env):
|
|
69
|
-
if user_env:
|
|
70
|
-
user_env_dict = json.loads(user_env)
|
|
71
|
-
# Check user env
|
|
72
|
-
if config.project.user_env:
|
|
73
|
-
if not user_env_dict:
|
|
74
|
-
raise ConnectionRefusedError("Missing user environment variables")
|
|
75
|
-
# Check if requested user environment variables are provided
|
|
76
|
-
for key in config.project.user_env:
|
|
77
|
-
if key not in user_env_dict:
|
|
78
|
-
raise ConnectionRefusedError(
|
|
79
|
-
"Missing user environment variable: " + key
|
|
80
|
-
)
|
|
81
|
-
return user_env_dict
|
|
82
|
-
|
|
83
|
-
|
|
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 get_token_from_cookies(cookies)
|
|
88
|
-
|
|
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")
|
|
122
|
-
|
|
123
|
-
# Session scoped function to emit to the client
|
|
124
|
-
def emit_fn(event, data):
|
|
125
|
-
return sio.emit(event, data, to=sid)
|
|
126
|
-
|
|
127
|
-
# Session scoped function to emit to the client and wait for a response
|
|
128
|
-
def emit_call_fn(event: Literal["ask", "call_fn"], data, timeout):
|
|
129
|
-
return sio.call(event, data, timeout=timeout, to=sid)
|
|
130
|
-
|
|
131
|
-
session_id = auth.get("sessionId")
|
|
132
|
-
if restore_existing_session(sid, session_id, emit_fn, emit_call_fn):
|
|
133
|
-
return True
|
|
134
|
-
|
|
135
|
-
user_env_string = auth.get("userEnv")
|
|
136
|
-
user_env = load_user_env(user_env_string)
|
|
137
|
-
|
|
138
|
-
client_type = auth.get("clientType")
|
|
139
|
-
url_encoded_chat_profile = auth.get("chatProfile")
|
|
140
|
-
chat_profile = (
|
|
141
|
-
unquote(url_encoded_chat_profile) if url_encoded_chat_profile else None
|
|
142
|
-
)
|
|
143
|
-
|
|
144
|
-
WebsocketSession(
|
|
145
|
-
id=session_id,
|
|
146
|
-
socket_id=sid,
|
|
147
|
-
emit=emit_fn,
|
|
148
|
-
emit_call=emit_call_fn,
|
|
149
|
-
client_type=client_type,
|
|
150
|
-
user_env=user_env,
|
|
151
|
-
user=user,
|
|
152
|
-
token=token,
|
|
153
|
-
chat_profile=chat_profile,
|
|
154
|
-
thread_id=auth.get("threadId"),
|
|
155
|
-
environ=environ,
|
|
156
|
-
)
|
|
157
|
-
|
|
158
|
-
return True
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
@sio.on("connection_successful") # pyright: ignore [reportOptionalCall]
|
|
162
|
-
async def connection_successful(sid):
|
|
163
|
-
context = init_ws_context(sid)
|
|
164
|
-
|
|
165
|
-
await context.emitter.task_end()
|
|
166
|
-
await context.emitter.clear("clear_ask")
|
|
167
|
-
await context.emitter.clear("clear_call_fn")
|
|
168
|
-
|
|
169
|
-
if context.session.restored:
|
|
170
|
-
return
|
|
171
|
-
|
|
172
|
-
if context.session.thread_id_to_resume and config.code.on_chat_resume:
|
|
173
|
-
thread = await resume_thread(context.session)
|
|
174
|
-
if thread:
|
|
175
|
-
context.session.has_first_interaction = True
|
|
176
|
-
await context.emitter.emit(
|
|
177
|
-
"first_interaction",
|
|
178
|
-
{"interaction": "resume", "thread_id": thread.get("id")},
|
|
179
|
-
)
|
|
180
|
-
await config.code.on_chat_resume(thread)
|
|
181
|
-
|
|
182
|
-
for step in thread.get("steps", []):
|
|
183
|
-
if "message" in step["type"]:
|
|
184
|
-
chat_context.add(Message.from_dict(step))
|
|
185
|
-
|
|
186
|
-
await context.emitter.resume_thread(thread)
|
|
187
|
-
return
|
|
188
|
-
else:
|
|
189
|
-
await context.emitter.send_resume_thread_error("Thread not found.")
|
|
190
|
-
|
|
191
|
-
if config.code.on_chat_start:
|
|
192
|
-
task = asyncio.create_task(config.code.on_chat_start())
|
|
193
|
-
context.session.current_task = task
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
@sio.on("clear_session") # pyright: ignore [reportOptionalCall]
|
|
197
|
-
async def clean_session(sid):
|
|
198
|
-
session = WebsocketSession.get(sid)
|
|
199
|
-
if session:
|
|
200
|
-
session.to_clear = True
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
@sio.on("disconnect") # pyright: ignore [reportOptionalCall]
|
|
204
|
-
async def disconnect(sid):
|
|
205
|
-
session = WebsocketSession.get(sid)
|
|
206
|
-
|
|
207
|
-
if not session:
|
|
208
|
-
return
|
|
209
|
-
|
|
210
|
-
init_ws_context(session)
|
|
211
|
-
|
|
212
|
-
if config.code.on_chat_end:
|
|
213
|
-
await config.code.on_chat_end()
|
|
214
|
-
|
|
215
|
-
if session.thread_id and session.has_first_interaction:
|
|
216
|
-
await persist_user_session(session.thread_id, session.to_persistable())
|
|
217
|
-
|
|
218
|
-
async def clear(_sid):
|
|
219
|
-
if session := WebsocketSession.get(_sid):
|
|
220
|
-
# Clean up the user session
|
|
221
|
-
if session.id in user_sessions:
|
|
222
|
-
user_sessions.pop(session.id)
|
|
223
|
-
# Clean up the session
|
|
224
|
-
await session.delete()
|
|
225
|
-
|
|
226
|
-
if session.to_clear:
|
|
227
|
-
await clear(sid)
|
|
228
|
-
else:
|
|
229
|
-
|
|
230
|
-
async def clear_on_timeout(_sid):
|
|
231
|
-
await asyncio.sleep(config.project.session_timeout)
|
|
232
|
-
await clear(_sid)
|
|
233
|
-
|
|
234
|
-
asyncio.ensure_future(clear_on_timeout(sid))
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
@sio.on("stop") # pyright: ignore [reportOptionalCall]
|
|
238
|
-
async def stop(sid):
|
|
239
|
-
if session := WebsocketSession.get(sid):
|
|
240
|
-
init_ws_context(session)
|
|
241
|
-
await Message(content="Task manually stopped.").send()
|
|
242
|
-
|
|
243
|
-
if session.current_task:
|
|
244
|
-
session.current_task.cancel()
|
|
245
|
-
|
|
246
|
-
if config.code.on_stop:
|
|
247
|
-
await config.code.on_stop()
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
async def process_message(session: WebsocketSession, payload: MessagePayload):
|
|
251
|
-
"""Process a message from the user."""
|
|
252
|
-
try:
|
|
253
|
-
context = init_ws_context(session)
|
|
254
|
-
await context.emitter.task_start()
|
|
255
|
-
message = await context.emitter.process_message(payload)
|
|
256
|
-
|
|
257
|
-
if config.code.on_message:
|
|
258
|
-
await asyncio.sleep(0.001)
|
|
259
|
-
await config.code.on_message(message)
|
|
260
|
-
except asyncio.CancelledError:
|
|
261
|
-
pass
|
|
262
|
-
except Exception as e:
|
|
263
|
-
logger.exception(e)
|
|
264
|
-
await ErrorMessage(
|
|
265
|
-
author="Error", content=str(e) or e.__class__.__name__
|
|
266
|
-
).send()
|
|
267
|
-
finally:
|
|
268
|
-
await context.emitter.task_end()
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
@sio.on("edit_message") # pyright: ignore [reportOptionalCall]
|
|
272
|
-
async def edit_message(sid, payload: MessagePayload):
|
|
273
|
-
"""Handle a message sent by the User."""
|
|
274
|
-
session = WebsocketSession.require(sid)
|
|
275
|
-
context = init_ws_context(session)
|
|
276
|
-
|
|
277
|
-
messages = chat_context.get()
|
|
278
|
-
|
|
279
|
-
orig_message = None
|
|
280
|
-
|
|
281
|
-
for message in messages:
|
|
282
|
-
if orig_message:
|
|
283
|
-
await message.remove()
|
|
284
|
-
|
|
285
|
-
if message.id == payload["message"]["id"]:
|
|
286
|
-
message.content = payload["message"]["output"]
|
|
287
|
-
await message.update()
|
|
288
|
-
orig_message = message
|
|
289
|
-
|
|
290
|
-
await context.emitter.task_start()
|
|
291
|
-
|
|
292
|
-
if config.code.on_message:
|
|
293
|
-
try:
|
|
294
|
-
await config.code.on_message(orig_message)
|
|
295
|
-
except asyncio.CancelledError:
|
|
296
|
-
pass
|
|
297
|
-
finally:
|
|
298
|
-
await context.emitter.task_end()
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
@sio.on("client_message") # pyright: ignore [reportOptionalCall]
|
|
302
|
-
async def message(sid, payload: MessagePayload):
|
|
303
|
-
"""Handle a message sent by the User."""
|
|
304
|
-
session = WebsocketSession.require(sid)
|
|
305
|
-
|
|
306
|
-
task = asyncio.create_task(process_message(session, payload))
|
|
307
|
-
session.current_task = task
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
@sio.on("window_message") # pyright: ignore [reportOptionalCall]
|
|
311
|
-
async def window_message(sid, data):
|
|
312
|
-
"""Handle a message send by the host window."""
|
|
313
|
-
session = WebsocketSession.require(sid)
|
|
314
|
-
init_ws_context(session)
|
|
315
|
-
|
|
316
|
-
if config.code.on_window_message:
|
|
317
|
-
try:
|
|
318
|
-
await config.code.on_window_message(data)
|
|
319
|
-
except asyncio.CancelledError:
|
|
320
|
-
pass
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
@sio.on("audio_start") # pyright: ignore [reportOptionalCall]
|
|
324
|
-
async def audio_start(sid):
|
|
325
|
-
"""Handle audio init."""
|
|
326
|
-
session = WebsocketSession.require(sid)
|
|
327
|
-
|
|
328
|
-
context = init_ws_context(session)
|
|
329
|
-
if config.features.audio.enabled:
|
|
330
|
-
connected = bool(await config.code.on_audio_start())
|
|
331
|
-
connection_state = "on" if connected else "off"
|
|
332
|
-
await context.emitter.update_audio_connection(connection_state)
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
@sio.on("audio_chunk")
|
|
336
|
-
async def audio_chunk(sid, payload: InputAudioChunkPayload):
|
|
337
|
-
"""Handle an audio chunk sent by the user."""
|
|
338
|
-
session = WebsocketSession.require(sid)
|
|
339
|
-
|
|
340
|
-
init_ws_context(session)
|
|
341
|
-
|
|
342
|
-
if config.features.audio.enabled:
|
|
343
|
-
asyncio.create_task(config.code.on_audio_chunk(InputAudioChunk(**payload)))
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
@sio.on("audio_end")
|
|
347
|
-
async def audio_end(sid):
|
|
348
|
-
"""Handle the end of the audio stream."""
|
|
349
|
-
session = WebsocketSession.require(sid)
|
|
350
|
-
try:
|
|
351
|
-
context = init_ws_context(session)
|
|
352
|
-
await context.emitter.task_start()
|
|
353
|
-
|
|
354
|
-
if not session.has_first_interaction:
|
|
355
|
-
session.has_first_interaction = True
|
|
356
|
-
asyncio.create_task(context.emitter.init_thread("audio"))
|
|
357
|
-
|
|
358
|
-
if config.features.audio.enabled:
|
|
359
|
-
await config.code.on_audio_end()
|
|
360
|
-
|
|
361
|
-
except asyncio.CancelledError:
|
|
362
|
-
pass
|
|
363
|
-
except Exception as e:
|
|
364
|
-
logger.exception(e)
|
|
365
|
-
await ErrorMessage(
|
|
366
|
-
author="Error", content=str(e) or e.__class__.__name__
|
|
367
|
-
).send()
|
|
368
|
-
finally:
|
|
369
|
-
await context.emitter.task_end()
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
@sio.on("chat_settings_change")
|
|
373
|
-
async def change_settings(sid, settings: Dict[str, Any]):
|
|
374
|
-
"""Handle change settings submit from the UI."""
|
|
375
|
-
context = init_ws_context(sid)
|
|
376
|
-
|
|
377
|
-
for key, value in settings.items():
|
|
378
|
-
context.session.chat_settings[key] = value
|
|
379
|
-
|
|
380
|
-
if config.code.on_settings_update:
|
|
381
|
-
await config.code.on_settings_update(settings)
|