chainlit 1.1.300__py3-none-any.whl → 1.1.300rc0__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 +1 -3
- chainlit/cli/__init__.py +6 -53
- chainlit/cli/utils.py +24 -0
- chainlit/config.py +0 -4
- chainlit/context.py +0 -9
- chainlit/copilot/dist/index.js +204 -204
- chainlit/data/__init__.py +3 -6
- chainlit/data/sql_alchemy.py +3 -3
- chainlit/element.py +9 -33
- chainlit/emitter.py +7 -8
- chainlit/frontend/dist/assets/{DailyMotion-578b63e6.js → DailyMotion-e54bf0dc.js} +1 -1
- chainlit/frontend/dist/assets/{Facebook-b825e5bb.js → Facebook-a767c817.js} +1 -1
- chainlit/frontend/dist/assets/{FilePlayer-bcba3b4e.js → FilePlayer-5d19f3d1.js} +1 -1
- chainlit/frontend/dist/assets/{Kaltura-fc1c9497.js → Kaltura-93bef413.js} +1 -1
- chainlit/frontend/dist/assets/{Mixcloud-4cfb2724.js → Mixcloud-d5d27c2a.js} +1 -1
- chainlit/frontend/dist/assets/{Mux-aa92055c.js → Mux-ad063035.js} +1 -1
- chainlit/frontend/dist/assets/{Preview-9f55905a.js → Preview-a9a0e47e.js} +1 -1
- chainlit/frontend/dist/assets/{SoundCloud-f991fe03.js → SoundCloud-1698c4da.js} +1 -1
- chainlit/frontend/dist/assets/{Streamable-53128f49.js → Streamable-e5832da9.js} +1 -1
- chainlit/frontend/dist/assets/{Twitch-fce8b9f5.js → Twitch-cd321ef4.js} +1 -1
- chainlit/frontend/dist/assets/{Vidyard-e35c6102.js → Vidyard-8646c638.js} +1 -1
- chainlit/frontend/dist/assets/{Vimeo-fff35f8e.js → Vimeo-0e590e3a.js} +1 -1
- chainlit/frontend/dist/assets/{Wistia-ec07dc64.js → Wistia-76f0c9b0.js} +1 -1
- chainlit/frontend/dist/assets/{YouTube-ad068e2a.js → YouTube-a94756f4.js} +1 -1
- chainlit/frontend/dist/assets/index-51fef15f.js +727 -0
- chainlit/frontend/dist/assets/{index-aaf974a9.css → index-53c62926.css} +1 -1
- chainlit/frontend/dist/assets/{react-plotly-b2c6442b.js → react-plotly-d9ffbf69.js} +1 -1
- chainlit/frontend/dist/index.html +3 -2
- chainlit/message.py +8 -13
- chainlit/oauth_providers.py +0 -118
- chainlit/server.py +57 -182
- chainlit/slack/app.py +2 -2
- chainlit/socket.py +20 -26
- chainlit/step.py +9 -24
- chainlit/translations/en-US.json +1 -1
- chainlit/types.py +17 -7
- chainlit/user.py +1 -9
- chainlit/utils.py +0 -43
- {chainlit-1.1.300.dist-info → chainlit-1.1.300rc0.dist-info}/METADATA +9 -18
- chainlit-1.1.300rc0.dist-info/RECORD +77 -0
- chainlit/data/dynamodb.py +0 -586
- chainlit/frontend/dist/assets/index-d40d41cc.js +0 -727
- chainlit/teams/__init__.py +0 -6
- chainlit/teams/app.py +0 -332
- chainlit-1.1.300.dist-info/RECORD +0 -79
- {chainlit-1.1.300.dist-info → chainlit-1.1.300rc0.dist-info}/WHEEL +0 -0
- {chainlit-1.1.300.dist-info → chainlit-1.1.300rc0.dist-info}/entry_points.txt +0 -0
chainlit/server.py
CHANGED
|
@@ -18,7 +18,6 @@ import webbrowser
|
|
|
18
18
|
from contextlib import asynccontextmanager
|
|
19
19
|
from pathlib import Path
|
|
20
20
|
|
|
21
|
-
import socketio
|
|
22
21
|
from chainlit.auth import create_jwt, get_configuration, get_current_user
|
|
23
22
|
from chainlit.config import (
|
|
24
23
|
APP_ROOT,
|
|
@@ -34,19 +33,19 @@ from chainlit.data import get_data_layer
|
|
|
34
33
|
from chainlit.data.acl import is_thread_author
|
|
35
34
|
from chainlit.logger import logger
|
|
36
35
|
from chainlit.markdown import get_markdown_str
|
|
36
|
+
from chainlit.telemetry import trace_event
|
|
37
37
|
from chainlit.types import (
|
|
38
38
|
DeleteFeedbackRequest,
|
|
39
39
|
DeleteThreadRequest,
|
|
40
|
+
GenerationRequest,
|
|
40
41
|
GetThreadsRequest,
|
|
41
42
|
Theme,
|
|
42
43
|
UpdateFeedbackRequest,
|
|
43
44
|
)
|
|
44
45
|
from chainlit.user import PersistedUser, User
|
|
45
46
|
from fastapi import (
|
|
46
|
-
APIRouter,
|
|
47
47
|
Depends,
|
|
48
48
|
FastAPI,
|
|
49
|
-
Form,
|
|
50
49
|
HTTPException,
|
|
51
50
|
Query,
|
|
52
51
|
Request,
|
|
@@ -57,14 +56,12 @@ from fastapi import (
|
|
|
57
56
|
from fastapi.responses import FileResponse, HTMLResponse, JSONResponse, RedirectResponse
|
|
58
57
|
from fastapi.security import OAuth2PasswordRequestForm
|
|
59
58
|
from fastapi.staticfiles import StaticFiles
|
|
59
|
+
from fastapi_socketio import SocketManager
|
|
60
60
|
from starlette.datastructures import URL
|
|
61
61
|
from starlette.middleware.cors import CORSMiddleware
|
|
62
62
|
from typing_extensions import Annotated
|
|
63
63
|
from watchfiles import awatch
|
|
64
64
|
|
|
65
|
-
ROOT_PATH = os.environ.get("CHAINLIT_ROOT_PATH", "")
|
|
66
|
-
IS_SUBMOUNT = os.environ.get("CHAINLIT_SUBMOUNT", "") == "true"
|
|
67
|
-
|
|
68
65
|
|
|
69
66
|
@asynccontextmanager
|
|
70
67
|
async def lifespan(app: FastAPI):
|
|
@@ -72,9 +69,9 @@ async def lifespan(app: FastAPI):
|
|
|
72
69
|
port = config.run.port
|
|
73
70
|
|
|
74
71
|
if host == DEFAULT_HOST:
|
|
75
|
-
url = f"http://localhost:{port}
|
|
72
|
+
url = f"http://localhost:{port}"
|
|
76
73
|
else:
|
|
77
|
-
url = f"http://{host}:{port}
|
|
74
|
+
url = f"http://{host}:{port}"
|
|
78
75
|
|
|
79
76
|
logger.info(f"Your app is available at {url}")
|
|
80
77
|
|
|
@@ -115,7 +112,7 @@ async def lifespan(app: FastAPI):
|
|
|
115
112
|
logger.error(f"Error reloading module: {e}")
|
|
116
113
|
|
|
117
114
|
await asyncio.sleep(1)
|
|
118
|
-
await
|
|
115
|
+
await socket.emit("reload", {})
|
|
119
116
|
|
|
120
117
|
break
|
|
121
118
|
|
|
@@ -169,36 +166,12 @@ def get_build_dir(local_target: str, packaged_target: str):
|
|
|
169
166
|
build_dir = get_build_dir("frontend", "frontend")
|
|
170
167
|
copilot_build_dir = get_build_dir(os.path.join("libs", "copilot"), "copilot")
|
|
171
168
|
|
|
172
|
-
app = FastAPI(lifespan=lifespan)
|
|
173
|
-
|
|
174
|
-
sio = socketio.AsyncServer(
|
|
175
|
-
cors_allowed_origins=[] if IS_SUBMOUNT else "*", async_mode="asgi"
|
|
176
|
-
)
|
|
177
|
-
|
|
178
|
-
combined_asgi_app = socketio.ASGIApp(
|
|
179
|
-
sio,
|
|
180
|
-
app,
|
|
181
|
-
socketio_path=f"{ROOT_PATH}/ws/socket.io" if ROOT_PATH else "/ws/socket.io",
|
|
182
|
-
)
|
|
183
|
-
|
|
184
|
-
app.add_middleware(
|
|
185
|
-
CORSMiddleware,
|
|
186
|
-
allow_origins=config.project.allow_origins,
|
|
187
|
-
allow_credentials=True,
|
|
188
|
-
allow_methods=["*"],
|
|
189
|
-
allow_headers=["*"],
|
|
190
|
-
)
|
|
191
|
-
|
|
192
|
-
router = APIRouter(prefix=ROOT_PATH)
|
|
193
169
|
|
|
194
|
-
app
|
|
195
|
-
f"{ROOT_PATH}/public",
|
|
196
|
-
StaticFiles(directory="public", check_dir=False),
|
|
197
|
-
name="public",
|
|
198
|
-
)
|
|
170
|
+
app = FastAPI(lifespan=lifespan)
|
|
199
171
|
|
|
172
|
+
app.mount("/public", StaticFiles(directory="public", check_dir=False), name="public")
|
|
200
173
|
app.mount(
|
|
201
|
-
|
|
174
|
+
"/assets",
|
|
202
175
|
StaticFiles(
|
|
203
176
|
packages=[("chainlit", os.path.join(build_dir, "assets"))],
|
|
204
177
|
follow_symlink=config.project.follow_symlink,
|
|
@@ -207,7 +180,7 @@ app.mount(
|
|
|
207
180
|
)
|
|
208
181
|
|
|
209
182
|
app.mount(
|
|
210
|
-
|
|
183
|
+
"/copilot",
|
|
211
184
|
StaticFiles(
|
|
212
185
|
packages=[("chainlit", copilot_build_dir)],
|
|
213
186
|
follow_symlink=config.project.follow_symlink,
|
|
@@ -216,6 +189,22 @@ app.mount(
|
|
|
216
189
|
)
|
|
217
190
|
|
|
218
191
|
|
|
192
|
+
app.add_middleware(
|
|
193
|
+
CORSMiddleware,
|
|
194
|
+
allow_origins=config.project.allow_origins,
|
|
195
|
+
allow_credentials=True,
|
|
196
|
+
allow_methods=["*"],
|
|
197
|
+
allow_headers=["*"],
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
socket = SocketManager(
|
|
201
|
+
app,
|
|
202
|
+
cors_allowed_origins=[],
|
|
203
|
+
async_mode="asgi",
|
|
204
|
+
socketio_path="/ws/socket.io",
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
|
|
219
208
|
# -------------------------------------------------------------------------------
|
|
220
209
|
# SLACK HANDLER
|
|
221
210
|
# -------------------------------------------------------------------------------
|
|
@@ -223,28 +212,11 @@ app.mount(
|
|
|
223
212
|
if os.environ.get("SLACK_BOT_TOKEN") and os.environ.get("SLACK_SIGNING_SECRET"):
|
|
224
213
|
from chainlit.slack.app import slack_app_handler
|
|
225
214
|
|
|
226
|
-
@
|
|
227
|
-
async def
|
|
215
|
+
@app.post("/slack/events")
|
|
216
|
+
async def endpoint(req: Request):
|
|
228
217
|
return await slack_app_handler.handle(req)
|
|
229
218
|
|
|
230
219
|
|
|
231
|
-
# -------------------------------------------------------------------------------
|
|
232
|
-
# TEAMS HANDLER
|
|
233
|
-
# -------------------------------------------------------------------------------
|
|
234
|
-
|
|
235
|
-
if os.environ.get("TEAMS_APP_ID") and os.environ.get("TEAMS_APP_PASSWORD"):
|
|
236
|
-
from botbuilder.schema import Activity
|
|
237
|
-
from chainlit.teams.app import adapter, bot
|
|
238
|
-
|
|
239
|
-
@router.post("/teams/events")
|
|
240
|
-
async def teams_endpoint(req: Request):
|
|
241
|
-
body = await req.json()
|
|
242
|
-
activity = Activity().deserialize(body)
|
|
243
|
-
auth_header = req.headers.get("Authorization", "")
|
|
244
|
-
response = await adapter.process_activity(activity, auth_header, bot.on_turn)
|
|
245
|
-
return response
|
|
246
|
-
|
|
247
|
-
|
|
248
220
|
# -------------------------------------------------------------------------------
|
|
249
221
|
# HTTP HANDLERS
|
|
250
222
|
# -------------------------------------------------------------------------------
|
|
@@ -266,17 +238,14 @@ def get_html_template():
|
|
|
266
238
|
)
|
|
267
239
|
url = config.ui.github or default_url
|
|
268
240
|
meta_image_url = config.ui.custom_meta_image_url or default_meta_image_url
|
|
269
|
-
favicon_path = ROOT_PATH + "/favicon" if ROOT_PATH else "/favicon"
|
|
270
241
|
|
|
271
242
|
tags = f"""<title>{config.ui.name}</title>
|
|
272
|
-
<link rel="icon" href="{favicon_path}" />
|
|
273
243
|
<meta name="description" content="{config.ui.description}">
|
|
274
244
|
<meta property="og:type" content="website">
|
|
275
245
|
<meta property="og:title" content="{config.ui.name}">
|
|
276
246
|
<meta property="og:description" content="{config.ui.description}">
|
|
277
247
|
<meta property="og:image" content="{meta_image_url}">
|
|
278
|
-
<meta property="og:url" content="{url}">
|
|
279
|
-
<meta property="og:root_path" content="{ROOT_PATH}">"""
|
|
248
|
+
<meta property="og:url" content="{url}">"""
|
|
280
249
|
|
|
281
250
|
js = f"""<script>{f"window.theme = {json.dumps(config.ui.theme.to_dict())}; " if config.ui.theme else ""}</script>"""
|
|
282
251
|
|
|
@@ -306,9 +275,6 @@ def get_html_template():
|
|
|
306
275
|
content = replace_between_tags(
|
|
307
276
|
content, "<!-- FONT START -->", "<!-- FONT END -->", font
|
|
308
277
|
)
|
|
309
|
-
if ROOT_PATH:
|
|
310
|
-
content = content.replace('href="/', f'href="{ROOT_PATH}/')
|
|
311
|
-
content = content.replace('src="/', f'src="{ROOT_PATH}/')
|
|
312
278
|
return content
|
|
313
279
|
|
|
314
280
|
|
|
@@ -318,7 +284,6 @@ def get_user_facing_url(url: URL):
|
|
|
318
284
|
Handles deployment with proxies (like cloud run).
|
|
319
285
|
"""
|
|
320
286
|
|
|
321
|
-
ROOT_PATH = os.environ.get("CHAINLIT_ROOT_PATH", "")
|
|
322
287
|
chainlit_url = os.environ.get("CHAINLIT_URL")
|
|
323
288
|
|
|
324
289
|
# No config, we keep the URL as is
|
|
@@ -334,26 +299,15 @@ def get_user_facing_url(url: URL):
|
|
|
334
299
|
if config_url.path.endswith("/"):
|
|
335
300
|
config_url = config_url.replace(path=config_url.path[:-1])
|
|
336
301
|
|
|
337
|
-
# Add ROOT_PATH to the final URL if it exists
|
|
338
|
-
if ROOT_PATH:
|
|
339
|
-
# Ensure ROOT_PATH starts with a slash
|
|
340
|
-
if not ROOT_PATH.startswith("/"):
|
|
341
|
-
ROOT_PATH = "/" + ROOT_PATH
|
|
342
|
-
# Ensure ROOT_PATH does not end with a slash
|
|
343
|
-
if ROOT_PATH.endswith("/"):
|
|
344
|
-
ROOT_PATH = ROOT_PATH[:-1]
|
|
345
|
-
|
|
346
|
-
return config_url.__str__() + ROOT_PATH + url.path
|
|
347
|
-
|
|
348
302
|
return config_url.__str__() + url.path
|
|
349
303
|
|
|
350
304
|
|
|
351
|
-
@
|
|
305
|
+
@app.get("/auth/config")
|
|
352
306
|
async def auth(request: Request):
|
|
353
307
|
return get_configuration()
|
|
354
308
|
|
|
355
309
|
|
|
356
|
-
@
|
|
310
|
+
@app.post("/login")
|
|
357
311
|
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
|
|
358
312
|
if not config.code.password_auth_callback:
|
|
359
313
|
raise HTTPException(
|
|
@@ -382,14 +336,14 @@ async def login(form_data: OAuth2PasswordRequestForm = Depends()):
|
|
|
382
336
|
}
|
|
383
337
|
|
|
384
338
|
|
|
385
|
-
@
|
|
339
|
+
@app.post("/logout")
|
|
386
340
|
async def logout(request: Request, response: Response):
|
|
387
341
|
if config.code.on_logout:
|
|
388
342
|
return await config.code.on_logout(request, response)
|
|
389
343
|
return {"success": True}
|
|
390
344
|
|
|
391
345
|
|
|
392
|
-
@
|
|
346
|
+
@app.post("/auth/header")
|
|
393
347
|
async def header_auth(request: Request):
|
|
394
348
|
if not config.code.header_auth_callback:
|
|
395
349
|
raise HTTPException(
|
|
@@ -418,7 +372,7 @@ async def header_auth(request: Request):
|
|
|
418
372
|
}
|
|
419
373
|
|
|
420
374
|
|
|
421
|
-
@
|
|
375
|
+
@app.get("/auth/oauth/{provider_id}")
|
|
422
376
|
async def oauth_login(provider_id: str, request: Request):
|
|
423
377
|
if config.code.oauth_callback is None:
|
|
424
378
|
raise HTTPException(
|
|
@@ -459,7 +413,7 @@ async def oauth_login(provider_id: str, request: Request):
|
|
|
459
413
|
return response
|
|
460
414
|
|
|
461
415
|
|
|
462
|
-
@
|
|
416
|
+
@app.get("/auth/oauth/{provider_id}/callback")
|
|
463
417
|
async def oauth_callback(
|
|
464
418
|
provider_id: str,
|
|
465
419
|
request: Request,
|
|
@@ -543,85 +497,7 @@ async def oauth_callback(
|
|
|
543
497
|
return response
|
|
544
498
|
|
|
545
499
|
|
|
546
|
-
|
|
547
|
-
@router.post("/auth/oauth/azure-ad-hybrid/callback")
|
|
548
|
-
async def oauth_azure_hf_callback(
|
|
549
|
-
request: Request,
|
|
550
|
-
error: Optional[str] = None,
|
|
551
|
-
code: Annotated[Optional[str], Form()] = None,
|
|
552
|
-
id_token: Annotated[Optional[str], Form()] = None,
|
|
553
|
-
):
|
|
554
|
-
provider_id = "azure-ad-hybrid"
|
|
555
|
-
if config.code.oauth_callback is None:
|
|
556
|
-
raise HTTPException(
|
|
557
|
-
status_code=status.HTTP_400_BAD_REQUEST,
|
|
558
|
-
detail="No oauth_callback defined",
|
|
559
|
-
)
|
|
560
|
-
|
|
561
|
-
provider = get_oauth_provider(provider_id)
|
|
562
|
-
if not provider:
|
|
563
|
-
raise HTTPException(
|
|
564
|
-
status_code=status.HTTP_404_NOT_FOUND,
|
|
565
|
-
detail=f"Provider {provider_id} not found",
|
|
566
|
-
)
|
|
567
|
-
|
|
568
|
-
if error:
|
|
569
|
-
params = urllib.parse.urlencode(
|
|
570
|
-
{
|
|
571
|
-
"error": error,
|
|
572
|
-
}
|
|
573
|
-
)
|
|
574
|
-
response = RedirectResponse(
|
|
575
|
-
# FIXME: redirect to the right frontend base url to improve the dev environment
|
|
576
|
-
url=f"/login?{params}",
|
|
577
|
-
)
|
|
578
|
-
return response
|
|
579
|
-
|
|
580
|
-
if not code:
|
|
581
|
-
raise HTTPException(
|
|
582
|
-
status_code=status.HTTP_400_BAD_REQUEST,
|
|
583
|
-
detail="Missing code",
|
|
584
|
-
)
|
|
585
|
-
|
|
586
|
-
url = get_user_facing_url(request.url)
|
|
587
|
-
token = await provider.get_token(code, url)
|
|
588
|
-
|
|
589
|
-
(raw_user_data, default_user) = await provider.get_user_info(token)
|
|
590
|
-
|
|
591
|
-
user = await config.code.oauth_callback(
|
|
592
|
-
provider_id, token, raw_user_data, default_user, id_token
|
|
593
|
-
)
|
|
594
|
-
|
|
595
|
-
if not user:
|
|
596
|
-
raise HTTPException(
|
|
597
|
-
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
598
|
-
detail="Unauthorized",
|
|
599
|
-
)
|
|
600
|
-
|
|
601
|
-
access_token = create_jwt(user)
|
|
602
|
-
|
|
603
|
-
if data_layer := get_data_layer():
|
|
604
|
-
try:
|
|
605
|
-
await data_layer.create_user(user)
|
|
606
|
-
except Exception as e:
|
|
607
|
-
logger.error(f"Error creating user: {e}")
|
|
608
|
-
|
|
609
|
-
params = urllib.parse.urlencode(
|
|
610
|
-
{
|
|
611
|
-
"access_token": access_token,
|
|
612
|
-
"token_type": "bearer",
|
|
613
|
-
}
|
|
614
|
-
)
|
|
615
|
-
response = RedirectResponse(
|
|
616
|
-
# FIXME: redirect to the right frontend base url to improve the dev environment
|
|
617
|
-
url=f"/login/callback?{params}",
|
|
618
|
-
status_code=302,
|
|
619
|
-
)
|
|
620
|
-
response.delete_cookie("oauth_state")
|
|
621
|
-
return response
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
@router.get("/project/translations")
|
|
500
|
+
@app.get("/project/translations")
|
|
625
501
|
async def project_translations(
|
|
626
502
|
language: str = Query(default="en-US", description="Language code"),
|
|
627
503
|
):
|
|
@@ -637,7 +513,7 @@ async def project_translations(
|
|
|
637
513
|
)
|
|
638
514
|
|
|
639
515
|
|
|
640
|
-
@
|
|
516
|
+
@app.get("/project/settings")
|
|
641
517
|
async def project_settings(
|
|
642
518
|
current_user: Annotated[Union[User, PersistedUser], Depends(get_current_user)],
|
|
643
519
|
language: str = Query(default="en-US", description="Language code"),
|
|
@@ -683,7 +559,7 @@ async def project_settings(
|
|
|
683
559
|
)
|
|
684
560
|
|
|
685
561
|
|
|
686
|
-
@
|
|
562
|
+
@app.put("/feedback")
|
|
687
563
|
async def update_feedback(
|
|
688
564
|
request: Request,
|
|
689
565
|
update: UpdateFeedbackRequest,
|
|
@@ -702,7 +578,7 @@ async def update_feedback(
|
|
|
702
578
|
return JSONResponse(content={"success": True, "feedbackId": feedback_id})
|
|
703
579
|
|
|
704
580
|
|
|
705
|
-
@
|
|
581
|
+
@app.delete("/feedback")
|
|
706
582
|
async def delete_feedback(
|
|
707
583
|
request: Request,
|
|
708
584
|
payload: DeleteFeedbackRequest,
|
|
@@ -721,7 +597,7 @@ async def delete_feedback(
|
|
|
721
597
|
return JSONResponse(content={"success": True})
|
|
722
598
|
|
|
723
599
|
|
|
724
|
-
@
|
|
600
|
+
@app.post("/project/threads")
|
|
725
601
|
async def get_user_threads(
|
|
726
602
|
request: Request,
|
|
727
603
|
payload: GetThreadsRequest,
|
|
@@ -746,7 +622,7 @@ async def get_user_threads(
|
|
|
746
622
|
return JSONResponse(content=res.to_dict())
|
|
747
623
|
|
|
748
624
|
|
|
749
|
-
@
|
|
625
|
+
@app.get("/project/thread/{thread_id}")
|
|
750
626
|
async def get_thread(
|
|
751
627
|
request: Request,
|
|
752
628
|
thread_id: str,
|
|
@@ -764,7 +640,7 @@ async def get_thread(
|
|
|
764
640
|
return JSONResponse(content=res)
|
|
765
641
|
|
|
766
642
|
|
|
767
|
-
@
|
|
643
|
+
@app.get("/project/thread/{thread_id}/element/{element_id}")
|
|
768
644
|
async def get_thread_element(
|
|
769
645
|
request: Request,
|
|
770
646
|
thread_id: str,
|
|
@@ -783,7 +659,7 @@ async def get_thread_element(
|
|
|
783
659
|
return JSONResponse(content=res)
|
|
784
660
|
|
|
785
661
|
|
|
786
|
-
@
|
|
662
|
+
@app.delete("/project/thread")
|
|
787
663
|
async def delete_thread(
|
|
788
664
|
request: Request,
|
|
789
665
|
payload: DeleteThreadRequest,
|
|
@@ -804,7 +680,7 @@ async def delete_thread(
|
|
|
804
680
|
return JSONResponse(content={"success": True})
|
|
805
681
|
|
|
806
682
|
|
|
807
|
-
@
|
|
683
|
+
@app.post("/project/file")
|
|
808
684
|
async def upload_file(
|
|
809
685
|
session_id: str,
|
|
810
686
|
file: UploadFile,
|
|
@@ -840,7 +716,7 @@ async def upload_file(
|
|
|
840
716
|
return JSONResponse(file_response)
|
|
841
717
|
|
|
842
718
|
|
|
843
|
-
@
|
|
719
|
+
@app.get("/project/file/{file_id}")
|
|
844
720
|
async def get_file(
|
|
845
721
|
file_id: str,
|
|
846
722
|
session_id: Optional[str] = None,
|
|
@@ -862,7 +738,7 @@ async def get_file(
|
|
|
862
738
|
raise HTTPException(status_code=404, detail="File not found")
|
|
863
739
|
|
|
864
740
|
|
|
865
|
-
@
|
|
741
|
+
@app.get("/files/{filename:path}")
|
|
866
742
|
async def serve_file(
|
|
867
743
|
filename: str,
|
|
868
744
|
current_user: Annotated[Union[User, PersistedUser], Depends(get_current_user)],
|
|
@@ -880,7 +756,7 @@ async def serve_file(
|
|
|
880
756
|
raise HTTPException(status_code=404, detail="File not found")
|
|
881
757
|
|
|
882
758
|
|
|
883
|
-
@
|
|
759
|
+
@app.get("/favicon")
|
|
884
760
|
async def get_favicon():
|
|
885
761
|
custom_favicon_path = os.path.join(APP_ROOT, "public", "favicon.*")
|
|
886
762
|
files = glob.glob(custom_favicon_path)
|
|
@@ -895,7 +771,7 @@ async def get_favicon():
|
|
|
895
771
|
return FileResponse(favicon_path, media_type=media_type)
|
|
896
772
|
|
|
897
773
|
|
|
898
|
-
@
|
|
774
|
+
@app.get("/logo")
|
|
899
775
|
async def get_logo(theme: Optional[Theme] = Query(Theme.light)):
|
|
900
776
|
theme_value = theme.value if theme else Theme.light.value
|
|
901
777
|
logo_path = None
|
|
@@ -917,7 +793,7 @@ async def get_logo(theme: Optional[Theme] = Query(Theme.light)):
|
|
|
917
793
|
return FileResponse(logo_path, media_type=media_type)
|
|
918
794
|
|
|
919
795
|
|
|
920
|
-
@
|
|
796
|
+
@app.get("/avatars/{avatar_id}")
|
|
921
797
|
async def get_avatar(avatar_id: str):
|
|
922
798
|
if avatar_id == "default":
|
|
923
799
|
avatar_id = config.ui.name
|
|
@@ -936,20 +812,19 @@ async def get_avatar(avatar_id: str):
|
|
|
936
812
|
return await get_favicon()
|
|
937
813
|
|
|
938
814
|
|
|
939
|
-
@
|
|
815
|
+
@app.head("/")
|
|
940
816
|
def status_check():
|
|
941
817
|
return {"message": "Site is operational"}
|
|
942
818
|
|
|
943
819
|
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
return response
|
|
820
|
+
def register_wildcard_route_handler():
|
|
821
|
+
@app.get("/{path:path}")
|
|
822
|
+
async def serve(request: Request, path: str):
|
|
823
|
+
html_template = get_html_template()
|
|
824
|
+
"""Serve the UI files."""
|
|
825
|
+
response = HTMLResponse(content=html_template, status_code=200)
|
|
951
826
|
|
|
827
|
+
return response
|
|
952
828
|
|
|
953
|
-
app.include_router(router)
|
|
954
829
|
|
|
955
830
|
import chainlit.socket # noqa
|
chainlit/slack/app.py
CHANGED
|
@@ -176,8 +176,8 @@ async def get_user(slack_user_id: str):
|
|
|
176
176
|
slack_user = await slack_app.client.users_info(user=slack_user_id)
|
|
177
177
|
slack_user_profile = slack_user["user"]["profile"]
|
|
178
178
|
|
|
179
|
-
|
|
180
|
-
user = User(identifier=USER_PREFIX +
|
|
179
|
+
user_email = slack_user_profile.get("email")
|
|
180
|
+
user = User(identifier=USER_PREFIX + user_email, metadata=slack_user_profile)
|
|
181
181
|
|
|
182
182
|
users_by_slack_id[slack_user_id] = user
|
|
183
183
|
|
chainlit/socket.py
CHANGED
|
@@ -3,7 +3,6 @@ import json
|
|
|
3
3
|
import time
|
|
4
4
|
import uuid
|
|
5
5
|
from typing import Any, Dict, Literal
|
|
6
|
-
from urllib.parse import unquote
|
|
7
6
|
|
|
8
7
|
from chainlit.action import Action
|
|
9
8
|
from chainlit.auth import get_current_user, require_login
|
|
@@ -13,15 +12,14 @@ from chainlit.data import get_data_layer
|
|
|
13
12
|
from chainlit.element import Element
|
|
14
13
|
from chainlit.logger import logger
|
|
15
14
|
from chainlit.message import ErrorMessage, Message
|
|
16
|
-
from chainlit.server import
|
|
15
|
+
from chainlit.server import socket
|
|
17
16
|
from chainlit.session import WebsocketSession
|
|
18
17
|
from chainlit.telemetry import trace_event
|
|
19
18
|
from chainlit.types import (
|
|
20
19
|
AudioChunk,
|
|
21
20
|
AudioChunkPayload,
|
|
22
21
|
AudioEndPayload,
|
|
23
|
-
|
|
24
|
-
SystemMessagePayload,
|
|
22
|
+
UIMessagePayload,
|
|
25
23
|
)
|
|
26
24
|
from chainlit.user_session import user_sessions
|
|
27
25
|
|
|
@@ -54,7 +52,7 @@ async def resume_thread(session: WebsocketSession):
|
|
|
54
52
|
user_is_author = author == session.user.identifier
|
|
55
53
|
|
|
56
54
|
if user_is_author:
|
|
57
|
-
metadata = thread.get("metadata"
|
|
55
|
+
metadata = thread.get("metadata", {})
|
|
58
56
|
user_sessions[session.id] = metadata.copy()
|
|
59
57
|
if chat_profile := metadata.get("chat_profile"):
|
|
60
58
|
session.chat_profile = chat_profile
|
|
@@ -99,8 +97,8 @@ def build_anon_user_identifier(environ):
|
|
|
99
97
|
return str(uuid.uuid5(uuid.NAMESPACE_DNS, ip))
|
|
100
98
|
|
|
101
99
|
|
|
102
|
-
@
|
|
103
|
-
async def connect(sid, environ):
|
|
100
|
+
@socket.on("connect")
|
|
101
|
+
async def connect(sid, environ, auth):
|
|
104
102
|
if (
|
|
105
103
|
not config.code.on_chat_start
|
|
106
104
|
and not config.code.on_message
|
|
@@ -125,11 +123,11 @@ async def connect(sid, environ):
|
|
|
125
123
|
|
|
126
124
|
# Session scoped function to emit to the client
|
|
127
125
|
def emit_fn(event, data):
|
|
128
|
-
return
|
|
126
|
+
return socket.emit(event, data, to=sid)
|
|
129
127
|
|
|
130
128
|
# Session scoped function to emit to the client and wait for a response
|
|
131
129
|
def emit_call_fn(event: Literal["ask", "call_fn"], data, timeout):
|
|
132
|
-
return
|
|
130
|
+
return socket.call(event, data, timeout=timeout, to=sid)
|
|
133
131
|
|
|
134
132
|
session_id = environ.get("HTTP_X_CHAINLIT_SESSION_ID")
|
|
135
133
|
if restore_existing_session(sid, session_id, emit_fn, emit_call_fn):
|
|
@@ -140,10 +138,6 @@ async def connect(sid, environ):
|
|
|
140
138
|
|
|
141
139
|
client_type = environ.get("HTTP_X_CHAINLIT_CLIENT_TYPE")
|
|
142
140
|
http_referer = environ.get("HTTP_REFERER")
|
|
143
|
-
url_encoded_chat_profile = environ.get("HTTP_X_CHAINLIT_CHAT_PROFILE")
|
|
144
|
-
chat_profile = (
|
|
145
|
-
unquote(url_encoded_chat_profile) if url_encoded_chat_profile else None
|
|
146
|
-
)
|
|
147
141
|
|
|
148
142
|
ws_session = WebsocketSession(
|
|
149
143
|
id=session_id,
|
|
@@ -154,7 +148,7 @@ async def connect(sid, environ):
|
|
|
154
148
|
user_env=user_env,
|
|
155
149
|
user=user,
|
|
156
150
|
token=token,
|
|
157
|
-
chat_profile=
|
|
151
|
+
chat_profile=environ.get("HTTP_X_CHAINLIT_CHAT_PROFILE"),
|
|
158
152
|
thread_id=environ.get("HTTP_X_CHAINLIT_THREAD_ID"),
|
|
159
153
|
languages=environ.get("HTTP_ACCEPT_LANGUAGE"),
|
|
160
154
|
http_referer=http_referer,
|
|
@@ -164,7 +158,7 @@ async def connect(sid, environ):
|
|
|
164
158
|
return True
|
|
165
159
|
|
|
166
160
|
|
|
167
|
-
@
|
|
161
|
+
@socket.on("connection_successful")
|
|
168
162
|
async def connection_successful(sid):
|
|
169
163
|
context = init_ws_context(sid)
|
|
170
164
|
|
|
@@ -192,14 +186,14 @@ async def connection_successful(sid):
|
|
|
192
186
|
context.session.current_task = task
|
|
193
187
|
|
|
194
188
|
|
|
195
|
-
@
|
|
189
|
+
@socket.on("clear_session")
|
|
196
190
|
async def clean_session(sid):
|
|
197
191
|
session = WebsocketSession.get(sid)
|
|
198
192
|
if session:
|
|
199
193
|
session.to_clear = True
|
|
200
194
|
|
|
201
195
|
|
|
202
|
-
@
|
|
196
|
+
@socket.on("disconnect")
|
|
203
197
|
async def disconnect(sid):
|
|
204
198
|
session = WebsocketSession.get(sid)
|
|
205
199
|
|
|
@@ -233,7 +227,7 @@ async def disconnect(sid):
|
|
|
233
227
|
asyncio.ensure_future(clear_on_timeout(sid))
|
|
234
228
|
|
|
235
229
|
|
|
236
|
-
@
|
|
230
|
+
@socket.on("stop")
|
|
237
231
|
async def stop(sid):
|
|
238
232
|
if session := WebsocketSession.get(sid):
|
|
239
233
|
trace_event("stop_task")
|
|
@@ -248,12 +242,12 @@ async def stop(sid):
|
|
|
248
242
|
await config.code.on_stop()
|
|
249
243
|
|
|
250
244
|
|
|
251
|
-
async def process_message(session: WebsocketSession, payload:
|
|
245
|
+
async def process_message(session: WebsocketSession, payload: UIMessagePayload):
|
|
252
246
|
"""Process a message from the user."""
|
|
253
247
|
try:
|
|
254
248
|
context = init_ws_context(session)
|
|
255
249
|
await context.emitter.task_start()
|
|
256
|
-
message = await context.emitter.
|
|
250
|
+
message = await context.emitter.process_user_message(payload)
|
|
257
251
|
|
|
258
252
|
if config.code.on_message:
|
|
259
253
|
# Sleep 1ms to make sure any children step starts after the message step start
|
|
@@ -270,8 +264,8 @@ async def process_message(session: WebsocketSession, payload: MessagePayload):
|
|
|
270
264
|
await context.emitter.task_end()
|
|
271
265
|
|
|
272
266
|
|
|
273
|
-
@
|
|
274
|
-
async def message(sid, payload:
|
|
267
|
+
@socket.on("ui_message")
|
|
268
|
+
async def message(sid, payload: UIMessagePayload):
|
|
275
269
|
"""Handle a message sent by the User."""
|
|
276
270
|
session = WebsocketSession.require(sid)
|
|
277
271
|
|
|
@@ -279,7 +273,7 @@ async def message(sid, payload: MessagePayload):
|
|
|
279
273
|
session.current_task = task
|
|
280
274
|
|
|
281
275
|
|
|
282
|
-
@
|
|
276
|
+
@socket.on("audio_chunk")
|
|
283
277
|
async def audio_chunk(sid, payload: AudioChunkPayload):
|
|
284
278
|
"""Handle an audio chunk sent by the user."""
|
|
285
279
|
session = WebsocketSession.require(sid)
|
|
@@ -290,7 +284,7 @@ async def audio_chunk(sid, payload: AudioChunkPayload):
|
|
|
290
284
|
asyncio.create_task(config.code.on_audio_chunk(AudioChunk(**payload)))
|
|
291
285
|
|
|
292
286
|
|
|
293
|
-
@
|
|
287
|
+
@socket.on("audio_end")
|
|
294
288
|
async def audio_end(sid, payload: AudioEndPayload):
|
|
295
289
|
"""Handle the end of the audio stream."""
|
|
296
290
|
session = WebsocketSession.require(sid)
|
|
@@ -334,7 +328,7 @@ async def process_action(action: Action):
|
|
|
334
328
|
logger.warning("No callback found for action %s", action.name)
|
|
335
329
|
|
|
336
330
|
|
|
337
|
-
@
|
|
331
|
+
@socket.on("action_call")
|
|
338
332
|
async def call_action(sid, action):
|
|
339
333
|
"""Handle an action call from the UI."""
|
|
340
334
|
context = init_ws_context(sid)
|
|
@@ -361,7 +355,7 @@ async def call_action(sid, action):
|
|
|
361
355
|
)
|
|
362
356
|
|
|
363
357
|
|
|
364
|
-
@
|
|
358
|
+
@socket.on("chat_settings_change")
|
|
365
359
|
async def change_settings(sid, settings: Dict[str, Any]):
|
|
366
360
|
"""Handle change settings submit from the UI."""
|
|
367
361
|
context = init_ws_context(sid)
|