Chainlit is running a Fast API server that you can extend with your own endpoints.
One good use case for this is to serve an assistant through a rest API.
If you create or update a custom endpoint, you will have to restart your
Chainlit process. The file watcher will not pick up changes.
How it works
To add a custom endpoint, import the FastAPI app
instance from chainlit.server
and declare your endpoint as you would in FastAPI.
from chainlit.server import app
from fastapi import Request
from fastapi.responses import (
HTMLResponse,
)
@app.get("/hello")
def hello(request: Request):
print(request.headers)
return HTMLResponse("Hello World")
Use Chainlit APIs in your endpoint
To use Chainlit APIs, a Chainlit context is required.
HTTP context
In an HTTP context, Chainlit APIs such as Message.send() will do nothing.
If data persistence is enabled, the Chainlit APIs will still persist data.
from chainlit.server import app
from fastapi import Request
from fastapi.responses import (
HTMLResponse,
)
from chainlit.context import init_http_context
import chainlit as cl
@app.get("/hello")
async def hello(
request: Request,
):
init_http_context()
await cl.Message(content="Hello World").send()
return HTMLResponse("Hello World")
Websocket context
The only use case that requires to use the Websocket context within a custom endpoint is to send data to a websocket client (which you know the session ID of) based on some arbitrary HTTP request.
from fastapi import Request
from fastapi.responses import HTMLResponse
from chainlit.server import app
from chainlit.context import init_ws_context
from chainlit.session import WebsocketSession
import chainlit as cl
@cl.on_chat_start
def main():
print("Session id:", cl.user_session.get("id"))
@app.get("/hello/{session_id}")
async def hello(
request: Request,
session_id: str,
):
ws_session = WebsocketSession.get_by_id(session_id=session_id)
init_ws_context(ws_session)
await cl.Message(content="Hello World").send()
return HTMLResponse("Data sent to the websocket client")
Authentication
You can use any authentication system since the request is accessible.
However Chainlit authentication is fully compatible with custom endpoints.
from typing_extensions import Annotated
from fastapi import Request, Depends
from fastapi.responses import (
HTMLResponse,
)
from chainlit.server import app
from chainlit.context import init_http_context
from chainlit.auth import authenticate_user
import chainlit as cl
@app.get("/hello")
async def hello(
request: Request,
current_user: Annotated[
Union[cl.User], Depends(authenticate_user)
],
):
init_http_context(user=current_user)
await cl.Message(content="Hello World").send()
return HTMLResponse("Hello World")
Once an endpoint is protected, it will require to have a valid token in the Authorization
header.
{
"Authorization": "Bearer TOKEN"
}
Generate a token
The token is the same token generated when you login in the Chainlit app.
You can generate a token manually for a given user with this python script.
This will require to have a CHAINLIT_AUTH_SECRET. You can run chainlit create-secret
to create one.
from chainlit.auth import create_jwt
import chainlit as cl
print(create_jwt(cl.User(identifier="USERNAME")))