"""Tests for the model-independent parts of the agent: tool-call parsing,
streamed tool-call suppression, and history truncation."""
from __future__ import annotations
import sys
import unittest
from pathlib import Path
PROJECT_ROOT = Path(__file__).resolve().parents[1]
if str(PROJECT_ROOT) not in sys.path:
sys.path.insert(0, str(PROJECT_ROOT))
from controlai_agent.agent import _StreamGate
from controlai_agent.toolcall import degenerate_reason, parse
class TestToolCallParsing(unittest.TestCase):
def test_plain_call(self):
calls, prose = parse(
'\n{"name": "continuous_lqr", "arguments": {"A": [[0, 1], [-2, -3]]}}\n'
)
self.assertEqual(len(calls), 1)
self.assertEqual(calls[0].name, "continuous_lqr")
self.assertEqual(calls[0].arguments["A"], [[0, 1], [-2, -3]])
self.assertEqual(prose, "")
def test_narration_before_call_is_kept_separate(self):
calls, prose = parse(
'Let me compute that.\n\n{"name": "eigen_analysis", "arguments": {}}\n'
)
self.assertEqual([c.name for c in calls], ["eigen_analysis"])
self.assertEqual(prose, "Let me compute that.")
def test_truncated_call_is_recovered(self):
"""Hitting the token limit mid-call should not discard the call."""
calls, _ = parse('\n{"name": "bode_analysis", "arguments": {"numerator": [10]')
self.assertEqual(len(calls), 1)
self.assertEqual(calls[0].arguments["numerator"], [10])
def test_empty_tool_call_yields_no_call(self):
"""The failure mode of the old fine-tuned adapter: an empty call plus
real prose. The prose must survive and no tool must run."""
calls, prose = parse("\n\n\n\nThe phase margin is the answer.")
self.assertEqual(calls, [])
self.assertEqual(prose, "The phase margin is the answer.")
def test_math_is_not_mangled(self):
_, prose = parse(r"Gain margin is $6$ dB with $\zeta = 0.5$.")
self.assertEqual(prose, r"Gain margin is $6$ dB with $\zeta = 0.5$.")
def test_multiple_calls(self):
calls, _ = parse(
'\n{"name": "a", "arguments": {}}\n'
'\n{"name": "b", "arguments": {}}\n'
)
self.assertEqual([c.name for c in calls], ["a", "b"])
def test_thinking_block_stripped(self):
_, prose = parse("weighing options\n\nThe answer is 3 dB.")
self.assertEqual(prose, "The answer is 3 dB.")
class TestDegenerateGuard(unittest.TestCase):
def test_repetition_loop_detected(self):
self.assertIsNotNone(degenerate_reason([[0.5] * 40]))
def test_runaway_length_detected(self):
self.assertIsNotNone(degenerate_reason(list(range(500))))
def test_ordinary_matrices_pass(self):
for value in ([[0, 1], [-2, -3]], [[1.0]], [1, 6, 5, 0], [[0], [1]]):
self.assertIsNone(degenerate_reason(value), value)
def test_identity_matrix_passes(self):
"""A legitimate matrix full of repeated values must not be rejected."""
self.assertIsNone(degenerate_reason([[1 if i == j else 0 for j in range(8)] for i in range(8)]))
class TestStreamGate(unittest.TestCase):
def test_prose_passes_through(self):
gate = _StreamGate()
self.assertEqual(gate.feed("The gain margin "), "The gain margin ")
self.assertEqual(gate.feed("is 6 dB."), "is 6 dB.")
def test_tool_call_never_leaks(self):
gate = _StreamGate()
emitted = "".join(gate.feed(part) for part in ("Computing.", "", '{"name"', "}"))
self.assertEqual(emitted, "Computing.")
self.assertTrue(gate.suppressed)
self.assertEqual(gate.flush(), "")
def test_marker_split_across_chunks_is_held_back(self):
gate = _StreamGate()
self.assertEqual(gate.feed("donex"), "")
def test_partial_lookalike_is_released(self):
"""`"), "")
class TestHistoryTruncation(unittest.TestCase):
def test_oldest_turns_dropped_first(self):
from controlai_agent import agent as agent_module
class FakeEngine:
def count_tokens(self, text):
return len(text.split())
holder = object.__new__(agent_module.ControlAgent)
holder.engine = FakeEngine()
history = [
{"role": "user", "content": "old " * 100},
{"role": "assistant", "content": "reply " * 100},
{"role": "user", "content": "recent question"},
]
kept = holder._truncate(history)
self.assertEqual(kept[-1]["content"], "recent question")
self.assertLessEqual(len(kept), 3)
def test_blank_and_tool_turns_dropped(self):
from controlai_agent import agent as agent_module
class FakeEngine:
def count_tokens(self, text):
return len(text.split())
holder = object.__new__(agent_module.ControlAgent)
holder.engine = FakeEngine()
kept = holder._truncate(
[{"role": "tool", "content": "x"}, {"role": "user", "content": " "}, {"role": "user", "content": "hi"}]
)
self.assertEqual(kept, [{"role": "user", "content": "hi"}])
if __name__ == "__main__":
unittest.main()