mirror of
https://github.com/usestrix/strix.git
synced 2026-08-19 01:55:46 +02:00
refactor: delete orphaned dirs, dead streaming infra, unused session/compressor
Orphaned files/dirs: - ``strix/agents/StrixAgent/`` — empty, only ``__pycache__``. - ``strix/tools/browser/litellm/`` — empty, only ``__pycache__``. - ``strix/strix_runs/`` — runtime output left in the working tree. - ``strix/prompts/`` — single Jinja template that nothing renders. Dead streaming pipeline (was never wired in the SDK migration): - Delete ``strix/interface/streaming_parser.py`` (XML tool-call parser for an output format the SDK doesn't produce). - Strip ``streaming_content`` / ``interrupted_content`` dicts and five unused methods from ``Tracer``. - Strip the streaming-render path + ``interrupted`` branch from TUI. - Trim ``strix/llm/utils.py``: drop ``normalize_tool_format``, ``parse_tool_invocations``, ``format_tool_call``, ``fix_incomplete_tool_call`` and the XML-stripping in ``clean_content``. Keep only the inter-agent-XML scrub. Unwired session compression: - Delete ``strix/llm/strix_session.py`` and ``strix/llm/memory_compressor.py``. ``Runner.run`` was never called with a ``session=``, so the compressor never ran. Drop the matching test file and the ``strix_memory_compressor_timeout`` config knob. Tracer cleanup: - Remove ``log_agent_creation``, ``log_tool_execution_start``, ``update_tool_execution``, ``update_agent_status``, ``get_agent_tools`` — none had production callers. - Rewrite the redaction + correlation tests against ``log_chat_message`` (which still emits events). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
4146174503
commit
369fa56148
@@ -1,125 +0,0 @@
|
||||
import html
|
||||
import re
|
||||
from dataclasses import dataclass
|
||||
from typing import Literal
|
||||
|
||||
from strix.llm.utils import normalize_tool_format
|
||||
|
||||
|
||||
_FUNCTION_TAG_PREFIX = "<function="
|
||||
_INVOKE_TAG_PREFIX = "<invoke "
|
||||
|
||||
_FUNC_PATTERN = re.compile(r"<function=([^>]+)>")
|
||||
_FUNC_END_PATTERN = re.compile(r"</function>")
|
||||
_COMPLETE_PARAM_PATTERN = re.compile(r"<parameter=([^>]+)>(.*?)</parameter>", re.DOTALL)
|
||||
_INCOMPLETE_PARAM_PATTERN = re.compile(r"<parameter=([^>]+)>(.*)$", re.DOTALL)
|
||||
|
||||
|
||||
def _get_safe_content(content: str) -> tuple[str, str]:
|
||||
if not content:
|
||||
return "", ""
|
||||
|
||||
last_lt = content.rfind("<")
|
||||
if last_lt == -1:
|
||||
return content, ""
|
||||
|
||||
suffix = content[last_lt:]
|
||||
|
||||
if _FUNCTION_TAG_PREFIX.startswith(suffix) or _INVOKE_TAG_PREFIX.startswith(suffix):
|
||||
return content[:last_lt], suffix
|
||||
|
||||
return content, ""
|
||||
|
||||
|
||||
@dataclass
|
||||
class StreamSegment:
|
||||
type: Literal["text", "tool"]
|
||||
content: str
|
||||
tool_name: str | None = None
|
||||
args: dict[str, str] | None = None
|
||||
is_complete: bool = False
|
||||
|
||||
|
||||
def parse_streaming_content(content: str) -> list[StreamSegment]:
|
||||
if not content:
|
||||
return []
|
||||
|
||||
content = normalize_tool_format(content)
|
||||
|
||||
segments: list[StreamSegment] = []
|
||||
|
||||
func_matches = list(_FUNC_PATTERN.finditer(content))
|
||||
|
||||
if not func_matches:
|
||||
safe_content, _ = _get_safe_content(content)
|
||||
text = safe_content.strip()
|
||||
if text:
|
||||
segments.append(StreamSegment(type="text", content=text))
|
||||
return segments
|
||||
|
||||
first_func_start = func_matches[0].start()
|
||||
if first_func_start > 0:
|
||||
text_before = content[:first_func_start].strip()
|
||||
if text_before:
|
||||
segments.append(StreamSegment(type="text", content=text_before))
|
||||
|
||||
for i, match in enumerate(func_matches):
|
||||
tool_name = match.group(1)
|
||||
func_start = match.end()
|
||||
|
||||
func_end_match = _FUNC_END_PATTERN.search(content, func_start)
|
||||
|
||||
if func_end_match:
|
||||
func_body = content[func_start : func_end_match.start()]
|
||||
is_complete = True
|
||||
end_pos = func_end_match.end()
|
||||
else:
|
||||
if i + 1 < len(func_matches):
|
||||
next_func_start = func_matches[i + 1].start()
|
||||
func_body = content[func_start:next_func_start]
|
||||
else:
|
||||
func_body = content[func_start:]
|
||||
is_complete = False
|
||||
end_pos = len(content)
|
||||
|
||||
args = _parse_streaming_params(func_body)
|
||||
|
||||
segments.append(
|
||||
StreamSegment(
|
||||
type="tool",
|
||||
content=func_body,
|
||||
tool_name=tool_name,
|
||||
args=args,
|
||||
is_complete=is_complete,
|
||||
)
|
||||
)
|
||||
|
||||
if is_complete and i + 1 < len(func_matches):
|
||||
next_start = func_matches[i + 1].start()
|
||||
text_between = content[end_pos:next_start].strip()
|
||||
if text_between:
|
||||
segments.append(StreamSegment(type="text", content=text_between))
|
||||
|
||||
return segments
|
||||
|
||||
|
||||
def _parse_streaming_params(func_body: str) -> dict[str, str]:
|
||||
args: dict[str, str] = {}
|
||||
|
||||
complete_matches = list(_COMPLETE_PARAM_PATTERN.finditer(func_body))
|
||||
complete_end_pos = 0
|
||||
|
||||
for match in complete_matches:
|
||||
param_name = match.group(1)
|
||||
param_value = html.unescape(match.group(2).strip())
|
||||
args[param_name] = param_value
|
||||
complete_end_pos = max(complete_end_pos, match.end())
|
||||
|
||||
remaining = func_body[complete_end_pos:]
|
||||
incomplete_match = _INCOMPLETE_PARAM_PATTERN.search(remaining)
|
||||
if incomplete_match:
|
||||
param_name = incomplete_match.group(1)
|
||||
param_value = html.unescape(incomplete_match.group(2).strip())
|
||||
args[param_name] = param_value
|
||||
|
||||
return args
|
||||
+4
-126
@@ -33,7 +33,6 @@ from textual.widgets.tree import TreeNode
|
||||
|
||||
from strix.config import Config
|
||||
from strix.entry import run_strix_scan
|
||||
from strix.interface.streaming_parser import parse_streaming_content
|
||||
from strix.interface.tool_components.agent_message_renderer import AgentMessageRenderer
|
||||
from strix.interface.tool_components.registry import get_tool_renderer
|
||||
from strix.interface.tool_components.user_message_renderer import UserMessageRenderer
|
||||
@@ -723,9 +722,6 @@ class StrixTUIApp(App): # type: ignore[misc]
|
||||
self._displayed_agents: set[str] = set()
|
||||
self._displayed_events: list[str] = []
|
||||
|
||||
self._streaming_render_cache: dict[str, tuple[int, Any]] = {}
|
||||
self._last_streaming_len: dict[str, int] = {}
|
||||
|
||||
self._scan_thread: threading.Thread | None = None
|
||||
self._scan_loop: asyncio.AbstractEventLoop | None = None
|
||||
self._scan_stop_event = threading.Event()
|
||||
@@ -979,25 +975,17 @@ class StrixTUIApp(App): # type: ignore[misc]
|
||||
)
|
||||
|
||||
events = self._gather_agent_events(self.selected_agent_id)
|
||||
streaming = self.tracer.get_streaming_content(self.selected_agent_id)
|
||||
|
||||
if not events and not streaming:
|
||||
if not events:
|
||||
return self._get_chat_placeholder_content(
|
||||
"Starting agent...", "placeholder-no-activity"
|
||||
)
|
||||
|
||||
current_event_ids = [e["id"] for e in events]
|
||||
current_streaming_len = len(streaming) if streaming else 0
|
||||
last_streaming_len = self._last_streaming_len.get(self.selected_agent_id, 0)
|
||||
|
||||
if (
|
||||
current_event_ids == self._displayed_events
|
||||
and current_streaming_len == last_streaming_len
|
||||
):
|
||||
if current_event_ids == self._displayed_events:
|
||||
return None, None
|
||||
|
||||
self._displayed_events = current_event_ids
|
||||
self._last_streaming_len[self.selected_agent_id] = current_streaming_len
|
||||
return self._get_rendered_events_content(events), "chat-content"
|
||||
|
||||
def _update_chat_view(self) -> None:
|
||||
@@ -1106,15 +1094,6 @@ class StrixTUIApp(App): # type: ignore[misc]
|
||||
renderables.append(Text(""))
|
||||
renderables.append(content)
|
||||
|
||||
if self.selected_agent_id:
|
||||
streaming = self.tracer.get_streaming_content(self.selected_agent_id)
|
||||
if streaming:
|
||||
streaming_text = self._render_streaming_content(streaming)
|
||||
if streaming_text:
|
||||
if renderables:
|
||||
renderables.append(Text(""))
|
||||
renderables.append(streaming_text)
|
||||
|
||||
if not renderables:
|
||||
return Text()
|
||||
|
||||
@@ -1123,85 +1102,6 @@ class StrixTUIApp(App): # type: ignore[misc]
|
||||
|
||||
return self._merge_renderables(renderables)
|
||||
|
||||
def _render_streaming_content(self, content: str, agent_id: str | None = None) -> Any:
|
||||
cache_key = agent_id or self.selected_agent_id or ""
|
||||
content_len = len(content)
|
||||
|
||||
if cache_key in self._streaming_render_cache:
|
||||
cached_len, cached_output = self._streaming_render_cache[cache_key]
|
||||
if cached_len == content_len:
|
||||
return cached_output
|
||||
|
||||
renderables: list[Any] = []
|
||||
segments = parse_streaming_content(content)
|
||||
|
||||
for segment in segments:
|
||||
if segment.type == "text":
|
||||
text_content = AgentMessageRenderer.render_simple(segment.content)
|
||||
if renderables:
|
||||
renderables.append(Text(""))
|
||||
renderables.append(text_content)
|
||||
|
||||
elif segment.type == "tool":
|
||||
tool_renderable = self._render_streaming_tool(
|
||||
segment.tool_name or "unknown",
|
||||
segment.args or {},
|
||||
segment.is_complete,
|
||||
)
|
||||
if renderables:
|
||||
renderables.append(Text(""))
|
||||
renderables.append(tool_renderable)
|
||||
|
||||
if not renderables:
|
||||
result = Text()
|
||||
elif len(renderables) == 1 and isinstance(renderables[0], Text):
|
||||
result = self._sanitize_text(renderables[0])
|
||||
else:
|
||||
result = self._merge_renderables(renderables)
|
||||
|
||||
self._streaming_render_cache[cache_key] = (content_len, result)
|
||||
return result
|
||||
|
||||
def _render_streaming_tool(
|
||||
self, tool_name: str, args: dict[str, str], is_complete: bool
|
||||
) -> Any:
|
||||
tool_data = {
|
||||
"tool_name": tool_name,
|
||||
"args": args,
|
||||
"status": "completed" if is_complete else "running",
|
||||
"result": None,
|
||||
}
|
||||
|
||||
renderer = get_tool_renderer(tool_name)
|
||||
if renderer:
|
||||
widget = renderer.render(tool_data)
|
||||
return widget.content
|
||||
|
||||
return self._render_default_streaming_tool(tool_name, args, is_complete)
|
||||
|
||||
def _render_default_streaming_tool(
|
||||
self, tool_name: str, args: dict[str, str], is_complete: bool
|
||||
) -> Text:
|
||||
text = Text()
|
||||
|
||||
if is_complete:
|
||||
text.append("✓ ", style="green")
|
||||
else:
|
||||
text.append("● ", style="yellow")
|
||||
|
||||
text.append("Using tool ", style="dim")
|
||||
text.append(tool_name, style="bold blue")
|
||||
|
||||
if args:
|
||||
for key, value in list(args.items())[:3]:
|
||||
text.append("\n ")
|
||||
text.append(key, style="dim")
|
||||
text.append(": ")
|
||||
display_value = value if len(value) <= 100 else value[:97] + "..."
|
||||
text.append(display_value, style="italic" if not is_complete else None)
|
||||
|
||||
return text
|
||||
|
||||
def _get_status_display_content(
|
||||
self, agent_id: str, agent_data: dict[str, Any]
|
||||
) -> tuple[Text | None, Text, bool]:
|
||||
@@ -1442,8 +1342,7 @@ class StrixTUIApp(App): # type: ignore[misc]
|
||||
if tool_name not in initial_tools:
|
||||
return True
|
||||
|
||||
streaming = self.tracer.get_streaming_content(agent_id)
|
||||
return bool(streaming and streaming.strip())
|
||||
return False
|
||||
|
||||
def _agent_vulnerability_count(self, agent_id: str) -> int:
|
||||
count = 0
|
||||
@@ -1493,8 +1392,6 @@ class StrixTUIApp(App): # type: ignore[misc]
|
||||
return
|
||||
|
||||
self._displayed_events.clear()
|
||||
self._streaming_render_cache.clear()
|
||||
self._last_streaming_len.clear()
|
||||
|
||||
self.call_later(self._update_chat_view)
|
||||
self._update_agent_status_display()
|
||||
@@ -1710,17 +1607,10 @@ class StrixTUIApp(App): # type: ignore[misc]
|
||||
if not content:
|
||||
return None
|
||||
|
||||
del metadata
|
||||
if role == "user":
|
||||
return UserMessageRenderer.render_simple(content)
|
||||
|
||||
if metadata.get("interrupted"):
|
||||
streaming_result = self._render_streaming_content(content)
|
||||
interrupted_text = Text()
|
||||
interrupted_text.append("\n")
|
||||
interrupted_text.append("⚠ ", style="yellow")
|
||||
interrupted_text.append("Interrupted by user", style="yellow dim")
|
||||
return self._merge_renderables([streaming_result, interrupted_text])
|
||||
|
||||
return AgentMessageRenderer.render_simple(content)
|
||||
|
||||
def _render_tool_content_simple(self, tool_data: dict[str, Any]) -> Any:
|
||||
@@ -1828,18 +1718,6 @@ class StrixTUIApp(App): # type: ignore[misc]
|
||||
if not self.selected_agent_id:
|
||||
return
|
||||
|
||||
if self.tracer:
|
||||
streaming_content = self.tracer.get_streaming_content(self.selected_agent_id)
|
||||
if streaming_content and streaming_content.strip():
|
||||
self.tracer.clear_streaming_content(self.selected_agent_id)
|
||||
self.tracer.interrupted_content[self.selected_agent_id] = streaming_content
|
||||
self.tracer.log_chat_message(
|
||||
content=streaming_content,
|
||||
role="assistant",
|
||||
agent_id=self.selected_agent_id,
|
||||
metadata={"interrupted": True},
|
||||
)
|
||||
|
||||
if self.tracer:
|
||||
self.tracer.log_chat_message(
|
||||
content=message,
|
||||
|
||||
Reference in New Issue
Block a user