diff --git a/lib/crewai/src/crewai/new_agent/executor.py b/lib/crewai/src/crewai/new_agent/executor.py index 640d32e1b..35b6c03dd 100644 --- a/lib/crewai/src/crewai/new_agent/executor.py +++ b/lib/crewai/src/crewai/new_agent/executor.py @@ -625,22 +625,14 @@ class ConversationalAgentExecutor(BaseModel): pass return "" - _SUMMARY_EXTRACT_RE = re.compile( - r"\s*(.*?)\s*", re.DOTALL + _INTERNAL_TAG_RE = re.compile( + r".*?", re.DOTALL ) def _strip_internal_tags(self, text: str) -> str: - """Handle tags leaked from the summarization prompt. - - Some models (e.g. gpt-4.1) wrap their actual response in - tags, treating the preceding text as chain-of-thought. When summary - tags are present we extract the inner content as the real answer. - """ - match = self._SUMMARY_EXTRACT_RE.search(text) - if match: - inner = match.group(1).strip() - return inner if inner else text - return text + """Strip blocks that leak from the summarization prompt.""" + cleaned = self._INTERNAL_TAG_RE.sub("", text).strip() + return cleaned if cleaned else text def _detect_artifacts(self, tool_name: str, result_str: str) -> list[Artifact]: """GAP-67: Detect artifacts from tool results. @@ -2413,77 +2405,38 @@ class ConversationalAgentExecutor(BaseModel): invoke_task = asyncio.create_task(self.ainvoke(user_message)) _streamed_chars = 0 _last_status_time = time.monotonic() - - # States: "preflight" (buffering, no seen yet), - # "inside" ( found, yielding inner content), - # "done" ( seen, suppress the rest), - # "passthrough" (no summary tags — yield everything) - _state = "preflight" - _preflight_buf: list[str] = [] _tag_buf = "" + _suppressing = False def _filter_chunk(raw: str) -> str: - nonlocal _state, _tag_buf - out: list[str] = [] + """Suppress ... blocks from streamed chunks.""" + nonlocal _tag_buf, _suppressing + out = [] for ch in raw: - if _state == "done": - break - - if _state == "passthrough": - out.append(ch) + if _suppressing: + _tag_buf += ch + if _tag_buf.endswith(""): + _suppressing = False + _tag_buf = "" continue - - # Accumulate into tag buffer when we might be mid-tag if _tag_buf: _tag_buf += ch - - if _state == "preflight": - target = "" - if target[: len(_tag_buf)] == _tag_buf: - if _tag_buf == target: - _state = "inside" - _preflight_buf.clear() - _tag_buf = "" + if len(_tag_buf) <= len(""): + if ""[: len(_tag_buf)] == _tag_buf: + if _tag_buf == "": + _suppressing = True continue - # Not a match — dump tag_buf to preflight buffer - _preflight_buf.append(_tag_buf) - _tag_buf = "" - - elif _state == "inside": - target = "" - if target[: len(_tag_buf)] == _tag_buf: - if _tag_buf == target: - _state = "done" - _tag_buf = "" - continue - # Not a closing tag — flush buffered chars out.append(_tag_buf) _tag_buf = "" - continue - - if ch == "<": + else: + out.append(_tag_buf) + _tag_buf = "" + elif ch == "<": _tag_buf = ch - continue - - if _state == "preflight": - _preflight_buf.append(ch) - elif _state == "inside": + else: out.append(ch) - return "".join(out) - def _flush_preflight() -> str: - """If we never saw , flush buffered text.""" - nonlocal _state - if _state == "preflight": - _state = "passthrough" - result = "".join(_preflight_buf) - _preflight_buf.clear() - if _tag_buf: - result += _tag_buf - return result - return "" - try: while not invoke_task.done(): try: @@ -2507,10 +2460,8 @@ class ConversationalAgentExecutor(BaseModel): _streamed_chars += len(filtered) yield filtered - leftover = _flush_preflight() - if leftover: - _streamed_chars += len(leftover) - yield leftover + if _tag_buf and not _suppressing: + yield _tag_buf result = invoke_task.result() self._last_stream_result = result