Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve tool call message processing #3036

Merged
merged 8 commits into from
Feb 21, 2025
Merged

Conversation

drbh
Copy link
Collaborator

@drbh drbh commented Feb 19, 2025

this PR allows incoming chat requests to specify tool_calls and no content, which allows tools to be passed from a tool response call into the messages of a subsequent request

for example if the following set of messages is sent without these changes an error is thrown attempting to deserialize the message missing field 'content'

[
    {"role": "user", "content": "What's the weather like in Paris today?"},
    {
        "content": "",
        "role": "assistant",
        "tool_calls": [
            {
                "id": "0",
                "function": {
                    "arguments": '{"longitude": 2.2945, "latitude": 48.8567}',
                    "name": "get_weather",
                    "description": None,
                },
                "type": "function",
            }
        ],
    },
    {"role": "tool", "tool_call_id": "0", "content": "6.7"},
]

response

I'm an AI and do not have access to real-time data. However, based on location information (Paris) I can provide general information. \n\nThe temperature in Paris varies widely throughout the year. In the summer (June to August), the average high temperature is around 23°C (73°F), while in the winter (December to February), the average low temperature is around -1°C (30°F). \n\nTo get the current weather in Paris, I recommend checking a weather website or

this PR allows messages without tools to be sent without error.

notes:

@saileshd1402
Copy link
Contributor

Thank you for this change! I'll test it out and update here

@saileshd1402
Copy link
Contributor

I've tested this change, it is working well!

@@ -1221,9 +1223,15 @@ pub struct TextMessage {

impl From<Message> for TextMessage {
fn from(value: Message) -> Self {
let content = value
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could add an additional check here to prevent this type of messages from anything other than the assistant, as well as if both content and tool_calls are provided, WDYT? Maybe even a custom new error as you suggested recently.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point! I've updated the PR to prefer a none optional enum that ensure either a content or tools are provided - looking into how errors may be improved now

("application/json" = Vec<GenerateResponse>),
("text/event-stream" = StreamResponse),
(Vec<GenerateResponse> = "application/json"),
(Vec<GenerateResponse> = "application/json"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicated here!

Suggested change
(Vec<GenerateResponse> = "application/json"),

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, avoided these issues/changes, by avoiding bumping the utopia version

Comment on lines 1184 to 1189
pub content: Option<MessageContent>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[schema(example = "\"David\"")]
name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
tool_calls: Option<Vec<ToolCall>>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we agree there can never be both a content and a tool_calls ?
Also never neither.

If true, then this begs to become an Enum of Either content or tool calling.
And that should simplify all the underlying code.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Untagged enum are not great for the error message so we should probably implement our own deserialize method instead. (Since there doesn't seem to be a type field equivalent serde could use to know which enum to deserialize)

"content": "I can't access real-time data, but I can provide you with current conditions and forecast for Paris, France:\n\nThe current conditions in Paris are mostly cloudy with a temperature of 6.7°C (44.1°F). \n\nPlease note that the actual weather may differ from this information, and I recommend checking the forecast on a reliable weather website for the most up-to-date information.",
"name": null,
"role": "assistant",
"tool_calls": null
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we skip that?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

name and tool_calls are actually skipped in the response, but the Python client library adds None when deserializing the response.

the actually response payload is

{
  "object": "chat.completion",
  "id": "",
  "created": 1740011163,
  "model": "meta-llama/Llama-3.1-8B-Instruct",
  "system_fingerprint": "3.1.1-dev0-native",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "I can't access real-time data, but I can provide you with current conditions and forecast for Paris, France:\n\nThe current conditions in Paris are mostly cloudy with a temperature of 6.7\u00b0C (44.1\u00b0F). \n\nPlease note that the actual weather may differ from this information, and I recommend checking the forecast on a reliable weather website for the most up-to-date information."
      },
      "logprobs": null,
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 103,
    "completion_tokens": 79,
    "total_tokens": 182
  }
}

@@ -16,7 +16,7 @@ path = "src/main.rs"
[dependencies]
async-trait = "0.1.74"
async-stream = "0.3.5"
axum = { version = "0.7", features = ["json"] }
axum = { version = "0.8", features = ["json"] }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need upgrades ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

only needed to generate valid openapi docs for the previous approach of Option<MessageContent> this is no longer needed with improved typing (using an enum instead of two optionals) in the latest commits

Comment on lines 483 to 497
{
"content": "",
"role": "assistant",
"tool_calls": [
{
"id": "0",
"function": {
"arguments": '{"longitude": 2.2945, "latitude": 48.8567}',
"name": "get_weather",
"description": None,
},
"type": "function",
}
],
},
{"role": "tool", "tool_call_id": "0", "content": "6.7"},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really don't get that structure.

Assistant is saying it's calling a tool, and the tool is another participant in the conversation saying it has the answer for the call, is that correct ?
How does this play with chat templates ?? Are they ready for that ?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated to include tool_call_id in the chat template as some models will expect this template input in the latest commits

@drbh drbh force-pushed the improve-tool-call-message-processing branch from db23337 to bcc4489 Compare February 20, 2025 00:40
@Narsil Narsil merged commit 1cae319 into main Feb 21, 2025
20 checks passed
@Narsil Narsil deleted the improve-tool-call-message-processing branch February 21, 2025 09:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants