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

feat!: new ChatMessage #8640

Merged
merged 15 commits into from
Dec 17, 2024
Merged

feat!: new ChatMessage #8640

merged 15 commits into from
Dec 17, 2024

Conversation

anakin87
Copy link
Member

@anakin87 anakin87 commented Dec 13, 2024

Related Issues

Proposed Changes:

How did you test it?

CI

Notes for the reviewer

I have prepared the draft docs for the new ChatMessage, including a migration guide. It might be helpful in reviewing this PR.

Checklist

  • I have read the contributors guidelines and the code of conduct
  • I have updated the related issue with new insights and changes
  • I added unit tests and updated the docstrings
  • I've used one of the conventional commit types for my PR title: fix:, feat:, build:, chore:, ci:, docs:, style:, refactor:, perf:, test: and added ! in case the PR includes breaking changes.
  • I documented my code
  • I ran pre-commit hooks and fixed any issue

@github-actions github-actions bot added topic:tests type:documentation Improvements on the docs labels Dec 13, 2024
@anakin87 anakin87 changed the title New chatmessage feat!: new ChatMessage Dec 13, 2024
@coveralls
Copy link
Collaborator

coveralls commented Dec 13, 2024

Pull Request Test Coverage Report for Build 12376304805

Details

  • 0 of 0 changed or added relevant lines in 0 files are covered.
  • 5 unchanged lines in 3 files lost coverage.
  • Overall coverage increased (+0.09%) to 90.565%

Files with Coverage Reduction New Missed Lines %
components/generators/openai_utils.py 1 83.33%
components/generators/chat/hugging_face_api.py 2 97.67%
dataclasses/chat_message.py 2 98.69%
Totals Coverage Status
Change from base Build 12353137414: 0.09%
Covered Lines: 8207
Relevant Lines: 9062

💛 - Coveralls

formatted_msg["name"] = message.name

return formatted_msg
return {"role": message.role.value, "content": message.text or ""}
Copy link
Member Author

Choose a reason for hiding this comment

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

these changes to HF API Chat Generator are only temporary: we will override them soon when porting the support for Tools

openai_msg["name"] = message.name

return openai_msg
return {"role": message.role.value, "content": message.text}
Copy link
Member Author

Choose a reason for hiding this comment

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

these changes to the OpenAI Chat Generator are only temporary: we will override them soon when porting the support for Tools

general_msg = (
"Use the `from_assistant`, `from_user`, `from_system`, and `from_tool` class methods to create a "
"ChatMessage. For more information about the new API and how to migrate, see the documentation:"
" https://docs.haystack.deepset.ai/docs/data-classes#chatmessage"
Copy link
Member Author

Choose a reason for hiding this comment

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

this link and the following ones will be updated once we have a new documentation page containing examples and the migration guide

@anakin87 anakin87 marked this pull request as ready for review December 16, 2024 17:49
@anakin87 anakin87 requested review from a team as code owners December 16, 2024 17:49
@anakin87 anakin87 requested review from dfokina, davidsbatista and vblagoje and removed request for a team and davidsbatista December 16, 2024 17:49
haystack/dataclasses/chat_message.py Show resolved Hide resolved
raise TypeError(f"Unsupported type in ChatMessage content: `{type(part).__name__}` for `{part}`.")

serialized["_content"] = content
return serialized
Copy link
Contributor

Choose a reason for hiding this comment

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

I still do not like the idea that the serialized chatmessage has underscore prefixes. May I ask what is the rationale behind it?

Copy link
Member Author

Choose a reason for hiding this comment

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

This simply stems from the new dataclass definition:

@dataclass
class ChatMessage:
    _role: ChatRole
    _content: Sequence[ChatMessageContentT]
    _meta: Dict[str, Any] = field(default_factory=dict, hash=False)

The idea is that these attributes are internal.
For creation, you should use class methods from_user, etc.
For attribute access, several properties are available: text, texts, tool_call, tool_calls, ...

Copy link
Contributor

@LastRemote LastRemote Dec 17, 2024

Choose a reason for hiding this comment

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

@anakin87 I get this part, but I still hope the serialized dictionaries can drop the underscore prefixes for easier access. For example, in a deployed chatbot pipeline, I'd prefer something like

{"chat_history": [{"role": "user", "content": [...]}, {"role": "assistant", "content": [...]}, "meta": {...}]}

as a part of the request instead of

{"chat_history": [{"_role": "user", "_content": [...]}, {"_role": "assistant", "_content": [...]}, "_meta": {...}]}

(assuming this takes an input chat_history of type List[ChatMessage]).

Also it makes migration a bit easier.

Copy link
Contributor

@LastRemote LastRemote Dec 17, 2024

Choose a reason for hiding this comment

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

Also as a bonus I have something like this in my draft PR:

        if isinstance(data["content"], str):
            content.append(TextContent(text=data["content"]))
        else:
            for part in data["content"]:
                ...  # rest of the logic

This makes the serialization form of a ChatMessage fully backwards compatible, and synergizes well with common shortcuts (in openai and anthropic APIs)

Copy link
Contributor

@LastRemote LastRemote Dec 18, 2024

Choose a reason for hiding this comment

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

@anakin87 @vblagoje wdyt about this? pinging in case this was lost in the threads.

Copy link
Member Author

Choose a reason for hiding this comment

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

@LastRemote sorry for not responding sooner.

Since this is a substantial change, we decided to make it visible and not backward compatible.

As soon as possible, we will publish a detailed migration guide + GitHub/Discord announcements. (Tracked in #8623 and #8654).

Copy link
Contributor

@LastRemote LastRemote Dec 19, 2024

Choose a reason for hiding this comment

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

@anakin87 Thanks for the reply, but I'm still not fully convinced. We can still keep the internal attributes with underscore prefixes, but just for serialization/deserialization I would suggest us dropping those for the sake of simplicity. Backward compatibility is not my top concern here.

In my current use cases, I have many deployed haystack pipelines that utilize List[ChatMessage] as a part of input/output (which is very common in chatbots to track chat history and multiple reply candidates), it makes more sense to have something like

{"chat_history": [{"role": "user", "content": [...]}, {"role": "assistant", "content": [...]}, "meta": {...}]}

instead of adding an underscore prefix in all fields. Of course, this could be mitigated by some customized logic on the server end (not sure if hayhook does this), but IMO that adds a layer of unneeded complexity.

Copy link
Member

@vblagoje vblagoje left a comment

Choose a reason for hiding this comment

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

Seems good on the first pass. Only one comment regarding ToolCallResult - I'll do another pass soon after we consolidate on that one

:param error: Whether the Tool invocation resulted in an error.
"""

result: str
Copy link
Member

Choose a reason for hiding this comment

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

A note here - I think we should have the result field be a Union of TextContent and other future media types that will include ByteStream and perhaps the upcoming MediaContent. Not sure if it is smart to add that immediately i.e have:

result: TextContent

and then make it Union as we add those new types. Or add it all in one batch sometimes soon when introduce these new types. wdyt is the best approach here?

Copy link
Member Author

Choose a reason for hiding this comment

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

I haven't studied in depth the multimodal support in different API providers, so I would prefer to introduce these changes when we know more, hopefully in a non-breaking way.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, neither did I but @mathislucka mentioned encountering this hurdle in his experiments IIRC. If it is a big change to have TextContent here then perhaps lets leave it for next release as this is admittedly already a big change. Wonder what do you think about this @LastRemote - i.e. having tools returning non str content. Have you already encountered that use case?

Copy link
Contributor

@LastRemote LastRemote Dec 17, 2024

Choose a reason for hiding this comment

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

I haven't studied in depth the multimodal support in different API providers, so I would prefer to introduce these changes when we know more, hopefully in a non-breaking way.

@anakin87 I did some research on this previously: Anthropic models can accept image (as an object containing base64 and mime type) tool result as well as text.

Oh, and it can have a list of contents.
https://docs.anthropic.com/en/api/messages#body-messages-content-content

Copy link
Contributor

Choose a reason for hiding this comment

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

having tools returning non str content. Have you already encountered that use case?

@vblagoje I am aware of this but it is not my current use case. To be honest I haven't really thought about this (I didn't change it in my multimodal PR), but this could be another Sequence[ChatMessageContentT] for maximum flexibility (it is up to the models and corresponding utility functions whether to accept certain types in tool call result)

Copy link
Member

@vblagoje vblagoje Dec 17, 2024

Choose a reason for hiding this comment

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

Yes, something like that. As this future change will likely break few specific tool integrations, and not affect many users, perhaps we can postpone it to after the integration and keep str as ToolCallResult field for now

Copy link
Contributor

Choose a reason for hiding this comment

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

...and not affect many users...

I prefer being a little careful on this one though. If a future change makes result to be a list (Anthropic already supports this), existing tool implementations may be affected.

Copy link
Member Author

Choose a reason for hiding this comment

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

In all honesty, I would prefer to keep the PR as it is regarding this aspect, especially since these changes have been extensively tested in recent months.

I am afraid that introducing a change on the fly at this time could cause some bugs, as well as complicate our lives.

For the future, unlike this PR, I think we can introduce the changes gradually (and without breaking changes), because they would be much smaller in scope.

(@vblagoje regarding the problems Mathis encountered, my impression was that they were not directly related to the tool message types, but to the Toolnvoker return types.)

Copy link
Contributor

Choose a reason for hiding this comment

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

In all honesty, I would prefer to keep the PR as it is regarding this aspect...

Sure, I am fine with it. Right now I am not seeing a lot of valid use cases for this, and there is still room for future updates.

Copy link
Member

Choose a reason for hiding this comment

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

Yes @anakin87 agreed

@anakin87 anakin87 requested a review from vblagoje December 17, 2024 11:06
@anakin87
Copy link
Member Author

@dfokina please take a look

Copy link
Contributor

@dfokina dfokina left a comment

Choose a reason for hiding this comment

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

Pushed one tiny fix, all is good ✅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
topic:tests type:documentation Improvements on the docs
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants