From f1d4594b650acbdce2e85ca3f22c50adcab8f88d Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 5 Aug 2025 12:49:25 -0500 Subject: [PATCH 1/6] chore: deprecate 'over-reaching' Chat features --- CHANGELOG.md | 7 ++ shiny/ui/_chat.py | 161 +++++++++++++++++++--------------------------- 2 files changed, 73 insertions(+), 95 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab2aa6f47..d54dadf2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * `ui.panel_well()` is deprecated in favor of `ui.card()`. (#2038) +* Numerous `ui.Chat()` features have been deprecated in preparation for future removal to simplify the API (#2050) + * The `messages` parameter in the `Chat()` constructor is deprecated. Pass initial messages to the `.ui(messages=...)` method instead. + * The `tokenizer` parameter in the `Chat()` constructor is deprecated. Token counting and message trimming features will eventually be removed. + * The `format`, `token_limits`, `transform_user`, and `transform_assistant` parameters in the `.messages()` method are deprecated. Provider-specific formatting and message transformation features will eventually be removed. + * The `.transform_user_input()` and `.transform_assistant_response()` methods are deprecated. Message transformation features will eventually be removed. + * The `transform` parameter in the `.user_input()` method is deprecated. User input transformation features will eventually be removed. + ## [1.4.0] - 2025-04-08 diff --git a/shiny/ui/_chat.py b/shiny/ui/_chat.py index e8b768230..604b9aabd 100644 --- a/shiny/ui/_chat.py +++ b/shiny/ui/_chat.py @@ -157,21 +157,7 @@ async def handle_user_input(user_input: str): A unique identifier for the chat session. In Shiny Core, make sure this id matches a corresponding :func:`~shiny.ui.chat_ui` call in the UI. messages - A sequence of messages to display in the chat. A given message can be one of the - following: - - * A string, which is interpreted as markdown and rendered to HTML on the client. - * To prevent interpreting as markdown, mark the string as - :class:`~shiny.ui.HTML`. - * A UI element (specifically, a :class:`~shiny.ui.TagChild`). - * This includes :class:`~shiny.ui.TagList`, which take UI elements - (including strings) as children. In this case, strings are still - interpreted as markdown as long as they're not inside HTML. - * A dictionary with `content` and `role` keys. The `content` key can contain a - content as described above, and the `role` key can be "assistant" or "user". - - **NOTE:** content may include specially formatted **input suggestion** links - (see `.append_message()` for more information). + Deprecated. Use `.ui(messages=...)` instead. on_error How to handle errors that occur in response to user input. When `"unhandled"`, the app will stop running when an error occurs. Otherwise, a notification @@ -183,11 +169,8 @@ async def handle_user_input(user_input: str): * `"sanitize"`: Sanitize the error message before displaying it to the user. * `"unhandled"`: Do not display any error message to the user. tokenizer - The tokenizer to use for calculating token counts, which is required to impose - `token_limits` in `.messages()`. If not provided, a default generic tokenizer - is attempted to be loaded from the tokenizers library. A specific tokenizer - may also be provided by following the `TokenEncoding` (tiktoken or tozenizers) - protocol (e.g., `tiktoken.encoding_for_model("gpt-4o")`). + Deprecated. Token counting and message trimming features will be removed in a + future version. """ def __init__( @@ -201,6 +184,18 @@ def __init__( if not isinstance(id, str): raise TypeError("`id` must be a string.") + if messages: + warn_deprecated( + "The `messages` parameter in Chat() is deprecated. " + "Use `.ui(messages=...)` instead for starting messages." + ) + + if tokenizer is not None: + warn_deprecated( + "The `tokenizer` parameter in Chat() is deprecated. " + "Token counting and message trimming features will be removed in a future version." + ) + self.id = resolve_id(id) self.user_input_id = ResolvedId(f"{self.id}_user_input") self._transform_user: TransformUserInputAsync | None = None @@ -459,48 +454,22 @@ def messages( """ Reactively read chat messages - Obtain chat messages within a reactive context. The default behavior is - intended for passing messages along to a model for response generation where - you typically want to: - - 1. Cap the number of tokens sent in a single request (i.e., `token_limits`). - 2. Apply user input transformations (i.e., `transform_user`), if any. - 3. Not apply assistant response transformations (i.e., `transform_assistant`) - since these are predominantly for display purposes (i.e., the model shouldn't - concern itself with how the responses are displayed). + Obtain chat messages within a reactive context. Parameters ---------- format - The message format to return. The default value of `MISSING` means - chat messages are returned as :class:`ChatMessage` objects (a dictionary - with `content` and `role` keys). Other supported formats include: - - * `"anthropic"`: Anthropic message format. - * `"google"`: Google message (aka content) format. - * `"langchain"`: LangChain message format. - * `"openai"`: OpenAI message format. - * `"ollama"`: Ollama message format. + Deprecated. Provider-specific message formatting will be removed in a future + version. token_limits - Limit the conversation history based on token limits. If specified, only - the most recent messages that fit within the token limits are returned. This - is useful for avoiding "exceeded token limit" errors when sending messages - to the relevant model, while still providing the most recent context available. - A specified value must be a tuple of two integers. The first integer is the - maximum number of tokens that can be sent to the model in a single request. - The second integer is the amount of tokens to reserve for the model's response. - Note that token counts based on the `tokenizer` provided to the `Chat` - constructor. + Deprecated. Token counting and message trimming features will be removed in + a future version. transform_user - Whether to return user input messages with transformation applied. This only - matters if a `transform_user_input` was provided to the chat constructor. - The default value of `"all"` means all user input messages are transformed. - The value of `"last"` means only the last user input message is transformed. - The value of `"none"` means no user input messages are transformed. + Deprecated. Message transformation features will be removed in a future + version. transform_assistant - Whether to return assistant messages with transformation applied. This only - matters if an `transform_assistant_response` was provided to the chat - constructor. + Deprecated. Message transformation features will be removed in a future + version. Note ---- @@ -514,6 +483,30 @@ def messages( A tuple of chat messages. """ + if not isinstance(format, MISSING_TYPE): + warn_deprecated( + "The `format` parameter in messages() is deprecated. " + "Provider-specific message formatting will be removed in a future version." + ) + + if token_limits is not None: + warn_deprecated( + "The `token_limits` parameter in messages() is deprecated. " + "Token counting and message trimming features will be removed in a future version." + ) + + if transform_user != "all": + warn_deprecated( + "The `transform_user` parameter in messages() is deprecated. " + "Message transformation features will be removed in a future version." + ) + + if transform_assistant: + warn_deprecated( + "The `transform_assistant` parameter in messages() is deprecated. " + "Message transformation features will be removed in a future version." + ) + messages = self._messages() # Anthropic requires a user message first and no system messages @@ -982,25 +975,14 @@ def transform_user_input( self, fn: TransformUserInput | TransformUserInputAsync | None = None ) -> None | Callable[[TransformUserInput | TransformUserInputAsync], None]: """ - Transform user input. - - Use this method as a decorator on a function (`fn`) that transforms user input - before storing it in the chat messages returned by `.messages()`. This is - useful for implementing RAG workflows, like taking a URL and scraping it for - text before sending it to the model. - - Parameters - ---------- - fn - A function to transform user input before storing it in the chat - `.messages()`. If `fn` returns `None`, the user input is effectively - ignored, and `.on_user_submit()` callbacks are suspended until more input is - submitted. This behavior is often useful to catch and handle errors that - occur during transformation. In this case, the transform function should - append an error message to the chat (via `.append_message()`) to inform the - user of the error. + Deprecated. User input transformation features will be removed in a future version. """ + warn_deprecated( + "The transform_user_input() method is deprecated. " + "User input transformation features will be removed in a future version." + ) + def _set_transform(fn: TransformUserInput | TransformUserInputAsync): self._transform_user = _utils.wrap_async(fn) @@ -1024,31 +1006,14 @@ def transform_assistant_response( fn: TransformAssistantResponseFunction | None = None, ) -> None | Callable[[TransformAssistantResponseFunction], None]: """ - Transform assistant responses. - - Use this method as a decorator on a function (`fn`) that transforms assistant - responses before displaying them in the chat. This is useful for post-processing - model responses before displaying them to the user. - - Parameters - ---------- - fn - A function that takes a string and returns either a string, - :class:`shiny.ui.HTML`, or `None`. If `fn` returns a string, it gets - interpreted and parsed as a markdown on the client (and the resulting HTML - is then sanitized). If `fn` returns :class:`shiny.ui.HTML`, it will be - displayed as-is. If `fn` returns `None`, the response is effectively ignored. - - Note - ---- - When doing an `.append_message_stream()`, `fn` gets called on every chunk of the - response (thus, it should be performant), and can optionally access more - information (i.e., arguments) about the stream. The 1st argument (required) - contains the accumulated content, the 2nd argument (optional) contains the - current chunk, and the 3rd argument (optional) is a boolean indicating whether - this chunk is the last one in the stream. + Deprecated. Assistant response transformation features will be removed in a future version. """ + warn_deprecated( + "The transform_assistant_response() method is deprecated. " + "Assistant response transformation features will be removed in a future version." + ) + def _set_transform( fn: TransformAssistantResponseFunction, ): @@ -1255,6 +1220,12 @@ def user_input(self, transform: bool = False) -> str | None: 2. Maintaining message state separately from `.messages()`. """ + if transform: + warn_deprecated( + "The `transform` parameter in user_input() is deprecated. " + "User input transformation features will be removed in a future version." + ) + msg = self._latest_user_input() if msg is None: return None From 8178738db167a769e57fb235e2ab61a4832c05b7 Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 5 Aug 2025 13:36:28 -0500 Subject: [PATCH 2/6] update example --- shiny/api-examples/Chat/app-core.py | 15 +++++++++------ shiny/api-examples/Chat/app-express.py | 18 ++++++++---------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/shiny/api-examples/Chat/app-core.py b/shiny/api-examples/Chat/app-core.py index 4c4dc79c5..35aefb0b6 100644 --- a/shiny/api-examples/Chat/app-core.py +++ b/shiny/api-examples/Chat/app-core.py @@ -3,21 +3,24 @@ app_ui = ui.page_fillable( ui.panel_title("Hello Shiny Chat"), ui.chat_ui("chat"), - fillable_mobile=True, -) - -# Create a welcome message -welcome = """ + ui.chat_ui( + "chat", + messages=[ + """ Hi! This is a simple Shiny `Chat` UI. Enter a message below and I will simply repeat it back to you. To learn more about chatbots and how to build them with Shiny, check out [the documentation](https://shiny.posit.co/py/docs/genai-chatbots.html). """ + ], + ), + fillable_mobile=True, +) def server(input, output, session): - chat = ui.Chat(id="chat", messages=[welcome]) + chat = ui.Chat(id="chat") # Define a callback to run when the user submits a message @chat.on_user_submit diff --git a/shiny/api-examples/Chat/app-express.py b/shiny/api-examples/Chat/app-express.py index f268d0b5a..1170118f1 100644 --- a/shiny/api-examples/Chat/app-express.py +++ b/shiny/api-examples/Chat/app-express.py @@ -7,24 +7,22 @@ fillable_mobile=True, ) -# Create a welcome message -welcome = """ +# Create a chat instance +chat = ui.Chat(id="chat") + +# Display it, with a startup message +chat.ui( + messages=[ + """ Hi! This is a simple Shiny `Chat` UI. Enter a message below and I will simply repeat it back to you. To learn more about chatbots and how to build them with Shiny, check out [the documentation](https://shiny.posit.co/py/docs/genai-chatbots.html). """ - -# Create a chat instance -chat = ui.Chat( - id="chat", - messages=[welcome], + ], ) -# Display it -chat.ui() - # Define a callback to run when the user submits a message @chat.on_user_submit From e386a476979260445956a6edbb45b15de3d0bd03 Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 5 Aug 2025 13:52:04 -0500 Subject: [PATCH 3/6] Cleanup changelog --- CHANGELOG.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d54dadf2f..b175a2ce9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,14 +49,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Deprecations -* `ui.panel_well()` is deprecated in favor of `ui.card()`. (#2038) +* `ui.panel_well()` was deprecated in favor of `ui.card()`. (#2038) * Numerous `ui.Chat()` features have been deprecated in preparation for future removal to simplify the API (#2050) - * The `messages` parameter in the `Chat()` constructor is deprecated. Pass initial messages to the `.ui(messages=...)` method instead. - * The `tokenizer` parameter in the `Chat()` constructor is deprecated. Token counting and message trimming features will eventually be removed. - * The `format`, `token_limits`, `transform_user`, and `transform_assistant` parameters in the `.messages()` method are deprecated. Provider-specific formatting and message transformation features will eventually be removed. - * The `.transform_user_input()` and `.transform_assistant_response()` methods are deprecated. Message transformation features will eventually be removed. - * The `transform` parameter in the `.user_input()` method is deprecated. User input transformation features will eventually be removed. + * `Chat(messages=)` was deprecated. Use `chat.ui(messages=)` instead. + * `Chat(tokenizer=)` was deprecated. This was only relevant for `.messages(token_limits=[])` which is also now deprecated. + * All parameters to `.messages()` were deprecated. This reflects an overall change philosophy for maintaining the conversation history sent to the LLM -- `Chat` should no longer be responsible for maintaining it -- another stateful object (perhaps the one provided by chatlas, LangChain, etc.) should be used instead. That said, `.messages()` is still useful if you want to access UI message state. + * The `.transform_user_input` and `.transform_assistant_response` decorators were deprecated. Instead, transformations should be done manually. + * As a result of the previous deprecations, the `transform` parameter in the `.user_input()` method was also deprecated. ## [1.4.0] - 2025-04-08 From 81dfd9f562ee3fda0ac8fe0ae2f8889dd9416682 Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 5 Aug 2025 15:32:09 -0500 Subject: [PATCH 4/6] More cleanup --- CHANGELOG.md | 8 ++++---- shiny/ui/_chat.py | 43 +++++++++++++++++++++++++------------------ 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b175a2ce9..ef1967009 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,11 +52,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * `ui.panel_well()` was deprecated in favor of `ui.card()`. (#2038) * Numerous `ui.Chat()` features have been deprecated in preparation for future removal to simplify the API (#2050) - * `Chat(messages=)` was deprecated. Use `chat.ui(messages=)` instead. - * `Chat(tokenizer=)` was deprecated. This was only relevant for `.messages(token_limits=[])` which is also now deprecated. + * `Chat(messages=...)` was deprecated. Use `chat.ui(messages=...)` instead. + * `Chat(tokenizer=...)` was deprecated. This is only relevant for `.messages(token_limits=...)` which is also now deprecated. * All parameters to `.messages()` were deprecated. This reflects an overall change philosophy for maintaining the conversation history sent to the LLM -- `Chat` should no longer be responsible for maintaining it -- another stateful object (perhaps the one provided by chatlas, LangChain, etc.) should be used instead. That said, `.messages()` is still useful if you want to access UI message state. - * The `.transform_user_input` and `.transform_assistant_response` decorators were deprecated. Instead, transformations should be done manually. - * As a result of the previous deprecations, the `transform` parameter in the `.user_input()` method was also deprecated. + * The `.transform_user_input` and `.transform_assistant_response` decorators were deprecated. Instead, transformation of input/responses should be done manually and independently of `Chat`. + * As a result of the previous deprecations, `.user_input(transform=...)` was also deprecated. ## [1.4.0] - 2025-04-08 diff --git a/shiny/ui/_chat.py b/shiny/ui/_chat.py index 604b9aabd..9a9ea28cd 100644 --- a/shiny/ui/_chat.py +++ b/shiny/ui/_chat.py @@ -186,14 +186,13 @@ def __init__( if messages: warn_deprecated( - "The `messages` parameter in Chat() is deprecated. " - "Use `.ui(messages=...)` instead for starting messages." + "`Chat(messages=...)` is deprecated. Use `.ui(messages=...)` instead." ) if tokenizer is not None: warn_deprecated( - "The `tokenizer` parameter in Chat() is deprecated. " - "Token counting and message trimming features will be removed in a future version." + "`Chat(tokenizer=...)` is deprecated. " + "This is only relevant for `.messages(token_limits=...)` which is also now deprecated." ) self.id = resolve_id(id) @@ -485,26 +484,30 @@ def messages( if not isinstance(format, MISSING_TYPE): warn_deprecated( - "The `format` parameter in messages() is deprecated. " - "Provider-specific message formatting will be removed in a future version." + "`.messages(format=...)` is deprecated. " + "Provider-specific message formatting will be removed in a future version. " + "See this issue for more details: https://github.com/posit-dev/py-shiny/pull/2050" ) if token_limits is not None: warn_deprecated( - "The `token_limits` parameter in messages() is deprecated. " - "Token counting and message trimming features will be removed in a future version." + "`.messages(token_limits=...)` is deprecated. " + "Token counting and message trimming features will be removed in a future version. " + "See this issue for more details: https://github.com/posit-dev/py-shiny/pull/2050" ) if transform_user != "all": warn_deprecated( - "The `transform_user` parameter in messages() is deprecated. " - "Message transformation features will be removed in a future version." + "`.messages(transform_user=...)` is deprecated. " + "Message transformation features will be removed in a future version. " + "See this issue for more details: https://github.com/posit-dev/py-shiny/pull/2050" ) if transform_assistant: warn_deprecated( - "The `transform_assistant` parameter in messages() is deprecated. " - "Message transformation features will be removed in a future version." + "`.messages(transform_assistant=...)` is deprecated. " + "Message transformation features will be removed in a future version. " + "See this issue for more details: https://github.com/posit-dev/py-shiny/pull/2050" ) messages = self._messages() @@ -979,8 +982,9 @@ def transform_user_input( """ warn_deprecated( - "The transform_user_input() method is deprecated. " - "User input transformation features will be removed in a future version." + "The `.transform_user_input` decorator is deprecated. " + "User input transformation features will be removed in a future version. " + "See this issue for more details: https://github.com/posit-dev/py-shiny/pull/2050" ) def _set_transform(fn: TransformUserInput | TransformUserInputAsync): @@ -1010,8 +1014,9 @@ def transform_assistant_response( """ warn_deprecated( - "The transform_assistant_response() method is deprecated. " - "Assistant response transformation features will be removed in a future version." + "The `.transform_assistant_response` decorator is deprecated. " + "Assistant response transformation features will be removed in a future version. " + "See this issue for more details: https://github.com/posit-dev/py-shiny/pull/2050" ) def _set_transform( @@ -1220,10 +1225,12 @@ def user_input(self, transform: bool = False) -> str | None: 2. Maintaining message state separately from `.messages()`. """ + if transform: warn_deprecated( - "The `transform` parameter in user_input() is deprecated. " - "User input transformation features will be removed in a future version." + "`.user_input(transform=...)` is deprecated. " + "User input transformation features will be removed in a future version. " + "See this issue for more details: https://github.com/posit-dev/py-shiny/pull/2050" ) msg = self._latest_user_input() From a8251519ccd8b52d5afe929284a71662f8491c62 Mon Sep 17 00:00:00 2001 From: Carson Sievert Date: Tue, 5 Aug 2025 15:33:49 -0500 Subject: [PATCH 5/6] Update shiny/ui/_chat.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- shiny/ui/_chat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shiny/ui/_chat.py b/shiny/ui/_chat.py index 9a9ea28cd..a1cf9553c 100644 --- a/shiny/ui/_chat.py +++ b/shiny/ui/_chat.py @@ -157,7 +157,7 @@ async def handle_user_input(user_input: str): A unique identifier for the chat session. In Shiny Core, make sure this id matches a corresponding :func:`~shiny.ui.chat_ui` call in the UI. messages - Deprecated. Use `.ui(messages=...)` instead. + Deprecated. Use `chat.ui(messages=...)` instead. on_error How to handle errors that occur in response to user input. When `"unhandled"`, the app will stop running when an error occurs. Otherwise, a notification From 4cdeb19061c2e21824532a4ed668bcb850561101 Mon Sep 17 00:00:00 2001 From: Carson Date: Wed, 6 Aug 2025 12:02:28 -0500 Subject: [PATCH 6/6] Deprecations will come via shinychat package --- CHANGELOG.md | 9 +-- shiny/ui/_chat.py | 168 ++++++++++++++++++++++++++-------------------- 2 files changed, 96 insertions(+), 81 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef1967009..ab2aa6f47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,14 +49,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Deprecations -* `ui.panel_well()` was deprecated in favor of `ui.card()`. (#2038) - -* Numerous `ui.Chat()` features have been deprecated in preparation for future removal to simplify the API (#2050) - * `Chat(messages=...)` was deprecated. Use `chat.ui(messages=...)` instead. - * `Chat(tokenizer=...)` was deprecated. This is only relevant for `.messages(token_limits=...)` which is also now deprecated. - * All parameters to `.messages()` were deprecated. This reflects an overall change philosophy for maintaining the conversation history sent to the LLM -- `Chat` should no longer be responsible for maintaining it -- another stateful object (perhaps the one provided by chatlas, LangChain, etc.) should be used instead. That said, `.messages()` is still useful if you want to access UI message state. - * The `.transform_user_input` and `.transform_assistant_response` decorators were deprecated. Instead, transformation of input/responses should be done manually and independently of `Chat`. - * As a result of the previous deprecations, `.user_input(transform=...)` was also deprecated. +* `ui.panel_well()` is deprecated in favor of `ui.card()`. (#2038) ## [1.4.0] - 2025-04-08 diff --git a/shiny/ui/_chat.py b/shiny/ui/_chat.py index a1cf9553c..e8b768230 100644 --- a/shiny/ui/_chat.py +++ b/shiny/ui/_chat.py @@ -157,7 +157,21 @@ async def handle_user_input(user_input: str): A unique identifier for the chat session. In Shiny Core, make sure this id matches a corresponding :func:`~shiny.ui.chat_ui` call in the UI. messages - Deprecated. Use `chat.ui(messages=...)` instead. + A sequence of messages to display in the chat. A given message can be one of the + following: + + * A string, which is interpreted as markdown and rendered to HTML on the client. + * To prevent interpreting as markdown, mark the string as + :class:`~shiny.ui.HTML`. + * A UI element (specifically, a :class:`~shiny.ui.TagChild`). + * This includes :class:`~shiny.ui.TagList`, which take UI elements + (including strings) as children. In this case, strings are still + interpreted as markdown as long as they're not inside HTML. + * A dictionary with `content` and `role` keys. The `content` key can contain a + content as described above, and the `role` key can be "assistant" or "user". + + **NOTE:** content may include specially formatted **input suggestion** links + (see `.append_message()` for more information). on_error How to handle errors that occur in response to user input. When `"unhandled"`, the app will stop running when an error occurs. Otherwise, a notification @@ -169,8 +183,11 @@ async def handle_user_input(user_input: str): * `"sanitize"`: Sanitize the error message before displaying it to the user. * `"unhandled"`: Do not display any error message to the user. tokenizer - Deprecated. Token counting and message trimming features will be removed in a - future version. + The tokenizer to use for calculating token counts, which is required to impose + `token_limits` in `.messages()`. If not provided, a default generic tokenizer + is attempted to be loaded from the tokenizers library. A specific tokenizer + may also be provided by following the `TokenEncoding` (tiktoken or tozenizers) + protocol (e.g., `tiktoken.encoding_for_model("gpt-4o")`). """ def __init__( @@ -184,17 +201,6 @@ def __init__( if not isinstance(id, str): raise TypeError("`id` must be a string.") - if messages: - warn_deprecated( - "`Chat(messages=...)` is deprecated. Use `.ui(messages=...)` instead." - ) - - if tokenizer is not None: - warn_deprecated( - "`Chat(tokenizer=...)` is deprecated. " - "This is only relevant for `.messages(token_limits=...)` which is also now deprecated." - ) - self.id = resolve_id(id) self.user_input_id = ResolvedId(f"{self.id}_user_input") self._transform_user: TransformUserInputAsync | None = None @@ -453,22 +459,48 @@ def messages( """ Reactively read chat messages - Obtain chat messages within a reactive context. + Obtain chat messages within a reactive context. The default behavior is + intended for passing messages along to a model for response generation where + you typically want to: + + 1. Cap the number of tokens sent in a single request (i.e., `token_limits`). + 2. Apply user input transformations (i.e., `transform_user`), if any. + 3. Not apply assistant response transformations (i.e., `transform_assistant`) + since these are predominantly for display purposes (i.e., the model shouldn't + concern itself with how the responses are displayed). Parameters ---------- format - Deprecated. Provider-specific message formatting will be removed in a future - version. + The message format to return. The default value of `MISSING` means + chat messages are returned as :class:`ChatMessage` objects (a dictionary + with `content` and `role` keys). Other supported formats include: + + * `"anthropic"`: Anthropic message format. + * `"google"`: Google message (aka content) format. + * `"langchain"`: LangChain message format. + * `"openai"`: OpenAI message format. + * `"ollama"`: Ollama message format. token_limits - Deprecated. Token counting and message trimming features will be removed in - a future version. + Limit the conversation history based on token limits. If specified, only + the most recent messages that fit within the token limits are returned. This + is useful for avoiding "exceeded token limit" errors when sending messages + to the relevant model, while still providing the most recent context available. + A specified value must be a tuple of two integers. The first integer is the + maximum number of tokens that can be sent to the model in a single request. + The second integer is the amount of tokens to reserve for the model's response. + Note that token counts based on the `tokenizer` provided to the `Chat` + constructor. transform_user - Deprecated. Message transformation features will be removed in a future - version. + Whether to return user input messages with transformation applied. This only + matters if a `transform_user_input` was provided to the chat constructor. + The default value of `"all"` means all user input messages are transformed. + The value of `"last"` means only the last user input message is transformed. + The value of `"none"` means no user input messages are transformed. transform_assistant - Deprecated. Message transformation features will be removed in a future - version. + Whether to return assistant messages with transformation applied. This only + matters if an `transform_assistant_response` was provided to the chat + constructor. Note ---- @@ -482,34 +514,6 @@ def messages( A tuple of chat messages. """ - if not isinstance(format, MISSING_TYPE): - warn_deprecated( - "`.messages(format=...)` is deprecated. " - "Provider-specific message formatting will be removed in a future version. " - "See this issue for more details: https://github.com/posit-dev/py-shiny/pull/2050" - ) - - if token_limits is not None: - warn_deprecated( - "`.messages(token_limits=...)` is deprecated. " - "Token counting and message trimming features will be removed in a future version. " - "See this issue for more details: https://github.com/posit-dev/py-shiny/pull/2050" - ) - - if transform_user != "all": - warn_deprecated( - "`.messages(transform_user=...)` is deprecated. " - "Message transformation features will be removed in a future version. " - "See this issue for more details: https://github.com/posit-dev/py-shiny/pull/2050" - ) - - if transform_assistant: - warn_deprecated( - "`.messages(transform_assistant=...)` is deprecated. " - "Message transformation features will be removed in a future version. " - "See this issue for more details: https://github.com/posit-dev/py-shiny/pull/2050" - ) - messages = self._messages() # Anthropic requires a user message first and no system messages @@ -978,14 +982,24 @@ def transform_user_input( self, fn: TransformUserInput | TransformUserInputAsync | None = None ) -> None | Callable[[TransformUserInput | TransformUserInputAsync], None]: """ - Deprecated. User input transformation features will be removed in a future version. - """ + Transform user input. - warn_deprecated( - "The `.transform_user_input` decorator is deprecated. " - "User input transformation features will be removed in a future version. " - "See this issue for more details: https://github.com/posit-dev/py-shiny/pull/2050" - ) + Use this method as a decorator on a function (`fn`) that transforms user input + before storing it in the chat messages returned by `.messages()`. This is + useful for implementing RAG workflows, like taking a URL and scraping it for + text before sending it to the model. + + Parameters + ---------- + fn + A function to transform user input before storing it in the chat + `.messages()`. If `fn` returns `None`, the user input is effectively + ignored, and `.on_user_submit()` callbacks are suspended until more input is + submitted. This behavior is often useful to catch and handle errors that + occur during transformation. In this case, the transform function should + append an error message to the chat (via `.append_message()`) to inform the + user of the error. + """ def _set_transform(fn: TransformUserInput | TransformUserInputAsync): self._transform_user = _utils.wrap_async(fn) @@ -1010,14 +1024,30 @@ def transform_assistant_response( fn: TransformAssistantResponseFunction | None = None, ) -> None | Callable[[TransformAssistantResponseFunction], None]: """ - Deprecated. Assistant response transformation features will be removed in a future version. - """ + Transform assistant responses. - warn_deprecated( - "The `.transform_assistant_response` decorator is deprecated. " - "Assistant response transformation features will be removed in a future version. " - "See this issue for more details: https://github.com/posit-dev/py-shiny/pull/2050" - ) + Use this method as a decorator on a function (`fn`) that transforms assistant + responses before displaying them in the chat. This is useful for post-processing + model responses before displaying them to the user. + + Parameters + ---------- + fn + A function that takes a string and returns either a string, + :class:`shiny.ui.HTML`, or `None`. If `fn` returns a string, it gets + interpreted and parsed as a markdown on the client (and the resulting HTML + is then sanitized). If `fn` returns :class:`shiny.ui.HTML`, it will be + displayed as-is. If `fn` returns `None`, the response is effectively ignored. + + Note + ---- + When doing an `.append_message_stream()`, `fn` gets called on every chunk of the + response (thus, it should be performant), and can optionally access more + information (i.e., arguments) about the stream. The 1st argument (required) + contains the accumulated content, the 2nd argument (optional) contains the + current chunk, and the 3rd argument (optional) is a boolean indicating whether + this chunk is the last one in the stream. + """ def _set_transform( fn: TransformAssistantResponseFunction, @@ -1225,14 +1255,6 @@ def user_input(self, transform: bool = False) -> str | None: 2. Maintaining message state separately from `.messages()`. """ - - if transform: - warn_deprecated( - "`.user_input(transform=...)` is deprecated. " - "User input transformation features will be removed in a future version. " - "See this issue for more details: https://github.com/posit-dev/py-shiny/pull/2050" - ) - msg = self._latest_user_input() if msg is None: return None