65
65
TransformAssistantResponseChunk ,
66
66
TransformAssistantResponseChunkAsync ,
67
67
]
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
+ ]
70
80
71
81
ChunkOption = Literal ["start" , "end" , True , False ]
72
82
@@ -79,9 +89,10 @@ class Chat:
79
89
Create a chat interface.
80
90
81
91
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()`.
85
96
86
97
Here's a rough outline for how to implement a `Chat`:
87
98
@@ -94,11 +105,9 @@ class Chat:
94
105
95
106
# Define a callback to run when the user submits a message
96
107
@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):
100
109
# 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)
102
111
# Append the response into the chat
103
112
await chat.append_message_stream(response)
104
113
```
@@ -112,6 +121,11 @@ async def _():
112
121
response to the chat. Streaming is preferrable when available since it allows for
113
122
more responsive and scalable chat interfaces.
114
123
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
+
115
129
Parameters
116
130
----------
117
131
id
@@ -278,33 +292,31 @@ def ui(
278
292
)
279
293
280
294
@overload
281
- def on_user_submit (
282
- self , fn : SubmitFunction | SubmitFunctionAsync
283
- ) -> reactive .Effect_ : ...
295
+ def on_user_submit (self , fn : UserSubmitFunction ) -> reactive .Effect_ : ...
284
296
285
297
@overload
286
298
def on_user_submit (
287
299
self ,
288
- ) -> Callable [[SubmitFunction | SubmitFunctionAsync ], reactive .Effect_ ]: ...
300
+ ) -> Callable [[UserSubmitFunction ], reactive .Effect_ ]: ...
289
301
290
302
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_ ]:
296
305
"""
297
306
Define a function to invoke when user input is submitted.
298
307
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.
301
311
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:
303
313
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).
308
320
309
321
Parameters
310
322
----------
@@ -318,8 +330,8 @@ def on_user_submit(
318
330
but it will only be re-invoked when the user submits a message.
319
331
"""
320
332
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
323
335
324
336
@reactive .effect
325
337
@reactive .event (self ._user_input )
@@ -329,7 +341,21 @@ async def handle_user_input():
329
341
330
342
req (False )
331
343
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 ()
333
359
except Exception as e :
334
360
await self ._raise_exception (e )
335
361
0 commit comments