Closed freddyaboulton closed 2 months ago
• | Name | Status | URL |
---|---|---|---|
Spaces | ready! | Spaces preview | |
Website | ready! | Website preview | |
Storybook | ready! | Storybook preview | |
:unicorn: | Changes | detected! | Details |
Install Gradio from this PR
pip install https://gradio-builds.s3.amazonaws.com/96d6e61c927fcf15374934cfde976c0a25000db3/gradio-4.37.2-py3-none-any.whl
Install Gradio Python Client from this PR
pip install "gradio-client @ git+https://github.com/gradio-app/gradio@96d6e61c927fcf15374934cfde976c0a25000db3#subdirectory=client/python"
Install Gradio JS Client from this PR
npm install https://gradio-builds.s3.amazonaws.com/96d6e61c927fcf15374934cfde976c0a25000db3/gradio-client-1.2.1.tgz
Package | Version |
---|---|
@gradio/chatbot |
minor |
@gradio/tootils |
minor |
gradio |
minor |
website |
minor |
Support message format in chatbot 💬
gr.Chatbot
andgr.ChatInterface
now support the Messages API, which is fully compatible with LLM API providers such as Hugging Face Text Generation Inference, OpenAI's chat completions API, and Llama.cpp server.Building Gradio applications around these LLM solutions is now even easier!
gr.Chatbot
andgr.ChatInterface
now have amsg_format
parameter that can accept two values -'tuples'
and'messages'
. If set to'tuples'
, the default chatbot data format is expected. If set to'messages'
, a list of dictionaries withcontent
androle
keys is expected. See below -def chat_greeter(msg, history): history.append({"role": "assistant", "content": "Hello!"}) return history
Additionally, gradio now exposes a
gr.ChatMessage
dataclass you can use for IDE type hints and auto completion.Tool use in Chatbot 🛠️
The Gradio Chatbot can now natively display tool usage and intermediate thoughts common in Agent and chain-of-thought workflows!
If you are using the new "messages" format, simply add a
metadata
key with a dictionary containing atitle
key andvalue
. This will display the assistant message in an expandable message box to show the result of a tool or intermediate step.import gradio as gr from gradio import ChatMessage import time def generate_response(history): history.append(ChatMessage(role="user", content="What is the weather in San Francisco right now?")) yield history time.sleep(0.25) history.append(ChatMessage(role="assistant", content="In order to find the current weather in San Francisco, I will need to use my weather tool.") ) yield history time.sleep(0.25) history.append(ChatMessage(role="assistant", content="API Error when connecting to weather service.", metadata={"title": "💥 Error using tool 'Weather'"}) ) yield history time.sleep(0.25) history.append(ChatMessage(role="assistant", content="I will try again", )) yield history time.sleep(0.25) history.append(ChatMessage(role="assistant", content="Weather 72 degrees Fahrenheit with 20% chance of rain.", metadata={"title": "🛠️ Used tool 'Weather'"} )) yield history time.sleep(0.25) history.append(ChatMessage(role="assistant", content="Now that the API succeeded I can complete my task.", )) yield history time.sleep(0.25) history.append(ChatMessage(role="assistant", content="It's a sunny day in San Francisco with a current temperature of 72 degrees Fahrenheit and a 20% chance of rain. Enjoy the weather!", )) yield history with gr.Blocks() as demo: chatbot = gr.Chatbot(msg_format="messages") button = gr.Button("Get San Francisco Weather") button.click(generate_response, chatbot, chatbot) if __name__ == "__main__": demo.launch()
⚠️ The changeset file for this pull request has been modified manually, so the changeset generation bot has been disabled. To go back into automatic mode, delete the changeset file.
@freddyaboulton this looks great! Just one quibble: I'd suggest not using "openai" as the name of the format. It might be that they modify their message format in a way that we don't want to track. Also Anthropic and others use the same format as well. What about "tuples" | "dicts"
?
So thinking how we could add support for components into this: we could modify the content
key to accept Component
as well?
class Message(GradioModel):
role: str
metadata: Metadata = Field(default_factory=Metadata)
content: str | FileData | Component
wdyt @dawoodkhan82 @freddyaboulton
Yes makes sense regarding renaming. What about "messages" (the name used by tgi/transformers and from the looks of it is actually the industry standard name transformers docs anthropic docs )
"messages" sounds good, compatibility with transformers/tgi makes more sense 👍
This format looks good.
So thinking how we could add support for components into this: we could modify the content key to accept Component as well?
Regrading this, it would have to be another dict ComponentMessage
or ComponentContent
which stores the component name, the processed value, and the constructor_args.
class ComponentMessage(GradioModel):
component: str
value: Any
constructor_args: List[Dict[str, Any]]
imo it would be much nicer DX if the ComponentMessage
class was only used internally and the developer could just pass in a Component object, e.g. gr.Gallery([..., ...])
Yes that is what I had in mind, we can handle the conversion from component instance to internal payload format in pre/postprocess
import gradio as gr
with gr.Blocks() as demo:
gr.Chatbot([
gr.ChatMessage(role="user", content="Hello!"),
gr.ChatMessage(role="user", content="Hello!")
], msg_format="messages")
demo.launch()
import gradio as gr
with gr.Blocks() as demo:
gr.Chatbot([
gr.ChatMessage(role="abc", content="Hello!"),
], msg_format="messages")
demo.launch()
import gradio as gr
demo = gr.ChatInterface(lambda x,y:x, msg_format="messages")
demo.launch()
@freddyaboulton very nice PR! Made a first pass and left some comments above. Down to do another deeper review again once these comments are addressed
Thanks for the review @abidlabs !! I think I got all of the comments (and added some more unit tests because of them :) )
(1) The first concerns this:
If msg_format is "messages", then in ChatInterface developers can just yield the next token. They don't have to yield the entire message up to and including that token. I think this makes demos easier to write. And lets developers simply yield from their iterator.
Although I agree that this makes demos easier to write, this introduces a different behavior for iterators in the very special case where you are iterating from a ChatInterface with msg_format="messages". This is likely to confuse users who are used to sending the complete message with yield
in all other cases. It also could lead to bugs. For example, if you run
python demo/chatinterface_streaming_echo/messages_testcase.py
and then use it via the client, e.g.
from gradio_client import Client
client = Client("http://127.0.0.1:7864/")
result = client.predict(
message="Hello!!",
api_name="/chat"
)
print(result)
You only get "!" (the final token). But if you run the regular version of this demo (with msg_format="tuples"), you get the entire final string: "You typed: Hello!!". This introduces a discrepancy between what a user would observe if they used the Gradio UI and what you get when you make a prediction with the client.
(2) Just to reiterate the earlier point about the design of tools, I think we can improve the UI quite a bit
On the second point, I think an accordion would be the best UI. This is one area I like Streamlit's UI. If its an accordion, we should keep the accordion open if the accordion is the final message, but then collapse it if there are subsequent messages.
cc @pngwn @hannahblair on this front. This isn't a blocker but I think having a nice UI for tools will facilitate some nice viral comms down the road
(3) Let's add some docs for this, perhaps in the chatbot/chatinterface guides. Excited to do some nice comms here!
Discussed with @freddyaboulton and we don't need this:
We should make the color of the tool "box" match the color of the messages
if we are bringing the bubbles back. @pngwn perhaps you could review the design of the chatbot_with_tools
demo after you revert the bubbles back to ensure it looks good in both light and dark mode.
Thanks everyone for the reviews! I addressed all comments and updated the chatbot docs and release notes. Will prepare a guide soon.
Description
Main Changes:
Adds a
msg_format
parameter toChatbot
andChatInterface
so that messages can be returned as either the current list of tuples or a dictionary that is a superset of the messages/"OAI" format, e.g. {"role": "user", "content": ""}.Adds a ChatMessage dataclass that can be used instead of a dictionary when `msg_format="messages". This is nice because the IDE will autocomplete.
~If
msg_format
is "messages", then inChatInterface
developers can just yield the next token. They don't have to yield the entire message up to and including that token. I think this makes demos easier to write. And lets developers simply yield from their iterator.~Added the ability to parametrize e2e tests. If you create a python file that ends with
_testcase.py
in a demo directory corresponding to an e2e test, that demo will also be loaded to the e2e app. And you can usego_to_testcase
to navigate to that testcase.Messages format overview
The message format is a dict with two required keys
role
andcontent
. There is an additionalmetadata
key that is not required but it can be used for tools and additional info about the message. Most messages returned from an "openai compatible" client will be compatible with gradio.The implementation of the message is below:
Examples
Realistic Inference API Streaming
ChatInterface Streaming
ChatInterface Multimodal
Chatbot
Multimodal Chatbot
Closes: https://github.com/gradio-app/gradio/issues/7118