diff --git a/panel/chat/feed.py b/panel/chat/feed.py index da77313ef4..177d99788c 100644 --- a/panel/chat/feed.py +++ b/panel/chat/feed.py @@ -723,6 +723,7 @@ def add_step( steps_layout: Column | Card | None = None, default_layout: Literal["column", "card"] = "card", layout_params: dict | None = None, + last_messages: int = 1, **step_params ) -> ChatStep: """ @@ -750,6 +751,8 @@ def add_step( 'card' will create a new Card layout. layout_params : dict | None Additional parameters to pass to the layout. + last_messages: int + The number of messages to go back to find the last message. step_params : dict Parameters to pass to the ChatStep. """ @@ -771,13 +774,19 @@ def add_step( if "context_exception" not in step_params: step_params["context_exception"] = self.callback_exception step = ChatStep(**step_params) + if append: - last = self._chat_log[-1] if self._chat_log else None - if last is not None and isinstance(last.object, Column) and ( + for i in range(1, last_messages + 1): + if not self._chat_log: + break + + last = self._chat_log[-i] + if last is not None and isinstance(last.object, Column) and ( all(isinstance(o, ChatStep) for o in last.object) or last.object.css_classes == 'chat-steps' - ) and (user is None or last.user == user): - steps_layout = last.object + ) and (user is None or last.user == user): + steps_layout = last.object + if steps_layout is None: layout_params = layout_params or {} input_layout_params = dict( diff --git a/panel/tests/chat/test_feed.py b/panel/tests/chat/test_feed.py index 64aa7304d4..65a3c381b9 100644 --- a/panel/tests/chat/test_feed.py +++ b/panel/tests/chat/test_feed.py @@ -304,6 +304,34 @@ def test_add_step_inherits_callback_exception(self, chat_feed): raise ValueError("Testing") assert "Traceback" in step.objects[0].object + def test_add_step_last_messages(self, chat_feed): + # create steps + with chat_feed.add_step("Object 1", title="Step 1"): + assert len(chat_feed) == 1 + + # add a message in the middle + chat_feed.send("Message 1") + assert len(chat_feed) == 2 + + # last_messages=2 should make it connect with the last steps object + with chat_feed.add_step("Object 2", title="Step 2", last_messages=2): + assert len(chat_feed) == 2 + message = chat_feed[0] + steps = message.object + assert len(steps) == 2 + assert steps[0].objects[0].object == "Object 1" + assert steps[1].objects[0].object == "Object 2" + + # add another message + chat_feed.send("Message 2") + assert len(chat_feed) == 3 + + # this should now not join with the last steps object because the steps is in the first message + with chat_feed.add_step("Object 3", title="Step 3", last_messages=2): + assert len(chat_feed) == 4 + steps = chat_feed[-1].object + assert len(steps) == 1 + def test_stream_with_user_avatar(self, chat_feed): user = "Bob" avatar = "👨"