chainlit 1.1.201__py3-none-any.whl → 1.1.300__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 +22 -4
- chainlit/cli/__init__.py +53 -6
- chainlit/config.py +25 -18
- chainlit/context.py +9 -0
- chainlit/copilot/dist/index.js +440 -407
- chainlit/data/__init__.py +20 -5
- chainlit/data/dynamodb.py +586 -0
- chainlit/data/sql_alchemy.py +48 -28
- chainlit/discord/app.py +4 -2
- chainlit/element.py +41 -20
- chainlit/emitter.py +8 -7
- chainlit/frontend/dist/assets/DailyMotion-578b63e6.js +1 -0
- chainlit/frontend/dist/assets/Facebook-b825e5bb.js +1 -0
- chainlit/frontend/dist/assets/FilePlayer-bcba3b4e.js +1 -0
- chainlit/frontend/dist/assets/Kaltura-fc1c9497.js +1 -0
- chainlit/frontend/dist/assets/Mixcloud-4cfb2724.js +1 -0
- chainlit/frontend/dist/assets/Mux-aa92055c.js +1 -0
- chainlit/frontend/dist/assets/Preview-9f55905a.js +1 -0
- chainlit/frontend/dist/assets/SoundCloud-f991fe03.js +1 -0
- chainlit/frontend/dist/assets/Streamable-53128f49.js +1 -0
- chainlit/frontend/dist/assets/Twitch-fce8b9f5.js +1 -0
- chainlit/frontend/dist/assets/Vidyard-e35c6102.js +1 -0
- chainlit/frontend/dist/assets/Vimeo-fff35f8e.js +1 -0
- chainlit/frontend/dist/assets/Wistia-ec07dc64.js +1 -0
- chainlit/frontend/dist/assets/YouTube-ad068e2a.js +1 -0
- chainlit/frontend/dist/assets/index-aaf974a9.css +1 -0
- chainlit/frontend/dist/assets/index-d40d41cc.js +727 -0
- chainlit/frontend/dist/assets/{react-plotly-1ca97c0e.js → react-plotly-b2c6442b.js} +1 -1
- chainlit/frontend/dist/index.html +2 -3
- chainlit/langchain/callbacks.py +4 -2
- chainlit/llama_index/callbacks.py +2 -2
- chainlit/message.py +30 -25
- chainlit/oauth_providers.py +118 -0
- chainlit/server.py +208 -83
- chainlit/slack/app.py +2 -3
- chainlit/socket.py +27 -23
- chainlit/step.py +44 -30
- chainlit/teams/__init__.py +6 -0
- chainlit/teams/app.py +332 -0
- chainlit/translations/en-US.json +2 -4
- chainlit/types.py +17 -17
- chainlit/user.py +9 -1
- chainlit/utils.py +47 -3
- {chainlit-1.1.201.dist-info → chainlit-1.1.300.dist-info}/METADATA +22 -14
- chainlit-1.1.300.dist-info/RECORD +79 -0
- chainlit/cli/utils.py +0 -24
- chainlit/frontend/dist/assets/index-bf0451c6.js +0 -698
- chainlit/frontend/dist/assets/index-d088547c.css +0 -1
- chainlit/playground/__init__.py +0 -2
- chainlit/playground/config.py +0 -36
- chainlit/playground/provider.py +0 -108
- chainlit/playground/providers/__init__.py +0 -11
- 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 -386
- chainlit/playground/providers/vertexai.py +0 -171
- chainlit-1.1.201.dist-info/RECORD +0 -72
- {chainlit-1.1.201.dist-info → chainlit-1.1.300.dist-info}/WHEEL +0 -0
- {chainlit-1.1.201.dist-info → chainlit-1.1.300.dist-info}/entry_points.txt +0 -0
chainlit/server.py
CHANGED
|
@@ -18,6 +18,7 @@ import webbrowser
|
|
|
18
18
|
from contextlib import asynccontextmanager
|
|
19
19
|
from pathlib import Path
|
|
20
20
|
|
|
21
|
+
import socketio
|
|
21
22
|
from chainlit.auth import create_jwt, get_configuration, get_current_user
|
|
22
23
|
from chainlit.config import (
|
|
23
24
|
APP_ROOT,
|
|
@@ -33,20 +34,19 @@ from chainlit.data import get_data_layer
|
|
|
33
34
|
from chainlit.data.acl import is_thread_author
|
|
34
35
|
from chainlit.logger import logger
|
|
35
36
|
from chainlit.markdown import get_markdown_str
|
|
36
|
-
from chainlit.playground.config import get_llm_providers
|
|
37
|
-
from chainlit.telemetry import trace_event
|
|
38
37
|
from chainlit.types import (
|
|
39
38
|
DeleteFeedbackRequest,
|
|
40
39
|
DeleteThreadRequest,
|
|
41
|
-
GenerationRequest,
|
|
42
40
|
GetThreadsRequest,
|
|
43
41
|
Theme,
|
|
44
42
|
UpdateFeedbackRequest,
|
|
45
43
|
)
|
|
46
44
|
from chainlit.user import PersistedUser, User
|
|
47
45
|
from fastapi import (
|
|
46
|
+
APIRouter,
|
|
48
47
|
Depends,
|
|
49
48
|
FastAPI,
|
|
49
|
+
Form,
|
|
50
50
|
HTTPException,
|
|
51
51
|
Query,
|
|
52
52
|
Request,
|
|
@@ -57,12 +57,14 @@ from fastapi import (
|
|
|
57
57
|
from fastapi.responses import FileResponse, HTMLResponse, JSONResponse, RedirectResponse
|
|
58
58
|
from fastapi.security import OAuth2PasswordRequestForm
|
|
59
59
|
from fastapi.staticfiles import StaticFiles
|
|
60
|
-
from fastapi_socketio import SocketManager
|
|
61
60
|
from starlette.datastructures import URL
|
|
62
61
|
from starlette.middleware.cors import CORSMiddleware
|
|
63
62
|
from typing_extensions import Annotated
|
|
64
63
|
from watchfiles import awatch
|
|
65
64
|
|
|
65
|
+
ROOT_PATH = os.environ.get("CHAINLIT_ROOT_PATH", "")
|
|
66
|
+
IS_SUBMOUNT = os.environ.get("CHAINLIT_SUBMOUNT", "") == "true"
|
|
67
|
+
|
|
66
68
|
|
|
67
69
|
@asynccontextmanager
|
|
68
70
|
async def lifespan(app: FastAPI):
|
|
@@ -70,9 +72,9 @@ async def lifespan(app: FastAPI):
|
|
|
70
72
|
port = config.run.port
|
|
71
73
|
|
|
72
74
|
if host == DEFAULT_HOST:
|
|
73
|
-
url = f"http://localhost:{port}"
|
|
75
|
+
url = f"http://localhost:{port}{ROOT_PATH}"
|
|
74
76
|
else:
|
|
75
|
-
url = f"http://{host}:{port}"
|
|
77
|
+
url = f"http://{host}:{port}{ROOT_PATH}"
|
|
76
78
|
|
|
77
79
|
logger.info(f"Your app is available at {url}")
|
|
78
80
|
|
|
@@ -113,7 +115,7 @@ async def lifespan(app: FastAPI):
|
|
|
113
115
|
logger.error(f"Error reloading module: {e}")
|
|
114
116
|
|
|
115
117
|
await asyncio.sleep(1)
|
|
116
|
-
await
|
|
118
|
+
await sio.emit("reload", {})
|
|
117
119
|
|
|
118
120
|
break
|
|
119
121
|
|
|
@@ -153,9 +155,9 @@ def get_build_dir(local_target: str, packaged_target: str):
|
|
|
153
155
|
packaged_build_dir = os.path.join(BACKEND_ROOT, packaged_target, "dist")
|
|
154
156
|
|
|
155
157
|
if config.ui.custom_build and os.path.exists(
|
|
156
|
-
os.path.join(APP_ROOT, config.ui.custom_build
|
|
158
|
+
os.path.join(APP_ROOT, config.ui.custom_build)
|
|
157
159
|
):
|
|
158
|
-
return os.path.join(APP_ROOT, config.ui.custom_build
|
|
160
|
+
return os.path.join(APP_ROOT, config.ui.custom_build)
|
|
159
161
|
elif os.path.exists(local_build_dir):
|
|
160
162
|
return local_build_dir
|
|
161
163
|
elif os.path.exists(packaged_build_dir):
|
|
@@ -167,12 +169,36 @@ def get_build_dir(local_target: str, packaged_target: str):
|
|
|
167
169
|
build_dir = get_build_dir("frontend", "frontend")
|
|
168
170
|
copilot_build_dir = get_build_dir(os.path.join("libs", "copilot"), "copilot")
|
|
169
171
|
|
|
170
|
-
|
|
171
172
|
app = FastAPI(lifespan=lifespan)
|
|
172
173
|
|
|
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
|
+
|
|
194
|
+
app.mount(
|
|
195
|
+
f"{ROOT_PATH}/public",
|
|
196
|
+
StaticFiles(directory="public", check_dir=False),
|
|
197
|
+
name="public",
|
|
198
|
+
)
|
|
199
|
+
|
|
174
200
|
app.mount(
|
|
175
|
-
"/assets",
|
|
201
|
+
f"{ROOT_PATH}/assets",
|
|
176
202
|
StaticFiles(
|
|
177
203
|
packages=[("chainlit", os.path.join(build_dir, "assets"))],
|
|
178
204
|
follow_symlink=config.project.follow_symlink,
|
|
@@ -181,7 +207,7 @@ app.mount(
|
|
|
181
207
|
)
|
|
182
208
|
|
|
183
209
|
app.mount(
|
|
184
|
-
"/copilot",
|
|
210
|
+
f"{ROOT_PATH}/copilot",
|
|
185
211
|
StaticFiles(
|
|
186
212
|
packages=[("chainlit", copilot_build_dir)],
|
|
187
213
|
follow_symlink=config.project.follow_symlink,
|
|
@@ -190,22 +216,6 @@ app.mount(
|
|
|
190
216
|
)
|
|
191
217
|
|
|
192
218
|
|
|
193
|
-
app.add_middleware(
|
|
194
|
-
CORSMiddleware,
|
|
195
|
-
allow_origins=config.project.allow_origins,
|
|
196
|
-
allow_credentials=True,
|
|
197
|
-
allow_methods=["*"],
|
|
198
|
-
allow_headers=["*"],
|
|
199
|
-
)
|
|
200
|
-
|
|
201
|
-
socket = SocketManager(
|
|
202
|
-
app,
|
|
203
|
-
cors_allowed_origins=[],
|
|
204
|
-
async_mode="asgi",
|
|
205
|
-
socketio_path="/ws/socket.io",
|
|
206
|
-
)
|
|
207
|
-
|
|
208
|
-
|
|
209
219
|
# -------------------------------------------------------------------------------
|
|
210
220
|
# SLACK HANDLER
|
|
211
221
|
# -------------------------------------------------------------------------------
|
|
@@ -213,11 +223,28 @@ socket = SocketManager(
|
|
|
213
223
|
if os.environ.get("SLACK_BOT_TOKEN") and os.environ.get("SLACK_SIGNING_SECRET"):
|
|
214
224
|
from chainlit.slack.app import slack_app_handler
|
|
215
225
|
|
|
216
|
-
@
|
|
217
|
-
async def
|
|
226
|
+
@router.post("/slack/events")
|
|
227
|
+
async def slack_endpoint(req: Request):
|
|
218
228
|
return await slack_app_handler.handle(req)
|
|
219
229
|
|
|
220
230
|
|
|
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
|
+
|
|
221
248
|
# -------------------------------------------------------------------------------
|
|
222
249
|
# HTTP HANDLERS
|
|
223
250
|
# -------------------------------------------------------------------------------
|
|
@@ -234,17 +261,22 @@ def get_html_template():
|
|
|
234
261
|
CSS_PLACEHOLDER = "<!-- CSS INJECTION PLACEHOLDER -->"
|
|
235
262
|
|
|
236
263
|
default_url = "https://github.com/Chainlit/chainlit"
|
|
237
|
-
default_meta_image_url =
|
|
264
|
+
default_meta_image_url = (
|
|
265
|
+
"https://chainlit-cloud.s3.eu-west-3.amazonaws.com/logo/chainlit_banner.png"
|
|
266
|
+
)
|
|
238
267
|
url = config.ui.github or default_url
|
|
239
268
|
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"
|
|
240
270
|
|
|
241
271
|
tags = f"""<title>{config.ui.name}</title>
|
|
272
|
+
<link rel="icon" href="{favicon_path}" />
|
|
242
273
|
<meta name="description" content="{config.ui.description}">
|
|
243
274
|
<meta property="og:type" content="website">
|
|
244
275
|
<meta property="og:title" content="{config.ui.name}">
|
|
245
276
|
<meta property="og:description" content="{config.ui.description}">
|
|
246
277
|
<meta property="og:image" content="{meta_image_url}">
|
|
247
|
-
<meta property="og:url" content="{url}">
|
|
278
|
+
<meta property="og:url" content="{url}">
|
|
279
|
+
<meta property="og:root_path" content="{ROOT_PATH}">"""
|
|
248
280
|
|
|
249
281
|
js = f"""<script>{f"window.theme = {json.dumps(config.ui.theme.to_dict())}; " if config.ui.theme else ""}</script>"""
|
|
250
282
|
|
|
@@ -274,6 +306,9 @@ def get_html_template():
|
|
|
274
306
|
content = replace_between_tags(
|
|
275
307
|
content, "<!-- FONT START -->", "<!-- FONT END -->", font
|
|
276
308
|
)
|
|
309
|
+
if ROOT_PATH:
|
|
310
|
+
content = content.replace('href="/', f'href="{ROOT_PATH}/')
|
|
311
|
+
content = content.replace('src="/', f'src="{ROOT_PATH}/')
|
|
277
312
|
return content
|
|
278
313
|
|
|
279
314
|
|
|
@@ -283,6 +318,7 @@ def get_user_facing_url(url: URL):
|
|
|
283
318
|
Handles deployment with proxies (like cloud run).
|
|
284
319
|
"""
|
|
285
320
|
|
|
321
|
+
ROOT_PATH = os.environ.get("CHAINLIT_ROOT_PATH", "")
|
|
286
322
|
chainlit_url = os.environ.get("CHAINLIT_URL")
|
|
287
323
|
|
|
288
324
|
# No config, we keep the URL as is
|
|
@@ -298,15 +334,26 @@ def get_user_facing_url(url: URL):
|
|
|
298
334
|
if config_url.path.endswith("/"):
|
|
299
335
|
config_url = config_url.replace(path=config_url.path[:-1])
|
|
300
336
|
|
|
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
|
+
|
|
301
348
|
return config_url.__str__() + url.path
|
|
302
349
|
|
|
303
350
|
|
|
304
|
-
@
|
|
351
|
+
@router.get("/auth/config")
|
|
305
352
|
async def auth(request: Request):
|
|
306
353
|
return get_configuration()
|
|
307
354
|
|
|
308
355
|
|
|
309
|
-
@
|
|
356
|
+
@router.post("/login")
|
|
310
357
|
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
|
|
311
358
|
if not config.code.password_auth_callback:
|
|
312
359
|
raise HTTPException(
|
|
@@ -335,14 +382,14 @@ async def login(form_data: OAuth2PasswordRequestForm = Depends()):
|
|
|
335
382
|
}
|
|
336
383
|
|
|
337
384
|
|
|
338
|
-
@
|
|
385
|
+
@router.post("/logout")
|
|
339
386
|
async def logout(request: Request, response: Response):
|
|
340
387
|
if config.code.on_logout:
|
|
341
388
|
return await config.code.on_logout(request, response)
|
|
342
389
|
return {"success": True}
|
|
343
390
|
|
|
344
391
|
|
|
345
|
-
@
|
|
392
|
+
@router.post("/auth/header")
|
|
346
393
|
async def header_auth(request: Request):
|
|
347
394
|
if not config.code.header_auth_callback:
|
|
348
395
|
raise HTTPException(
|
|
@@ -371,7 +418,7 @@ async def header_auth(request: Request):
|
|
|
371
418
|
}
|
|
372
419
|
|
|
373
420
|
|
|
374
|
-
@
|
|
421
|
+
@router.get("/auth/oauth/{provider_id}")
|
|
375
422
|
async def oauth_login(provider_id: str, request: Request):
|
|
376
423
|
if config.code.oauth_callback is None:
|
|
377
424
|
raise HTTPException(
|
|
@@ -412,7 +459,7 @@ async def oauth_login(provider_id: str, request: Request):
|
|
|
412
459
|
return response
|
|
413
460
|
|
|
414
461
|
|
|
415
|
-
@
|
|
462
|
+
@router.get("/auth/oauth/{provider_id}/callback")
|
|
416
463
|
async def oauth_callback(
|
|
417
464
|
provider_id: str,
|
|
418
465
|
request: Request,
|
|
@@ -496,41 +543,85 @@ async def oauth_callback(
|
|
|
496
543
|
return response
|
|
497
544
|
|
|
498
545
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
546
|
+
# specific route for azure ad hybrid flow
|
|
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,
|
|
503
553
|
):
|
|
504
|
-
|
|
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
|
+
)
|
|
505
560
|
|
|
506
|
-
|
|
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
|
+
)
|
|
507
567
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
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:
|
|
511
581
|
raise HTTPException(
|
|
512
|
-
status_code=
|
|
513
|
-
detail=
|
|
582
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
|
583
|
+
detail="Missing code",
|
|
514
584
|
)
|
|
515
585
|
|
|
516
|
-
|
|
517
|
-
|
|
586
|
+
url = get_user_facing_url(request.url)
|
|
587
|
+
token = await provider.get_token(code, url)
|
|
518
588
|
|
|
519
|
-
|
|
589
|
+
(raw_user_data, default_user) = await provider.get_user_info(token)
|
|
520
590
|
|
|
591
|
+
user = await config.code.oauth_callback(
|
|
592
|
+
provider_id, token, raw_user_data, default_user, id_token
|
|
593
|
+
)
|
|
521
594
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
trace_event("pp_get_llm_providers")
|
|
528
|
-
providers = get_llm_providers()
|
|
529
|
-
providers = [p.to_dict() for p in providers]
|
|
530
|
-
return JSONResponse(content={"providers": providers})
|
|
595
|
+
if not user:
|
|
596
|
+
raise HTTPException(
|
|
597
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
598
|
+
detail="Unauthorized",
|
|
599
|
+
)
|
|
531
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}")
|
|
532
608
|
|
|
533
|
-
|
|
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")
|
|
534
625
|
async def project_translations(
|
|
535
626
|
language: str = Query(default="en-US", description="Language code"),
|
|
536
627
|
):
|
|
@@ -546,7 +637,7 @@ async def project_translations(
|
|
|
546
637
|
)
|
|
547
638
|
|
|
548
639
|
|
|
549
|
-
@
|
|
640
|
+
@router.get("/project/settings")
|
|
550
641
|
async def project_settings(
|
|
551
642
|
current_user: Annotated[Union[User, PersistedUser], Depends(get_current_user)],
|
|
552
643
|
language: str = Query(default="en-US", description="Language code"),
|
|
@@ -562,9 +653,21 @@ async def project_settings(
|
|
|
562
653
|
if chat_profiles:
|
|
563
654
|
profiles = [p.to_dict() for p in chat_profiles]
|
|
564
655
|
|
|
656
|
+
starters = []
|
|
657
|
+
if config.code.set_starters:
|
|
658
|
+
starters = await config.code.set_starters(current_user)
|
|
659
|
+
if starters:
|
|
660
|
+
starters = [s.to_dict() for s in starters]
|
|
661
|
+
|
|
565
662
|
if config.code.on_audio_chunk:
|
|
566
663
|
config.features.audio.enabled = True
|
|
567
664
|
|
|
665
|
+
debug_url = None
|
|
666
|
+
data_layer = get_data_layer()
|
|
667
|
+
|
|
668
|
+
if data_layer and config.run.debug:
|
|
669
|
+
debug_url = await data_layer.build_debug_url()
|
|
670
|
+
|
|
568
671
|
return JSONResponse(
|
|
569
672
|
content={
|
|
570
673
|
"ui": config.ui.to_dict(),
|
|
@@ -574,11 +677,13 @@ async def project_settings(
|
|
|
574
677
|
"threadResumable": bool(config.code.on_chat_resume),
|
|
575
678
|
"markdown": markdown,
|
|
576
679
|
"chatProfiles": profiles,
|
|
680
|
+
"starters": starters,
|
|
681
|
+
"debugUrl": debug_url,
|
|
577
682
|
}
|
|
578
683
|
)
|
|
579
684
|
|
|
580
685
|
|
|
581
|
-
@
|
|
686
|
+
@router.put("/feedback")
|
|
582
687
|
async def update_feedback(
|
|
583
688
|
request: Request,
|
|
584
689
|
update: UpdateFeedbackRequest,
|
|
@@ -597,7 +702,7 @@ async def update_feedback(
|
|
|
597
702
|
return JSONResponse(content={"success": True, "feedbackId": feedback_id})
|
|
598
703
|
|
|
599
704
|
|
|
600
|
-
@
|
|
705
|
+
@router.delete("/feedback")
|
|
601
706
|
async def delete_feedback(
|
|
602
707
|
request: Request,
|
|
603
708
|
payload: DeleteFeedbackRequest,
|
|
@@ -616,7 +721,7 @@ async def delete_feedback(
|
|
|
616
721
|
return JSONResponse(content={"success": True})
|
|
617
722
|
|
|
618
723
|
|
|
619
|
-
@
|
|
724
|
+
@router.post("/project/threads")
|
|
620
725
|
async def get_user_threads(
|
|
621
726
|
request: Request,
|
|
622
727
|
payload: GetThreadsRequest,
|
|
@@ -641,7 +746,7 @@ async def get_user_threads(
|
|
|
641
746
|
return JSONResponse(content=res.to_dict())
|
|
642
747
|
|
|
643
748
|
|
|
644
|
-
@
|
|
749
|
+
@router.get("/project/thread/{thread_id}")
|
|
645
750
|
async def get_thread(
|
|
646
751
|
request: Request,
|
|
647
752
|
thread_id: str,
|
|
@@ -659,7 +764,7 @@ async def get_thread(
|
|
|
659
764
|
return JSONResponse(content=res)
|
|
660
765
|
|
|
661
766
|
|
|
662
|
-
@
|
|
767
|
+
@router.get("/project/thread/{thread_id}/element/{element_id}")
|
|
663
768
|
async def get_thread_element(
|
|
664
769
|
request: Request,
|
|
665
770
|
thread_id: str,
|
|
@@ -678,7 +783,7 @@ async def get_thread_element(
|
|
|
678
783
|
return JSONResponse(content=res)
|
|
679
784
|
|
|
680
785
|
|
|
681
|
-
@
|
|
786
|
+
@router.delete("/project/thread")
|
|
682
787
|
async def delete_thread(
|
|
683
788
|
request: Request,
|
|
684
789
|
payload: DeleteThreadRequest,
|
|
@@ -699,7 +804,7 @@ async def delete_thread(
|
|
|
699
804
|
return JSONResponse(content={"success": True})
|
|
700
805
|
|
|
701
806
|
|
|
702
|
-
@
|
|
807
|
+
@router.post("/project/file")
|
|
703
808
|
async def upload_file(
|
|
704
809
|
session_id: str,
|
|
705
810
|
file: UploadFile,
|
|
@@ -735,7 +840,7 @@ async def upload_file(
|
|
|
735
840
|
return JSONResponse(file_response)
|
|
736
841
|
|
|
737
842
|
|
|
738
|
-
@
|
|
843
|
+
@router.get("/project/file/{file_id}")
|
|
739
844
|
async def get_file(
|
|
740
845
|
file_id: str,
|
|
741
846
|
session_id: Optional[str] = None,
|
|
@@ -757,7 +862,7 @@ async def get_file(
|
|
|
757
862
|
raise HTTPException(status_code=404, detail="File not found")
|
|
758
863
|
|
|
759
864
|
|
|
760
|
-
@
|
|
865
|
+
@router.get("/files/{filename:path}")
|
|
761
866
|
async def serve_file(
|
|
762
867
|
filename: str,
|
|
763
868
|
current_user: Annotated[Union[User, PersistedUser], Depends(get_current_user)],
|
|
@@ -775,7 +880,7 @@ async def serve_file(
|
|
|
775
880
|
raise HTTPException(status_code=404, detail="File not found")
|
|
776
881
|
|
|
777
882
|
|
|
778
|
-
@
|
|
883
|
+
@router.get("/favicon")
|
|
779
884
|
async def get_favicon():
|
|
780
885
|
custom_favicon_path = os.path.join(APP_ROOT, "public", "favicon.*")
|
|
781
886
|
files = glob.glob(custom_favicon_path)
|
|
@@ -790,7 +895,7 @@ async def get_favicon():
|
|
|
790
895
|
return FileResponse(favicon_path, media_type=media_type)
|
|
791
896
|
|
|
792
897
|
|
|
793
|
-
@
|
|
898
|
+
@router.get("/logo")
|
|
794
899
|
async def get_logo(theme: Optional[Theme] = Query(Theme.light)):
|
|
795
900
|
theme_value = theme.value if theme else Theme.light.value
|
|
796
901
|
logo_path = None
|
|
@@ -812,19 +917,39 @@ async def get_logo(theme: Optional[Theme] = Query(Theme.light)):
|
|
|
812
917
|
return FileResponse(logo_path, media_type=media_type)
|
|
813
918
|
|
|
814
919
|
|
|
815
|
-
@
|
|
920
|
+
@router.get("/avatars/{avatar_id}")
|
|
921
|
+
async def get_avatar(avatar_id: str):
|
|
922
|
+
if avatar_id == "default":
|
|
923
|
+
avatar_id = config.ui.name
|
|
924
|
+
|
|
925
|
+
avatar_id = avatar_id.strip().lower().replace(" ", "_")
|
|
926
|
+
|
|
927
|
+
avatar_path = os.path.join(APP_ROOT, "public", "avatars", f"{avatar_id}.*")
|
|
928
|
+
|
|
929
|
+
files = glob.glob(avatar_path)
|
|
930
|
+
|
|
931
|
+
if files:
|
|
932
|
+
avatar_path = files[0]
|
|
933
|
+
media_type, _ = mimetypes.guess_type(avatar_path)
|
|
934
|
+
return FileResponse(avatar_path, media_type=media_type)
|
|
935
|
+
else:
|
|
936
|
+
return await get_favicon()
|
|
937
|
+
|
|
938
|
+
|
|
939
|
+
@router.head("/")
|
|
816
940
|
def status_check():
|
|
817
941
|
return {"message": "Site is operational"}
|
|
818
942
|
|
|
819
943
|
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
944
|
+
@router.get("/{full_path:path}")
|
|
945
|
+
async def serve():
|
|
946
|
+
html_template = get_html_template()
|
|
947
|
+
"""Serve the UI files."""
|
|
948
|
+
response = HTMLResponse(content=html_template, status_code=200)
|
|
949
|
+
|
|
950
|
+
return response
|
|
826
951
|
|
|
827
|
-
return response
|
|
828
952
|
|
|
953
|
+
app.include_router(router)
|
|
829
954
|
|
|
830
955
|
import chainlit.socket # noqa
|
chainlit/slack/app.py
CHANGED
|
@@ -71,7 +71,6 @@ class SlackEmitter(BaseChainlitEmitter):
|
|
|
71
71
|
is_message = step_type in [
|
|
72
72
|
"user_message",
|
|
73
73
|
"assistant_message",
|
|
74
|
-
"system_message",
|
|
75
74
|
]
|
|
76
75
|
is_chain_of_thought = bool(step_dict.get("parentId"))
|
|
77
76
|
is_empty_output = not step_dict.get("output")
|
|
@@ -177,8 +176,8 @@ async def get_user(slack_user_id: str):
|
|
|
177
176
|
slack_user = await slack_app.client.users_info(user=slack_user_id)
|
|
178
177
|
slack_user_profile = slack_user["user"]["profile"]
|
|
179
178
|
|
|
180
|
-
|
|
181
|
-
user = User(identifier=USER_PREFIX +
|
|
179
|
+
user_identifier = slack_user_profile.get("email") or slack_user_id
|
|
180
|
+
user = User(identifier=USER_PREFIX + user_identifier, metadata=slack_user_profile)
|
|
182
181
|
|
|
183
182
|
users_by_slack_id[slack_user_id] = user
|
|
184
183
|
|