-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
Chat Generatol Protocol POC #9037
Conversation
@@ -2,57 +2,33 @@ | |||
# | |||
# SPDX-License-Identifier: Apache-2.0 | |||
|
|||
import sys |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
slightly related: after intense debugging, I found that the current haystack/__init__.py
, which mixes eager and lazy imports, somehow overshadows the static type of component
. This means that we would not able to check Protocol adherence of run
methods decorated with @component.output_types
.
For this reason, I decided to go back to eager imports in this module.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this a bug when using all protocols based on components? If so it could be good to bring this change in a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. I will do this in a different PR.
BTW, this PR is just a POC and not meant to be merged.
@@ -13,50 +13,15 @@ | |||
|
|||
from haystack import Document, component, default_from_dict, default_to_dict, logging | |||
from haystack.components.builders import PromptBuilder | |||
from haystack.components.generators.chat import AzureOpenAIChatGenerator, OpenAIChatGenerator |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this module, you can find how we could simplify the LLMMetadataExtractor
.
This is just an example, and we should respect our breaking changes policy, first introducing chat_generator
(in 2.x.0 release) and then removing generator_api
and generator_api_params
(in 2.x+1.0 release).
""" | ||
... | ||
|
||
def run(self, messages: List[ChatMessage]) -> Dict[str, Any]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is compatible with more rich run
methods.
For example def run (self, messages: List[ChatMessage], param2="default", param3="another_default")
Creating a minimal Protocol ensures that our Chat Generators are already compatible.
@@ -442,7 +447,7 @@ def run(self, value: int): | |||
instance, {name: OutputSocket(name=name, type=type_) for name, type_ in types.items()}, OutputSocket | |||
) | |||
|
|||
def output_types(self, **types): | |||
def output_types(self, **types: Any) -> Callable[[Callable[P, R]], Callable[P, R]]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to be able to check the adherence of run
method to the Protocol, we need to add type hints to the decorator and the decorator factory.
@@ -0,0 +1,58 @@ | |||
# SPDX-FileCopyrightText: 2022-present deepset GmbH <info@deepset.ai> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just a simple file showing the Protocol in action
@anakin87 this looks really cool and looks good to me! |
Also after rereading the thread on the original issue I believe this protocol is helpful for the static type checking as well as visually as a Haystack user I see that the type hint is ChatGenerator rather than just Component. Although I still do think that we should add runtime checking where needed (as you originally mention @anakin87). |
Pull Request Test Coverage Report for Build 13855199401Warning: This coverage report may be inaccurate.This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.
Details
💛 - Coveralls |
PoC looks good to me! 👍 The examples in there are very helpful and I tried |
As discussed in deepset-ai/haystack-experimental#218, I propose introducing a
ChatGenerator
Protocol to qualifyChatGenerator
components from a static type-checking perspective.The current PR outlines a POC demonstrating how this could work and the benefits it provides.
Why
Chat Generators are central components in Haystack. They are used directly and often incorporated into more complex components like
LLMMetadataExtractor
,Agent
, andSummarizer
. EvenLLMEvaluator
could fit, though it currently uses a Generator.Using them inside other components has been handled in various ways. For example, see the
LLMMetadataExtractor
implementation. This often results in several lazy imports, support for limited set of Chat Generators, inconsistent user experience.Introducing a shallow abstraction to qualify Chat Generators would improve flexibility, reduce code duplication and standardize the integration in other components.
Pros
No Runtime Checking
While the Protocol ensures static type safety, it does not provide strong runtime guarantees.
@runtime_checkable
, runtime checks would only verify method existence—not their signatures (see mypy docs).@runtime_checkable
entirely and handling runtime validation where needed.Protocol vs Abstract Class
An abstract class would offer stronger guarantees both statically and dynamically, but at the cost of flexibility.
A Protocol, on the other hand, requires little to no changes to our codebase.
For more details on the implementation, look at comments in this PR.