diff --git a/api/gateway_chat.py b/api/gateway_chat.py index e155329f..23833777 100644 --- a/api/gateway_chat.py +++ b/api/gateway_chat.py @@ -1297,7 +1297,11 @@ def _run_gateway_chat_streaming( # role/content ordering instead of turn order. assistant_ts = now + 0.000001 pending_source = getattr(s, "pending_user_source", None) or "webui" - from api.streaming import _active_turn_authority, _materialize_active_turn_user + from api.streaming import ( + _active_turn_authority, + _active_turn_token_matches, + _materialize_active_turn_user, + ) active_turn_identity = _active_turn_authority(s, stream_id, msg_text) user_msg = _materialize_active_turn_user( @@ -1373,6 +1377,14 @@ def _run_gateway_chat_streaming( if latest_text == msg_norm: display = display[:-1] s.messages = display + [user_msg, assistant_msg] + if active_turn_identity.get("token"): + current_display_rows = [ + message + for message in s.messages + if _active_turn_token_matches(message, active_turn_identity) + ] + if len(current_display_rows) == 1: + current_display_rows[0]["timestamp"] = user_msg["timestamp"] s.active_stream_id = None s.pending_user_message = None s.pending_attachments = None diff --git a/api/streaming.py b/api/streaming.py index b224dbe2..2804150a 100644 --- a/api/streaming.py +++ b/api/streaming.py @@ -2105,6 +2105,11 @@ def _active_turn_user_text_matches(message, expected_text): actual_text = content else: return False + if ( + isinstance(content, str) + and " ".join(actual_text.split()) == " ".join(expected_text.split()) + ): + return True return _submitted_user_text_matches(actual_text, expected_text) diff --git a/tests/test_batch_fixes.py b/tests/test_batch_fixes.py index e4a5a138..853979c8 100644 --- a/tests/test_batch_fixes.py +++ b/tests/test_batch_fixes.py @@ -41,12 +41,13 @@ class TestRootWorkspaceUnblocked: assert "'/etc'" in src or 'Path("/etc")' in src or "Path('/etc')" in src assert "'/proc'" in src or 'Path("/proc")' in src or "Path('/proc')" in src - def test_split_guard_present(self): - src = read("api/streaming.py") - assert "'\\n\\n[Attached files:' in msg_text" in src, ( - "base_text split must guard against missing '[Attached files:' " - "to avoid empty-string on plain messages" - ) + def test_attached_files_suffix_is_optional(self): + from api.models import _strip_attached_files_marker + + assert _strip_attached_files_marker("Plain message") == "Plain message" + assert _strip_attached_files_marker( + "Question\n\n[Attached files: photo.png]" + ) == "Question" # ── Group B: custom_providers visibility ─────────────────────────────────────