6565 TransformAssistantResponseChunk ,
6666 TransformAssistantResponseChunkAsync ,
6767]
68- SubmitFunction = Callable [[], None ]
69- SubmitFunctionAsync = Callable [[], Awaitable [None ]]
68+ UserSubmitFunction0 = Union [
69+ Callable [[], None ],
70+ Callable [[], Awaitable [None ]],
71+ ]
72+ UserSubmitFunction1 = Union [
73+ Callable [[str ], None ],
74+ Callable [[str ], Awaitable [None ]],
75+ ]
76+ UserSubmitFunction = Union [
77+ UserSubmitFunction0 ,
78+ UserSubmitFunction1 ,
79+ ]
7080
7181ChunkOption = Literal ["start" , "end" , True , False ]
7282
@@ -79,9 +89,10 @@ class Chat:
7989 Create a chat interface.
8090
8191 A UI component for building conversational interfaces. With it, end users can submit
82- messages, which will cause a `.on_user_submit()` callback to run. In that callback,
83- a response can be generated based on the chat's `.messages()`, and appended to the
84- chat using `.append_message()` or `.append_message_stream()`.
92+ messages, which will cause a `.on_user_submit()` callback to run. That callback gets
93+ passed the user input message, which can be used to generate a response. The
94+ response can then be appended to the chat using `.append_message()` or
95+ `.append_message_stream()`.
8596
8697 Here's a rough outline for how to implement a `Chat`:
8798
@@ -94,11 +105,9 @@ class Chat:
94105
95106 # Define a callback to run when the user submits a message
96107 @chat.on_user_submit
97- async def _():
98- # Get messages currently in the chat
99- messages = chat.messages()
108+ async def handle_user_input(user_input):
100109 # Create a response message stream
101- response = await my_model.generate_response(messages , stream=True)
110+ response = await my_model.generate_response(user_input , stream=True)
102111 # Append the response into the chat
103112 await chat.append_message_stream(response)
104113 ```
@@ -112,6 +121,11 @@ async def _():
112121 response to the chat. Streaming is preferrable when available since it allows for
113122 more responsive and scalable chat interfaces.
114123
124+ It is also highly recommended to use a package like
125+ [chatlas](https://posit-dev.github.io/chatlas/) to generate responses, especially
126+ when responses should be aware of the chat history, support tool calls, etc.
127+ See this [article](https://posit-dev.github.io/chatlas/web-apps.html) to learn more.
128+
115129 Parameters
116130 ----------
117131 id
@@ -278,33 +292,31 @@ def ui(
278292 )
279293
280294 @overload
281- def on_user_submit (
282- self , fn : SubmitFunction | SubmitFunctionAsync
283- ) -> reactive .Effect_ : ...
295+ def on_user_submit (self , fn : UserSubmitFunction ) -> reactive .Effect_ : ...
284296
285297 @overload
286298 def on_user_submit (
287299 self ,
288- ) -> Callable [[SubmitFunction | SubmitFunctionAsync ], reactive .Effect_ ]: ...
300+ ) -> Callable [[UserSubmitFunction ], reactive .Effect_ ]: ...
289301
290302 def on_user_submit (
291- self , fn : SubmitFunction | SubmitFunctionAsync | None = None
292- ) -> (
293- reactive .Effect_
294- | Callable [[SubmitFunction | SubmitFunctionAsync ], reactive .Effect_ ]
295- ):
303+ self , fn : UserSubmitFunction | None = None
304+ ) -> reactive .Effect_ | Callable [[UserSubmitFunction ], reactive .Effect_ ]:
296305 """
297306 Define a function to invoke when user input is submitted.
298307
299- Apply this method as a decorator to a function (`fn`) that should be invoked when the
300- user submits a message. The function should take no arguments.
308+ Apply this method as a decorator to a function (`fn`) that should be invoked
309+ when the user submits a message. This function can take an optional argument,
310+ which will be the user input message.
301311
302- In many cases, the implementation of `fn` should do at least the following:
312+ In many cases, the implementation of `fn` should also do the following:
303313
304- 1. Call `.messages()` to obtain the current chat history.
305- 2. Generate a response based on those messages.
306- 3. Append the response to the chat history using `.append_message()` (
307- or `.append_message_stream()` if the response is streamed).
314+ 1. Generate a response based on the user input.
315+ * If the response should be aware of chat history, use a package
316+ like [chatlas](https://posit-dev.github.io/chatlas/) to manage the chat
317+ state, or use the `.messages()` method to get the chat history.
318+ 2. Append that response to the chat component using `.append_message()` ( or
319+ `.append_message_stream()` if the response is streamed).
308320
309321 Parameters
310322 ----------
@@ -318,8 +330,8 @@ def on_user_submit(
318330 but it will only be re-invoked when the user submits a message.
319331 """
320332
321- def create_effect (fn : SubmitFunction | SubmitFunctionAsync ):
322- afunc = _utils . wrap_async (fn )
333+ def create_effect (fn : UserSubmitFunction ):
334+ fn_params = inspect . signature (fn ). parameters
323335
324336 @reactive .effect
325337 @reactive .event (self ._user_input )
@@ -329,7 +341,21 @@ async def handle_user_input():
329341
330342 req (False )
331343 try :
332- await afunc ()
344+ if len (fn_params ) > 1 :
345+ raise ValueError (
346+ "A on_user_submit function should not take more than 1 argument"
347+ )
348+ elif len (fn_params ) == 1 :
349+ input = self .user_input (transform = True )
350+ # The line immediately below handles the possibility of input
351+ # being transformed to None. Technically, input should never be
352+ # None at this point (since the handler should be suspended).
353+ input = "" if input is None else input
354+ afunc = _utils .wrap_async (cast (UserSubmitFunction1 , fn ))
355+ await afunc (input )
356+ else :
357+ afunc = _utils .wrap_async (cast (UserSubmitFunction0 , fn ))
358+ await afunc ()
333359 except Exception as e :
334360 await self ._raise_exception (e )
335361
0 commit comments