"""Tests for lambdagent.agentruntime components.""" import pytest from lambdagent.agentruntime.action_parser import ActionParser, Action, ParseError from lambdagent.agentruntime.termination import TerminationOracle from lambdagent.agentruntime.memory_backend import LocalMemory, SQLiteMemory, MemoryBackend from lambdagent.agentruntime.trace_store import TraceStore, TraceRecord from lambdagent.agentruntime.config import RuntimeConfig, LLMConfig, MemoryConfig # ActionParser tests def test_parser_json_block(): p = ActionParser(["search", "terminate"]) action = p.parse('Let me search. ```json\n{"action": "search", "input": {"q": "test"}}\n```') assert action.tool == "search" def test_parser_inline_json(): p = ActionParser(["calc", "terminate"]) action = p.parse('I will calculate. {"action": "calc", "input": {"expr": "1+1"}}') assert action.tool == "calc" def test_parser_implicit_terminate(): p = ActionParser(["search", "terminate"]) action = p.parse("Final Answer: The result is 42.") assert action.tool == "terminate" assert "42" in str(action.input) def test_parser_unknown_tool(): p = ActionParser(["search"]) with pytest.raises(ParseError): p.parse('{"action": "unknown_tool"}') # TerminationOracle tests def test_termination_implicit(): oracle = TerminationOracle() assert oracle.should_stop("Task complete. Here is the result.", None, 0) == True assert oracle.should_stop("I need to search more.", None, 0) == False def test_termination_disabled(): oracle = TerminationOracle(implicit_detection=False) assert oracle.should_stop("Final Answer: 42", None, 0) == False # LocalMemory tests def test_local_memory_write_read(): mem = LocalMemory(size=5, ttl=3600) mem.write("k1", "v1") assert mem.read("k1") == "v1" def test_local_memory_lru(): mem = LocalMemory(size=3, ttl=3600) for i in range(5): mem.write(f"k{i}", f"v{i}") assert mem.read("k0") is None # evicted assert mem.read("k4") == "v4" def test_local_memory_recent(): mem = LocalMemory(size=10, ttl=3600) mem.write("a", "1") mem.write("b", "2") recent = mem.read_recent(5) assert len(recent) == 2 def test_local_memory_clear(): mem = LocalMemory() mem.write("k", "v") mem.clear() assert mem.read("k") is None # SQLiteMemory tests def test_sqlite_memory(): mem = SQLiteMemory(size=5, ttl=3600, db_path=":memory:") mem.write("k1", "v1") assert mem.read("k1") == "v1" mem.clear() assert mem.read("k1") is None # MemoryBackend factory def test_memory_factory(): mem = MemoryBackend.create({"strategy": "local", "size": 10, "ttl": 3600}) assert isinstance(mem, LocalMemory) mem2 = MemoryBackend.create({"strategy": "sqlite", "size": 10, "ttl": 3600}) assert isinstance(mem2, SQLiteMemory) # TraceStore tests def test_trace_store(): ts = TraceStore() ts.append(TraceRecord(step=0, term_name="think", term_type="Lam", duration_ms=100)) ts.append(TraceRecord(step=1, term_name="tool", term_type="Tool", duration_ms=50)) assert len(ts.get_all()) == 2 assert ts.get_step(0).term_name == "think" stats = ts.stats() assert stats.total_steps == 2 assert stats.total_time_ms == 150 def test_trace_timeline(): ts = TraceStore() ts.append(TraceRecord(step=0, term_name="think", term_type="Lam", duration_ms=100)) timeline = ts.to_timeline() assert "think" in timeline def test_trace_json(): ts = TraceStore() ts.append(TraceRecord(step=0, term_name="t", term_type="Lam")) j = ts.to_json() assert '"term_name": "t"' in j # RuntimeConfig tests def test_config_defaults(): cfg = RuntimeConfig() assert cfg.llm.model == "claude-sonnet-4-20250514" assert cfg.memory.strategy == "local" assert cfg.react.max_steps == 10