mirror of
https://github.com/usestrix/strix.git
synced 2026-09-26 01:11:25 +00:00
Merge 7259ffebec into 97f577a610
This commit is contained in:
commit
00550204b1
4 changed files with 76 additions and 12 deletions
|
|
@ -629,6 +629,7 @@ def main() -> None: # noqa: PLR0912, PLR0915
|
|||
finally:
|
||||
tracer = get_global_tracer()
|
||||
if tracer:
|
||||
tracer.cleanup()
|
||||
posthog.end(tracer, exit_reason=exit_reason)
|
||||
|
||||
results_path = Path("strix_runs") / args.run_name
|
||||
|
|
|
|||
|
|
@ -263,7 +263,9 @@ class LLM:
|
|||
conversation_history.extend(compressed)
|
||||
messages.extend(compressed)
|
||||
|
||||
if messages[-1].get("role") == "assistant" and not self.config.interactive:
|
||||
if messages[-1].get("role") == "assistant" and (
|
||||
not self.config.interactive or self._is_anthropic()
|
||||
):
|
||||
messages.append({"role": "user", "content": "<meta>Continue the task.</meta>"})
|
||||
|
||||
if self._is_anthropic() and self.config.enable_prompt_caching:
|
||||
|
|
|
|||
|
|
@ -99,19 +99,30 @@ def finish_scan(
|
|||
if active_agents_error:
|
||||
return active_agents_error
|
||||
|
||||
validation_errors = []
|
||||
_NOT_PROVIDED = "[Not provided by model]"
|
||||
placeholder_fields = []
|
||||
if not (executive_summary or "").strip():
|
||||
placeholder_fields.append("executive_summary")
|
||||
if not (methodology or "").strip():
|
||||
placeholder_fields.append("methodology")
|
||||
if not (technical_analysis or "").strip():
|
||||
placeholder_fields.append("technical_analysis")
|
||||
if not (recommendations or "").strip():
|
||||
placeholder_fields.append("recommendations")
|
||||
|
||||
if not executive_summary or not executive_summary.strip():
|
||||
validation_errors.append("Executive summary cannot be empty")
|
||||
if not methodology or not methodology.strip():
|
||||
validation_errors.append("Methodology cannot be empty")
|
||||
if not technical_analysis or not technical_analysis.strip():
|
||||
validation_errors.append("Technical analysis cannot be empty")
|
||||
if not recommendations or not recommendations.strip():
|
||||
validation_errors.append("Recommendations cannot be empty")
|
||||
executive_summary = (executive_summary or "").strip() or _NOT_PROVIDED
|
||||
methodology = (methodology or "").strip() or _NOT_PROVIDED
|
||||
technical_analysis = (technical_analysis or "").strip() or _NOT_PROVIDED
|
||||
recommendations = (recommendations or "").strip() or _NOT_PROVIDED
|
||||
|
||||
if validation_errors:
|
||||
return {"success": False, "message": "Validation failed", "errors": validation_errors}
|
||||
if placeholder_fields:
|
||||
import logging
|
||||
|
||||
logging.warning(
|
||||
"finish_scan: model omitted required field(s) %s; "
|
||||
"saving partial report with placeholder text",
|
||||
placeholder_fields,
|
||||
)
|
||||
|
||||
try:
|
||||
from strix.telemetry.tracer import get_global_tracer
|
||||
|
|
|
|||
50
tests/llm/test_prepare_messages.py
Normal file
50
tests/llm/test_prepare_messages.py
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
"""Tests for LLM._prepare_messages trailing-assistant-message handling."""
|
||||
from strix.llm.config import LLMConfig
|
||||
from strix.llm.llm import LLM
|
||||
|
||||
|
||||
def _make_llm(monkeypatch, model_name: str, interactive: bool) -> LLM:
|
||||
monkeypatch.setenv("STRIX_LLM", model_name)
|
||||
config = LLMConfig(model_name=model_name, interactive=interactive, enable_prompt_caching=False)
|
||||
return LLM(config, agent_name=None)
|
||||
|
||||
|
||||
def _history_ending_with_assistant() -> list[dict]:
|
||||
return [
|
||||
{"role": "user", "content": "Scan this target."},
|
||||
{"role": "assistant", "content": "I found a vulnerability."},
|
||||
]
|
||||
|
||||
|
||||
def test_non_interactive_anthropic_adds_user_message(monkeypatch) -> None:
|
||||
"""Non-interactive mode always appends a user message when history ends with assistant."""
|
||||
llm = _make_llm(monkeypatch, "claude-sonnet-4-6", interactive=False)
|
||||
history = _history_ending_with_assistant()
|
||||
messages = llm._prepare_messages(history)
|
||||
assert messages[-1]["role"] == "user"
|
||||
assert messages[-1]["content"] == "<meta>Continue the task.</meta>"
|
||||
|
||||
|
||||
def test_interactive_anthropic_adds_user_message(monkeypatch) -> None:
|
||||
"""Interactive mode with Anthropic model must also append a user message.
|
||||
|
||||
Anthropic API rejects messages where the last entry has role 'assistant'
|
||||
(no assistant prefill support). This should hold regardless of interactive mode.
|
||||
"""
|
||||
llm = _make_llm(monkeypatch, "claude-sonnet-4-6", interactive=True)
|
||||
history = _history_ending_with_assistant()
|
||||
messages = llm._prepare_messages(history)
|
||||
assert messages[-1]["role"] == "user"
|
||||
assert messages[-1]["content"] == "<meta>Continue the task.</meta>"
|
||||
|
||||
|
||||
def test_interactive_non_anthropic_does_not_add_user_message(monkeypatch) -> None:
|
||||
"""Interactive mode with a non-Anthropic model keeps the trailing assistant message.
|
||||
|
||||
Non-Anthropic models may support assistant prefill; in interactive mode the
|
||||
caller (TUI) is responsible for appending the next user message.
|
||||
"""
|
||||
llm = _make_llm(monkeypatch, "openai/gpt-5.4", interactive=True)
|
||||
history = _history_ending_with_assistant()
|
||||
messages = llm._prepare_messages(history)
|
||||
assert messages[-1]["role"] == "assistant"
|
||||
Loading…
Add table
Reference in a new issue