chainlit
                                
                                
                                
                                    chainlit copied to clipboard
                            
                            
                            
                        Make browser query parameters available in `@on_chat_start`
Feature request for query params to be made available in the on_chat_start decorator. From there you could add them to user session or take another action.
@on_chat_start
async def main(query_params):
    document_url = query_params.get('document_url')
    if document_url:
        ....
Imagine an application that lists multiple documents and you want to design a chainlit that can Q&A one of those documents.
Possible workflow today:
- The user downloads the document from the application.
 - In a separate browser tab they open the chainlit.
 - In the chainlit they upload the file with a 
AskFileMessagedialog. - Process the file and chat.
 
Possible workflow enabled by query params:
- User clicks a [Chat] button next the document they want to Q&A and a new browser tab opens to mychainlit.xyz?document_url=/my/doc.pdf
 - The chainlit begins processing the file referenced in the document.
 
Additional scenarios could include:
- passing a reference to an ID to an API accessible resource
 - chaining chainlit apps by passing fragments of one chat to another chainlit as part of a larger workflow
 
I would very much like to support @kevinwmerritt's idea—I think it's a feature with a big impact.
Noted! Will try to ship that in the 1.0.0 release
@willydouhard do you have any updates on this matter? thanks a lot
Did not have the time to add it yet. I am unsure about the behaviour though. Let's say you connect to http://localhost:8000?params={...}. In this case the json would probably encoded in b64 but that's not the point.
So we send that to the socket connection headers. Now let's say I navigate in the app a few times and create a new session. Is it expected that the params are sent again? In that case that means we should store them in the local storage or something. Also what happens if I refresh the page without the ?params?
from my perspective it should be something like in streamlit: https://docs.streamlit.io/library/api-reference/utilities/st.experimental_get_query_params
what's your opinion on this?
I think it works well with Streamlit since you have more freedom and control over the app. In Chainlit these url parameters will disappear as the user uses the app.
To be clear I think this is a good feature but I don't have the right design atm.
@willydouhard just give either the request object or at least the GET/POST/Headers as optional param to the@cl.on_chat_start method. I guess that involves forwarding the data from python to react when loading the page, and then back react to python for the handlers..
Alternatively or additionally, do the same here:
@app.post("/auth/header")
async def header_auth(request: Request):
    if not config.code.header_auth_callback:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="No header_auth_callback defined",
        )
    app_user = await config.code.header_auth_callback(request.headers, add the request here)
    ...
PS: I just tried to use cookies, but of course, with an <iframe> and Apple, that's not working at all... A bit at a loss now..
Update: We now pass the needed GET param as a header to chainlit, which involves a) overriding the /login route to inject a JS into the frontend, which in turn overrides the JS fetch() method to add the needed header with our parameter. And then we get it finally where we need it to create the users, in our header_auth_callback().
If anyone needs more details, let me know. Of course we hope for a native clean solution - but at least we managed to not do a vendor code hack here.
This kind of feature would be super useful for creating links with pre-populated chats, which is somewhat similar to the document Id scenario above
For instance in an app you could embed links where the user clicks them and it launches your bot with the user's input entered ready for them to submit (or to add to if they need something extra in their input). This takes some friction out of the interaction. In some cases you could imagine dynamically creating the link parameters too (eg in Excel you could have a list of company names and each row gets a custom link to ask for more about that particular company using Excel's HYPERLINK function to build the link).
I was able to get close to this with a few hacky lines of custom js to write a parameter value into the user input box but I haven't the React experience to have that feed into the message properly (so the input box quickly gets overwritten with blank text unless the user specifically types something in the input box immediately, which isn't a good user experience).
I need this feature
Update: We now pass the needed GET param as a header to chainlit, which involves a) overriding the /login route to inject a JS into the frontend, which in turn overrides the JS fetch() method to add the needed header with our parameter. And then we get it finally where we need it to create the users, in our header_auth_callback().
If anyone needs more details, let me know. Of course we hope for a native clean solution - but at least we managed to not do a vendor code hack here.
@bachi76 Please share:)
I have found a solution that might work for others as well.
Let's assume we have the following URL with query parameter: https://xyz.ch/?agent_id=myAgentId
First, I define a custom_js with the following content:
window.addEventListener("chainlit-call-fn", (e) => {
  const { name, args, callback } = e.detail;
  if (name === "url_query_parameter") {
    callback((new URLSearchParams(window.location.search)).get(args.msg));
  }
});
And then I can use this CopilotFunction in Chainlit as follows, for example:
@cl.on_chat_start
async def main():
    agent_id = await cl.CopilotFunction(name="url_query_parameter", args={"msg": "agent_id"}).acall()
                                    
                                    
                                    
                                
Would love to add this in the docs examples or advanced concepts. Do you have time for this @andreasmartin ?
This is broken since Chainlit 1.1.300. Can anyone help me out with fastapi code that "send" the url parameter to the chainlit module?
I can confirm this is broken, and the changes are not mentioned in the documentation.
I agree, this is really a needed feature.. There needs to be some way to send arbitrary parameters to a chainlit app, and there currently is not anything that doesn't require extensive hacking.
For others looking for a workaround, I am currently doing this, but I'm not sure how reliable this is at all as it is very much a hack...
Basically, I see that chainlit, at least in my setup, passes the "referrer" header from the browser when doing the authentication. Assuming your chainlit app is running at the path /chat, if you direct a user to https://example.com/chat/login?arg1=arg1&arg2=arg2 and then have code something like this:
from chainlit import Message, User
from typing import Dict, Optional
from urllib.parse import urlparse, parse_qs
import chainlit as cl
@cl.header_auth_callback
async def header_auth_callback(headers: Dict) -> Optional[User]:
    user = None
    referrer = headers.get('referer')
    if referrer:
        parsed_url = urlparse(referrer)
        query_params = parse_qs(parsed_url.query)
        payload = dict(
            ...
        )
        
        user = User(
                identifier='asldkfj',
                metadata=payload,
        )
    return user
@cl.on_chat_start
async def chat_start():
    logger.info(f'Chat started.')
    user = cl.user_session.get("user")
    await Message(
        content=user.metadata,
    ).send()
You can basically, during your authentication hook, just add whatever you want from the query string or headers into your user payload. You can even just do this without needing to do anything further or for actual authentication.
This is at least slightly elegant, mainly because it is pure python.
Some good ideas/workarounds here. I see several users trying to do this in discord forum as well. I am trying to address a similar need where I am trying to create a no-code agent. Chainlit application acts as generic agent driver, accepts agent_id as path/query parameter. And in on_chat_start, generic agent driver can pull in model, starter_prompts, system prompt, any tools etc, setup langchain and continue. All this works, except getting access to agent_id from the path.
I posted this in discord yesterday https://discord.com/channels/1088038867602526210/1126876648839598130/1260866911298781214
I wanted a solution with no hacks at all. So, I made some minor changes to chainlit backend today to achieve this. Essentially, when a client connects, I am extracting PATH_INFO from WSGI environ and adding it to WebsocketSession along with other attributes that already exist. And then, added it to UserSession as well. Same thing can be extended to query parameter with couple of more lines.
I just tested this and I can get "http_path" from session object and can extract agent_id from the path. Planning to raise a PR soon. In the meantime, here is my commit to the fork https://github.com/nethi/chainlit/commit/c90b4a5f6cbd87e4c1126ac83a2b07683883d267
I need similar solution to other non-websocket related REST APIs and chainlit hooks, where access to Request object is requried . For example, I want to agent_id specific profiles to be dispalyed - I am not familiar with FastAPI, but flask has a nice way to get to the context object without explicitly passing in in the arguments. It would be nice to make Request object (or some attributes likes path, query, headers etc) made available in these chainlit hooks. I believe, FastAPI context var could be a solution- still looking around
Chainlit is a wonderful framework. Great work and thank you @willydouhard
There is a very easy but undocumented solution to this as of 1.1.0 - the User Session docs are incomplete but per the release notes the object now includes the "http referer" [sic].
Thus, the following works:
@cl.on_chat_start
async def start():
    path = cl.user_session.get("http_referer")
    # get query string from path
    query_string = urllib3.util.parse_url(path).query
    print(query_string)
This should be added to the docs and the bug reports/feature requests closed, IMO. Kicking myself for not figuring this out quicker - IMO it is poorly named because while in-spec (the resource is being used on the "refering" page) it is not, for example, going to give you the actual referrer (eg news.ycombinator.com if someone clicks a link to your agent from HN). I don't think there needs to be a separate query strings parameter because, per example above, extracting that is trivial given the "referer."
I observed that cl.user_session.get("http_referer") returns None if I start the UI in browser by typing in the address. But this may be useful in other scenarios where http_referer returns valid value.
I would indeed be concerned that this might not work during the login flow too, since the query string may be lost during the redirects, as described as a limiter at the beginning of this issue. I don't know whether that is the case or not, but that is why I built my workaround into the login hook, and link users directly to the login endpoint, rather than looking elsewhere.
Some good ideas/workarounds here. I see several users trying to do this in discord forum as well. I am trying to address a similar need where I am trying to create a no-code agent. Chainlit application acts as generic agent driver, accepts agent_id as path/query parameter. And in on_chat_start, generic agent driver can pull in model, starter_prompts, system prompt, any tools etc, setup langchain and continue. All this works, except getting access to agent_id from the path.
I posted this in discord yesterday discord.com/channels/1088038867602526210/1126876648839598130/1260866911298781214
I wanted a solution with no hacks at all. So, I made some minor changes to chainlit backend today to achieve this. Essentially, when a client connects, I am extracting PATH_INFO from WSGI environ and adding it to WebsocketSession along with other attributes that already exist. And then, added it to UserSession as well. Same thing can be extended to query parameter with couple of more lines.
I just tested this and I can get "http_path" from session object and can extract agent_id from the path. Planning to raise a PR soon. In the meantime, here is my commit to the fork nethi@c90b4a5
I need similar solution to other non-websocket related REST APIs and chainlit hooks, where access to Request object is requried . For example, I want to agent_id specific profiles to be dispalyed - I am not familiar with FastAPI, but flask has a nice way to get to the context object without explicitly passing in in the arguments. It would be nice to make Request object (or some attributes likes path, query, headers etc) made available in these chainlit hooks. I believe, FastAPI context var could be a solution- still looking around
Chainlit is a wonderful framework. Great work and thank you @willydouhard
I made some more changes to the fork
- added support for accessing query parameters
 - An option to pass a separate path for socket.io path
 
WIth these changes, I now have a chainlit based generic agent driver that can accept an agent_id in the URL (can also be query parameter), call out to another service to get details of the agent (given the agent id), containing details like model profiles, starter prompts, system prompt etc. With this, it is now possible to build a fully capable no-code agent (like that of custom GPT assistant, Hugging face assistatnst), all using chainlit framework.
I also need this feature, hope it can be added in the future
+1 for requesting this feature. in the meantime, does anyone recommend another supported method for providing user metadata or native app context to the chat web app without having to make the user provide it (e.g. user profile info)?
I've created a PR https://github.com/Chainlit/chainlit/pull/1239 for this, thanks to the many suggestions in this thread. Pls help to test/review. A working version can be seen here: https://2in.ai/
I'm closing this in favour of #1213, which would make the entire request object (including cookies, URL, headers) available. Happy to receive PR's for that one and hereby explicitly offering guidance/bike-shedding on spec'ing the feature.