chainlit 1.0.401__py3-none-any.whl → 2.0.3__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/__init__.py +98 -279
- chainlit/_utils.py +8 -0
- chainlit/action.py +12 -10
- chainlit/{auth.py → auth/__init__.py} +28 -36
- chainlit/auth/cookie.py +122 -0
- chainlit/auth/jwt.py +39 -0
- chainlit/cache.py +4 -6
- chainlit/callbacks.py +362 -0
- chainlit/chat_context.py +64 -0
- chainlit/chat_settings.py +3 -1
- chainlit/cli/__init__.py +77 -8
- chainlit/config.py +181 -101
- chainlit/context.py +42 -13
- chainlit/copilot/dist/index.js +8750 -903
- chainlit/data/__init__.py +101 -416
- chainlit/data/acl.py +6 -2
- chainlit/data/base.py +107 -0
- chainlit/data/chainlit_data_layer.py +608 -0
- chainlit/data/dynamodb.py +590 -0
- chainlit/data/literalai.py +500 -0
- chainlit/data/sql_alchemy.py +721 -0
- chainlit/data/storage_clients/__init__.py +0 -0
- chainlit/data/storage_clients/azure.py +81 -0
- chainlit/data/storage_clients/azure_blob.py +89 -0
- chainlit/data/storage_clients/base.py +26 -0
- chainlit/data/storage_clients/gcs.py +88 -0
- chainlit/data/storage_clients/s3.py +75 -0
- chainlit/data/utils.py +29 -0
- chainlit/discord/__init__.py +6 -0
- chainlit/discord/app.py +354 -0
- chainlit/element.py +91 -33
- chainlit/emitter.py +80 -29
- chainlit/frontend/dist/assets/DailyMotion-C_XC7xJI.js +1 -0
- chainlit/frontend/dist/assets/Dataframe-Cs4l4hA1.js +22 -0
- chainlit/frontend/dist/assets/Facebook-CUeCH7hk.js +1 -0
- chainlit/frontend/dist/assets/FilePlayer-CB-fYkx8.js +1 -0
- chainlit/frontend/dist/assets/Kaltura-YX6qaq72.js +1 -0
- chainlit/frontend/dist/assets/Mixcloud-DGV0ldjP.js +1 -0
- chainlit/frontend/dist/assets/Mux-CmRss5oc.js +1 -0
- chainlit/frontend/dist/assets/Preview-DBVJn7-H.js +1 -0
- chainlit/frontend/dist/assets/SoundCloud-qLUb18oY.js +1 -0
- chainlit/frontend/dist/assets/Streamable-BvYP7bFp.js +1 -0
- chainlit/frontend/dist/assets/Twitch-CTHt-sGZ.js +1 -0
- chainlit/frontend/dist/assets/Vidyard-B-0mCJbm.js +1 -0
- chainlit/frontend/dist/assets/Vimeo-Dnp7ri8q.js +1 -0
- chainlit/frontend/dist/assets/Wistia-DW0x_UBn.js +1 -0
- chainlit/frontend/dist/assets/YouTube--98FipvA.js +1 -0
- chainlit/frontend/dist/assets/index-D71nZ46o.js +8665 -0
- chainlit/frontend/dist/assets/index-g8LTJwwr.css +1 -0
- chainlit/frontend/dist/assets/react-plotly-Cn_BQTQw.js +3484 -0
- chainlit/frontend/dist/index.html +2 -4
- chainlit/haystack/callbacks.py +4 -7
- chainlit/input_widget.py +8 -4
- chainlit/langchain/callbacks.py +103 -68
- chainlit/langflow/__init__.py +1 -0
- chainlit/llama_index/callbacks.py +65 -40
- chainlit/markdown.py +22 -6
- chainlit/message.py +54 -56
- chainlit/mistralai/__init__.py +50 -0
- chainlit/oauth_providers.py +266 -8
- chainlit/openai/__init__.py +10 -18
- chainlit/secret.py +1 -1
- chainlit/server.py +789 -228
- chainlit/session.py +108 -90
- chainlit/slack/__init__.py +6 -0
- chainlit/slack/app.py +397 -0
- chainlit/socket.py +199 -116
- chainlit/step.py +141 -89
- chainlit/sync.py +2 -1
- chainlit/teams/__init__.py +6 -0
- chainlit/teams/app.py +338 -0
- chainlit/translations/bn.json +235 -0
- chainlit/translations/en-US.json +83 -4
- chainlit/translations/gu.json +235 -0
- chainlit/translations/he-IL.json +235 -0
- chainlit/translations/hi.json +235 -0
- chainlit/translations/kn.json +235 -0
- chainlit/translations/ml.json +235 -0
- chainlit/translations/mr.json +235 -0
- chainlit/translations/nl-NL.json +233 -0
- chainlit/translations/ta.json +235 -0
- chainlit/translations/te.json +235 -0
- chainlit/translations/zh-CN.json +233 -0
- chainlit/translations.py +60 -0
- chainlit/types.py +133 -28
- chainlit/user.py +14 -3
- chainlit/user_session.py +6 -3
- chainlit/utils.py +52 -5
- chainlit/version.py +3 -2
- {chainlit-1.0.401.dist-info → chainlit-2.0.3.dist-info}/METADATA +48 -50
- chainlit-2.0.3.dist-info/RECORD +106 -0
- chainlit/cli/utils.py +0 -24
- chainlit/frontend/dist/assets/index-9711593e.js +0 -723
- chainlit/frontend/dist/assets/index-d088547c.css +0 -1
- chainlit/frontend/dist/assets/react-plotly-d8762cc2.js +0 -3602
- chainlit/playground/__init__.py +0 -2
- chainlit/playground/config.py +0 -40
- chainlit/playground/provider.py +0 -108
- chainlit/playground/providers/__init__.py +0 -13
- chainlit/playground/providers/anthropic.py +0 -118
- chainlit/playground/providers/huggingface.py +0 -75
- chainlit/playground/providers/langchain.py +0 -89
- chainlit/playground/providers/openai.py +0 -408
- chainlit/playground/providers/vertexai.py +0 -171
- chainlit/translations/pt-BR.json +0 -155
- chainlit-1.0.401.dist-info/RECORD +0 -66
- /chainlit/copilot/dist/assets/{logo_dark-2a3cf740.svg → logo_dark-IkGJ_IwC.svg} +0 -0
- /chainlit/copilot/dist/assets/{logo_light-b078e7bc.svg → logo_light-Bb_IPh6r.svg} +0 -0
- /chainlit/frontend/dist/assets/{logo_dark-2a3cf740.svg → logo_dark-IkGJ_IwC.svg} +0 -0
- /chainlit/frontend/dist/assets/{logo_light-b078e7bc.svg → logo_light-Bb_IPh6r.svg} +0 -0
- {chainlit-1.0.401.dist-info → chainlit-2.0.3.dist-info}/WHEEL +0 -0
- {chainlit-1.0.401.dist-info → chainlit-2.0.3.dist-info}/entry_points.txt +0 -0
chainlit/socket.py
CHANGED
|
@@ -1,22 +1,27 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import json
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
|
|
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
|
|
6
8
|
|
|
7
|
-
from chainlit.action import Action
|
|
8
9
|
from chainlit.auth import get_current_user, require_login
|
|
10
|
+
from chainlit.chat_context import chat_context
|
|
9
11
|
from chainlit.config import config
|
|
10
12
|
from chainlit.context import init_ws_context
|
|
11
13
|
from chainlit.data import get_data_layer
|
|
12
14
|
from chainlit.logger import logger
|
|
13
15
|
from chainlit.message import ErrorMessage, Message
|
|
14
|
-
from chainlit.server import
|
|
16
|
+
from chainlit.server import sio
|
|
15
17
|
from chainlit.session import WebsocketSession
|
|
16
18
|
from chainlit.telemetry import trace_event
|
|
17
|
-
from chainlit.types import
|
|
19
|
+
from chainlit.types import InputAudioChunk, InputAudioChunkPayload, MessagePayload
|
|
20
|
+
from chainlit.user import PersistedUser, User
|
|
18
21
|
from chainlit.user_session import user_sessions
|
|
19
22
|
|
|
23
|
+
WSGIEnvironment: TypeAlias = dict[str, Any]
|
|
24
|
+
|
|
20
25
|
|
|
21
26
|
def restore_existing_session(sid, session_id, emit_fn, emit_call_fn):
|
|
22
27
|
"""Restore a session from the sessionId provided by the client."""
|
|
@@ -42,11 +47,11 @@ async def resume_thread(session: WebsocketSession):
|
|
|
42
47
|
if not thread:
|
|
43
48
|
return
|
|
44
49
|
|
|
45
|
-
author = thread.get("
|
|
50
|
+
author = thread.get("userIdentifier")
|
|
46
51
|
user_is_author = author == session.user.identifier
|
|
47
52
|
|
|
48
53
|
if user_is_author:
|
|
49
|
-
metadata = thread.get("metadata"
|
|
54
|
+
metadata = thread.get("metadata") or {}
|
|
50
55
|
user_sessions[session.id] = metadata.copy()
|
|
51
56
|
if chat_profile := metadata.get("chat_profile"):
|
|
52
57
|
session.chat_profile = chat_profile
|
|
@@ -75,68 +80,69 @@ def load_user_env(user_env):
|
|
|
75
80
|
return user_env
|
|
76
81
|
|
|
77
82
|
|
|
78
|
-
def
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
83
|
+
def _get_token_from_cookie(environ: WSGIEnvironment) -> Optional[str]:
|
|
84
|
+
if cookie_header := environ.get("HTTP_COOKIE", None):
|
|
85
|
+
cookies = cookie_parser(cookie_header)
|
|
86
|
+
return cookies.get("access_token", None)
|
|
82
87
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
88
|
+
return None
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _get_token(environ: WSGIEnvironment, auth: dict) -> Optional[str]:
|
|
92
|
+
"""Take WSGI environ, return access token."""
|
|
93
|
+
return _get_token_from_cookie(environ)
|
|
89
94
|
|
|
90
|
-
except StopIteration:
|
|
91
|
-
return str(uuid.uuid5(uuid.NAMESPACE_DNS, ip))
|
|
92
95
|
|
|
96
|
+
async def _authenticate_connection(
|
|
97
|
+
environ,
|
|
98
|
+
auth,
|
|
99
|
+
) -> Union[Tuple[Union[User, PersistedUser], str], Tuple[None, None]]:
|
|
100
|
+
if token := _get_token(environ, auth):
|
|
101
|
+
user = await get_current_user(token=token)
|
|
102
|
+
if user:
|
|
103
|
+
return user, token
|
|
93
104
|
|
|
94
|
-
|
|
105
|
+
return None, None
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@sio.on("connect") # pyright: ignore [reportOptionalCall]
|
|
95
109
|
async def connect(sid, environ, auth):
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
authorization_header = environ.get("HTTP_AUTHORIZATION")
|
|
108
|
-
token = authorization_header.split(" ")[1] if authorization_header else None
|
|
109
|
-
user = await get_current_user(token=token)
|
|
110
|
-
except Exception as e:
|
|
111
|
-
logger.info("Authentication failed")
|
|
112
|
-
return False
|
|
110
|
+
user = token = None
|
|
111
|
+
|
|
112
|
+
if require_login():
|
|
113
|
+
try:
|
|
114
|
+
user, token = await _authenticate_connection(environ, auth)
|
|
115
|
+
except Exception as e:
|
|
116
|
+
logger.exception("Exception authenticating connection: %s", e)
|
|
117
|
+
|
|
118
|
+
if not user:
|
|
119
|
+
logger.error("Authentication failed in websocket connect.")
|
|
120
|
+
raise ConnectionRefusedError("authentication failed")
|
|
113
121
|
|
|
114
122
|
# Session scoped function to emit to the client
|
|
115
123
|
def emit_fn(event, data):
|
|
116
|
-
|
|
117
|
-
if session.should_stop:
|
|
118
|
-
session.should_stop = False
|
|
119
|
-
raise InterruptedError("Task stopped by user")
|
|
120
|
-
return socket.emit(event, data, to=sid)
|
|
124
|
+
return sio.emit(event, data, to=sid)
|
|
121
125
|
|
|
122
126
|
# Session scoped function to emit to the client and wait for a response
|
|
123
127
|
def emit_call_fn(event: Literal["ask", "call_fn"], data, timeout):
|
|
124
|
-
|
|
125
|
-
if session.should_stop:
|
|
126
|
-
session.should_stop = False
|
|
127
|
-
raise InterruptedError("Task stopped by user")
|
|
128
|
-
return socket.call(event, data, timeout=timeout, to=sid)
|
|
128
|
+
return sio.call(event, data, timeout=timeout, to=sid)
|
|
129
129
|
|
|
130
|
-
session_id =
|
|
130
|
+
session_id = auth.get("sessionId")
|
|
131
131
|
if restore_existing_session(sid, session_id, emit_fn, emit_call_fn):
|
|
132
132
|
return True
|
|
133
133
|
|
|
134
|
-
user_env_string =
|
|
134
|
+
user_env_string = auth.get("userEnv")
|
|
135
135
|
user_env = load_user_env(user_env_string)
|
|
136
136
|
|
|
137
|
-
client_type =
|
|
137
|
+
client_type = auth.get("clientType")
|
|
138
|
+
http_referer = environ.get("HTTP_REFERER")
|
|
139
|
+
http_cookie = environ.get("HTTP_COOKIE")
|
|
140
|
+
url_encoded_chat_profile = auth.get("chatProfile")
|
|
141
|
+
chat_profile = (
|
|
142
|
+
unquote(url_encoded_chat_profile) if url_encoded_chat_profile else None
|
|
143
|
+
)
|
|
138
144
|
|
|
139
|
-
|
|
145
|
+
WebsocketSession(
|
|
140
146
|
id=session_id,
|
|
141
147
|
socket_id=sid,
|
|
142
148
|
emit=emit_fn,
|
|
@@ -145,101 +151,119 @@ async def connect(sid, environ, auth):
|
|
|
145
151
|
user_env=user_env,
|
|
146
152
|
user=user,
|
|
147
153
|
token=token,
|
|
148
|
-
chat_profile=
|
|
149
|
-
thread_id=
|
|
154
|
+
chat_profile=chat_profile,
|
|
155
|
+
thread_id=auth.get("threadId"),
|
|
156
|
+
languages=environ.get("HTTP_ACCEPT_LANGUAGE"),
|
|
157
|
+
http_referer=http_referer,
|
|
158
|
+
http_cookie=http_cookie,
|
|
150
159
|
)
|
|
151
160
|
|
|
152
161
|
trace_event("connection_successful")
|
|
153
162
|
return True
|
|
154
163
|
|
|
155
164
|
|
|
156
|
-
@
|
|
165
|
+
@sio.on("connection_successful") # pyright: ignore [reportOptionalCall]
|
|
157
166
|
async def connection_successful(sid):
|
|
158
167
|
context = init_ws_context(sid)
|
|
159
168
|
|
|
160
|
-
if context.session.restored:
|
|
161
|
-
return
|
|
162
|
-
|
|
163
169
|
await context.emitter.task_end()
|
|
164
170
|
await context.emitter.clear("clear_ask")
|
|
165
171
|
await context.emitter.clear("clear_call_fn")
|
|
166
172
|
|
|
173
|
+
if context.session.restored:
|
|
174
|
+
return
|
|
175
|
+
|
|
167
176
|
if context.session.thread_id_to_resume and config.code.on_chat_resume:
|
|
168
177
|
thread = await resume_thread(context.session)
|
|
169
178
|
if thread:
|
|
170
179
|
context.session.has_first_interaction = True
|
|
171
|
-
await context.emitter.emit(
|
|
172
|
-
|
|
180
|
+
await context.emitter.emit(
|
|
181
|
+
"first_interaction",
|
|
182
|
+
{"interaction": "resume", "thread_id": thread.get("id")},
|
|
183
|
+
)
|
|
173
184
|
await config.code.on_chat_resume(thread)
|
|
185
|
+
|
|
186
|
+
for step in thread.get("steps", []):
|
|
187
|
+
if "message" in step["type"]:
|
|
188
|
+
chat_context.add(Message.from_dict(step))
|
|
189
|
+
|
|
190
|
+
await context.emitter.resume_thread(thread)
|
|
174
191
|
return
|
|
192
|
+
else:
|
|
193
|
+
await context.emitter.send_resume_thread_error("Thread not found.")
|
|
175
194
|
|
|
176
195
|
if config.code.on_chat_start:
|
|
177
|
-
|
|
196
|
+
task = asyncio.create_task(config.code.on_chat_start())
|
|
197
|
+
context.session.current_task = task
|
|
178
198
|
|
|
179
199
|
|
|
180
|
-
@
|
|
200
|
+
@sio.on("clear_session") # pyright: ignore [reportOptionalCall]
|
|
181
201
|
async def clean_session(sid):
|
|
182
|
-
|
|
202
|
+
session = WebsocketSession.get(sid)
|
|
203
|
+
if session:
|
|
204
|
+
session.to_clear = True
|
|
183
205
|
|
|
184
206
|
|
|
185
|
-
@
|
|
186
|
-
async def disconnect(sid
|
|
207
|
+
@sio.on("disconnect") # pyright: ignore [reportOptionalCall]
|
|
208
|
+
async def disconnect(sid):
|
|
187
209
|
session = WebsocketSession.get(sid)
|
|
188
|
-
if session:
|
|
189
|
-
init_ws_context(session)
|
|
190
210
|
|
|
191
|
-
if
|
|
211
|
+
if not session:
|
|
212
|
+
return
|
|
213
|
+
|
|
214
|
+
init_ws_context(session)
|
|
215
|
+
|
|
216
|
+
if config.code.on_chat_end:
|
|
192
217
|
await config.code.on_chat_end()
|
|
193
218
|
|
|
194
|
-
if session
|
|
219
|
+
if session.thread_id and session.has_first_interaction:
|
|
195
220
|
await persist_user_session(session.thread_id, session.to_persistable())
|
|
196
221
|
|
|
197
|
-
def clear():
|
|
198
|
-
if session := WebsocketSession.get(
|
|
222
|
+
def clear(_sid):
|
|
223
|
+
if session := WebsocketSession.get(_sid):
|
|
199
224
|
# Clean up the user session
|
|
200
225
|
if session.id in user_sessions:
|
|
201
226
|
user_sessions.pop(session.id)
|
|
202
227
|
# Clean up the session
|
|
203
228
|
session.delete()
|
|
204
229
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
clear()
|
|
208
|
-
|
|
209
|
-
if force_clear:
|
|
210
|
-
clear()
|
|
230
|
+
if session.to_clear:
|
|
231
|
+
clear(sid)
|
|
211
232
|
else:
|
|
233
|
+
|
|
234
|
+
async def clear_on_timeout(_sid):
|
|
235
|
+
await asyncio.sleep(config.project.session_timeout)
|
|
236
|
+
clear(_sid)
|
|
237
|
+
|
|
212
238
|
asyncio.ensure_future(clear_on_timeout(sid))
|
|
213
239
|
|
|
214
240
|
|
|
215
|
-
@
|
|
241
|
+
@sio.on("stop") # pyright: ignore [reportOptionalCall]
|
|
216
242
|
async def stop(sid):
|
|
217
243
|
if session := WebsocketSession.get(sid):
|
|
218
244
|
trace_event("stop_task")
|
|
219
245
|
|
|
220
246
|
init_ws_context(session)
|
|
221
|
-
await Message(
|
|
222
|
-
author="System", content="Task stopped by the user.", disable_feedback=True
|
|
223
|
-
).send()
|
|
247
|
+
await Message(content="Task manually stopped.").send()
|
|
224
248
|
|
|
225
|
-
session.
|
|
249
|
+
if session.current_task:
|
|
250
|
+
session.current_task.cancel()
|
|
226
251
|
|
|
227
252
|
if config.code.on_stop:
|
|
228
253
|
await config.code.on_stop()
|
|
229
254
|
|
|
230
255
|
|
|
231
|
-
async def process_message(session: WebsocketSession, payload:
|
|
256
|
+
async def process_message(session: WebsocketSession, payload: MessagePayload):
|
|
232
257
|
"""Process a message from the user."""
|
|
233
258
|
try:
|
|
234
259
|
context = init_ws_context(session)
|
|
235
260
|
await context.emitter.task_start()
|
|
236
|
-
message = await context.emitter.
|
|
261
|
+
message = await context.emitter.process_message(payload)
|
|
237
262
|
|
|
238
263
|
if config.code.on_message:
|
|
239
|
-
|
|
240
|
-
time.sleep(0.001)
|
|
264
|
+
await asyncio.sleep(0.001)
|
|
241
265
|
await config.code.on_message(message)
|
|
242
|
-
except
|
|
266
|
+
except asyncio.CancelledError:
|
|
243
267
|
pass
|
|
244
268
|
except Exception as e:
|
|
245
269
|
logger.exception(e)
|
|
@@ -250,49 +274,108 @@ async def process_message(session: WebsocketSession, payload: UIMessagePayload):
|
|
|
250
274
|
await context.emitter.task_end()
|
|
251
275
|
|
|
252
276
|
|
|
253
|
-
@
|
|
254
|
-
async def
|
|
277
|
+
@sio.on("edit_message") # pyright: ignore [reportOptionalCall]
|
|
278
|
+
async def edit_message(sid, payload: MessagePayload):
|
|
255
279
|
"""Handle a message sent by the User."""
|
|
256
280
|
session = WebsocketSession.require(sid)
|
|
257
|
-
|
|
281
|
+
context = init_ws_context(session)
|
|
258
282
|
|
|
259
|
-
|
|
283
|
+
messages = chat_context.get()
|
|
260
284
|
|
|
285
|
+
orig_message = None
|
|
261
286
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
res = await callback(action)
|
|
266
|
-
return res
|
|
267
|
-
else:
|
|
268
|
-
logger.warning("No callback found for action %s", action.name)
|
|
287
|
+
for message in messages:
|
|
288
|
+
if orig_message:
|
|
289
|
+
await message.remove()
|
|
269
290
|
|
|
291
|
+
if message.id == payload["message"]["id"]:
|
|
292
|
+
message.content = payload["message"]["output"]
|
|
293
|
+
await message.update()
|
|
294
|
+
orig_message = message
|
|
270
295
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
296
|
+
await context.emitter.task_start()
|
|
297
|
+
|
|
298
|
+
if config.code.on_message:
|
|
299
|
+
try:
|
|
300
|
+
await config.code.on_message(orig_message)
|
|
301
|
+
except asyncio.CancelledError:
|
|
302
|
+
pass
|
|
303
|
+
finally:
|
|
304
|
+
await context.emitter.task_end()
|
|
275
305
|
|
|
276
|
-
action = Action(**action)
|
|
277
306
|
|
|
307
|
+
@sio.on("client_message") # pyright: ignore [reportOptionalCall]
|
|
308
|
+
async def message(sid, payload: MessagePayload):
|
|
309
|
+
"""Handle a message sent by the User."""
|
|
310
|
+
session = WebsocketSession.require(sid)
|
|
311
|
+
|
|
312
|
+
task = asyncio.create_task(process_message(session, payload))
|
|
313
|
+
session.current_task = task
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
@sio.on("window_message") # pyright: ignore [reportOptionalCall]
|
|
317
|
+
async def window_message(sid, data):
|
|
318
|
+
"""Handle a message send by the host window."""
|
|
319
|
+
session = WebsocketSession.require(sid)
|
|
320
|
+
init_ws_context(session)
|
|
321
|
+
|
|
322
|
+
if config.code.on_window_message:
|
|
323
|
+
try:
|
|
324
|
+
await config.code.on_window_message(data)
|
|
325
|
+
except asyncio.CancelledError:
|
|
326
|
+
pass
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
@sio.on("audio_start") # pyright: ignore [reportOptionalCall]
|
|
330
|
+
async def audio_start(sid):
|
|
331
|
+
"""Handle audio init."""
|
|
332
|
+
session = WebsocketSession.require(sid)
|
|
333
|
+
|
|
334
|
+
context = init_ws_context(session)
|
|
335
|
+
if config.code.on_audio_start:
|
|
336
|
+
connected = bool(await config.code.on_audio_start())
|
|
337
|
+
connection_state = "on" if connected else "off"
|
|
338
|
+
await context.emitter.update_audio_connection(connection_state)
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
@sio.on("audio_chunk")
|
|
342
|
+
async def audio_chunk(sid, payload: InputAudioChunkPayload):
|
|
343
|
+
"""Handle an audio chunk sent by the user."""
|
|
344
|
+
session = WebsocketSession.require(sid)
|
|
345
|
+
|
|
346
|
+
init_ws_context(session)
|
|
347
|
+
|
|
348
|
+
if config.code.on_audio_chunk:
|
|
349
|
+
asyncio.create_task(config.code.on_audio_chunk(InputAudioChunk(**payload)))
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
@sio.on("audio_end")
|
|
353
|
+
async def audio_end(sid):
|
|
354
|
+
"""Handle the end of the audio stream."""
|
|
355
|
+
session = WebsocketSession.require(sid)
|
|
278
356
|
try:
|
|
279
|
-
|
|
280
|
-
await context.emitter.
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
357
|
+
context = init_ws_context(session)
|
|
358
|
+
await context.emitter.task_start()
|
|
359
|
+
|
|
360
|
+
if not session.has_first_interaction:
|
|
361
|
+
session.has_first_interaction = True
|
|
362
|
+
asyncio.create_task(context.emitter.init_thread("audio"))
|
|
363
|
+
|
|
364
|
+
if config.code.on_audio_end:
|
|
365
|
+
await config.code.on_audio_end()
|
|
366
|
+
|
|
367
|
+
except asyncio.CancelledError:
|
|
368
|
+
pass
|
|
288
369
|
except Exception as e:
|
|
289
370
|
logger.exception(e)
|
|
290
|
-
await
|
|
291
|
-
|
|
292
|
-
)
|
|
371
|
+
await ErrorMessage(
|
|
372
|
+
author="Error", content=str(e) or e.__class__.__name__
|
|
373
|
+
).send()
|
|
374
|
+
finally:
|
|
375
|
+
await context.emitter.task_end()
|
|
293
376
|
|
|
294
377
|
|
|
295
|
-
@
|
|
378
|
+
@sio.on("chat_settings_change")
|
|
296
379
|
async def change_settings(sid, settings: Dict[str, Any]):
|
|
297
380
|
"""Handle change settings submit from the UI."""
|
|
298
381
|
context = init_ws_context(sid)
|