This commit is contained in:
0xhis 2026-04-22 16:11:14 -04:00 committed by GitHub
commit 12597e1739
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 84 additions and 9 deletions

View file

@ -334,6 +334,13 @@ class BaseAgent(metaclass=AgentMeta):
sandbox_mode = os.getenv("STRIX_SANDBOX_MODE", "false").lower() == "true"
if not sandbox_mode and self.state.sandbox_id is None:
from strix.runtime import get_runtime
from strix.telemetry.tracer import get_global_tracer
tracer = get_global_tracer()
if tracer:
tracer.update_agent_system_message(
self.state.agent_id, "Setting up sandbox environment..."
)
try:
runtime = get_runtime()
@ -368,6 +375,9 @@ class BaseAgent(metaclass=AgentMeta):
async def _process_iteration(self, tracer: Optional["Tracer"]) -> bool | None:
final_response = None
if tracer:
tracer.update_agent_system_message(self.state.agent_id, "Thinking...")
async for response in self.llm.generate(self.state.get_conversation_history()):
final_response = response
if tracer and response.content:
@ -409,8 +419,19 @@ class BaseAgent(metaclass=AgentMeta):
)
if actions:
if tracer:
tool_names = [a.get("toolName") or a.get("tool_name") or "tool" for a in actions]
display_names = tool_names[:2]
overflow = len(tool_names) - 2
suffix = f" +{overflow} more" if overflow > 0 else ""
tracer.update_agent_system_message(
self.state.agent_id, f"Executing {', '.join(display_names)}{suffix}..."
)
return await self._execute_actions(actions, tracer)
if tracer:
tracer.update_agent_system_message(self.state.agent_id, "Processing response...")
return None
async def _execute_actions(self, actions: list[Any], tracer: Optional["Tracer"]) -> bool:

View file

@ -23,7 +23,8 @@ class ThinkRenderer(BaseToolRenderer):
text.append("\n ")
if thought:
text.append(thought, style="italic dim")
indented_thought = "\n ".join(thought.split("\n"))
text.append(indented_thought, style="italic dim")
else:
text.append("Thinking...", style="italic dim")

View file

@ -1243,14 +1243,19 @@ class StrixTUIApp(App): # type: ignore[misc]
return (Text(" "), keymap, False)
if status == "running":
sys_msg = agent_data.get("system_message", "")
if self._agent_has_real_activity(agent_id):
animated_text = Text()
animated_text.append_text(self._get_sweep_animation(self._sweep_colors))
if sys_msg:
animated_text.append(sys_msg, style="dim italic")
animated_text.append(" ", style="dim")
animated_text.append("esc", style="white")
animated_text.append(" ", style="dim")
animated_text.append("stop", style="dim")
return (animated_text, keymap_styled([("ctrl-q", "quit")]), True)
animated_text = self._get_animated_verb_text(agent_id, "Initializing")
msg = sys_msg or "Initializing..."
animated_text = self._get_animated_verb_text(agent_id, msg)
return (animated_text, keymap_styled([("ctrl-q", "quit")]), True)
return (None, Text(), False)
@ -1683,21 +1688,46 @@ class StrixTUIApp(App): # type: ignore[misc]
content = msg_data.get("content", "")
metadata = msg_data.get("metadata", {})
if not content:
return None
if role == "user":
if not content:
return None
return UserMessageRenderer.render_simple(content)
renderables = []
if "thinking_blocks" in metadata and metadata["thinking_blocks"]:
from strix.interface.tool_components.thinking_renderer import ThinkRenderer
for block in metadata["thinking_blocks"]:
thought = block.get("thinking", "")
if thought:
renderables.append(
ThinkRenderer.render({"args": {"thought": thought}}).renderable
)
if not content and not renderables:
return None
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 self._merge_renderables([*renderables, streaming_result, interrupted_text])
return AgentMessageRenderer.render_simple(content)
if content:
msg_renderable = AgentMessageRenderer.render_simple(content)
renderables.append(msg_renderable)
if not renderables:
return None
if len(renderables) == 1:
r = renderables[0]
return self._sanitize_text(r) if isinstance(r, Text) else r
return self._merge_renderables(renderables)
def _render_tool_content_simple(self, tool_data: dict[str, Any]) -> Any:
tool_name = tool_data.get("tool_name", "Unknown Tool")

View file

@ -156,12 +156,21 @@ class LLM:
async def generate(
self, conversation_history: list[dict[str, Any]]
) -> AsyncIterator[LLMResponse]:
from strix.telemetry.tracer import get_global_tracer
tracer = get_global_tracer()
if tracer and self.agent_id:
tracer.update_agent_system_message(self.agent_id, "Compressing memory...")
messages = self._prepare_messages(conversation_history)
max_retries = int(Config.get("strix_llm_max_retries") or "5")
for attempt in range(max_retries + 1):
try:
async for response in self._stream(messages):
if tracer and self.agent_id:
tracer.update_agent_system_message(self.agent_id, "Waiting for LLM provider...")
async for response in self._stream(messages, tracer):
yield response
return # noqa: TRY300
except Exception as e: # noqa: BLE001
@ -170,15 +179,23 @@ class LLM:
wait = min(90, 2 * (2**attempt))
await asyncio.sleep(wait)
async def _stream(self, messages: list[dict[str, Any]]) -> AsyncIterator[LLMResponse]:
async def _stream(
self, messages: list[dict[str, Any]], tracer: Any = None
) -> AsyncIterator[LLMResponse]:
accumulated = ""
chunks: list[Any] = []
done_streaming = 0
first_chunk_received = False
self._total_stats.requests += 1
response = await acompletion(**self._build_completion_args(messages), stream=True)
async for chunk in response:
if not first_chunk_received:
first_chunk_received = True
if tracer and self.agent_id:
tracer.update_agent_system_message(self.agent_id, "Generating response...")
chunks.append(chunk)
if done_streaming:
done_streaming += 1

View file

@ -439,6 +439,7 @@ class Tracer:
"name": name,
"task": task,
"status": "running",
"system_message": "",
"parent_id": parent_id,
"created_at": datetime.now(UTC).isoformat(),
"updated_at": datetime.now(UTC).isoformat(),
@ -587,6 +588,11 @@ class Tracer:
source="strix.agents",
)
def update_agent_system_message(self, agent_id: str, message: str) -> None:
if agent_id in self.agents:
self.agents[agent_id]["system_message"] = message
self.agents[agent_id]["updated_at"] = datetime.now(UTC).isoformat()
def set_scan_config(self, config: dict[str, Any]) -> None:
self.scan_config = config
self.run_metadata.update(