Skip to content

feat: Add product widget response#210

Open
dsowinski2 wants to merge 1 commit into
mainfrom
feat/ent-121-make-the-product-search-agent-capable-of-generating-widget
Open

feat: Add product widget response#210
dsowinski2 wants to merge 1 commit into
mainfrom
feat/ent-121-make-the-product-search-agent-capable-of-generating-widget

Conversation

@dsowinski2
Copy link
Copy Markdown
Contributor

@dsowinski2 dsowinski2 commented Oct 27, 2025

This PR adds possibility to render a product widget.

  1. Adds a direct return tool which can handle returning product widget response.
  2. Adds new agent callback handler class responsible for streaming widgets
  3. Adds callback handler to injector so tool can stream
  4. Handle messages and stream on Chat UI
  5. Adds new message type (widget)
  6. Add new field on Message model (widget_data)
  7. Saves widget response to conversation history
Screenshot 2026-01-27 at 15 46 08

@dsowinski2 dsowinski2 self-assigned this Oct 27, 2025
@dsowinski2 dsowinski2 marked this pull request as draft October 27, 2025 11:03
@dsowinski2 dsowinski2 force-pushed the feat/ent-121-make-the-product-search-agent-capable-of-generating-widget branch 2 times, most recently from 2e3f74d to 64d97bd Compare October 27, 2025 12:00
@dsowinski2 dsowinski2 marked this pull request as ready for review October 27, 2025 12:01
self.MessageType.INTERMEDIATE_STEP: "ai",
self.MessageType.AI: "ai",
self.MessageType.FUNCTION: "ai",
self.MessageType.WIDGET: "ai",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@dsowinski2 I'm missing some context when it comes to how this is supposed to work. Could you add a description to this PR that describes the overall concept behind widget responses, how they're generated and managed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I've updated description and resolved conflicts, let me know if you need more details.

@dsowinski2 dsowinski2 force-pushed the feat/ent-121-make-the-product-search-agent-capable-of-generating-widget branch from 4f031a5 to 4934ea9 Compare January 27, 2026 14:33
@dsowinski2 dsowinski2 force-pushed the feat/ent-121-make-the-product-search-agent-capable-of-generating-widget branch from 14841e5 to bcef640 Compare January 27, 2026 14:43
@dsowinski2 dsowinski2 requested a review from mateus-po February 10, 2026 06:57
Copy link
Copy Markdown
Contributor

@mateus-po mateus-po left a comment

Choose a reason for hiding this comment

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

Two minor suggestions to the PresentProductTool implementation, otherwise LGTM

)
all_products = []
for id in ids:
product = self._injector.repositories.product.get_by_id(int(id))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Maybe it would be a good idea to prefetch all products first? By implementing a get_by_ids(ids: list[str]) method on a product registry that would fetch all products in one DB call. Not a big issue, but would speed up the tool + would reduce the number of DB calls.

Comment on lines +28 to +31
if self._streaming:
self._injector.callbacks_handler.on_product_widget_start(
json.dumps({"number_of_products": len(ids), "message": message_to_user})
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I am not a big fan of these 3 if self._streaming: blocks in the run method. I would do it like this:

  1. Define a private method like:
def _handle_streaming(step: 'start' | 'in_progress' | 'end') -> void:
    if not self._streaming: return
    # remaining logic based on the streaming step
  1. use it inside of the run method

It would make the code inside of the run method more readible and the whole tool class more modular.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Or more something like this (so that you can pass the streaming message data):

def _handle_streaming(step: 'start' | 'in_progress' | 'end', data: Optional[str]) -> void:
    if not self._streaming: return
    # remaining logic based on the streaming step

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.

3 participants