Skip to content

Commit 95c0df2

Browse files
justinmadisonclaude
andcommitted
Fix pre-existing mypy/ruff/black errors and restore deleted memory module
- Restore memory module (base.py, sliding_window.py, spatial.py) deleted in LDX refactor but still imported by local_llm_behavior.py - Add world_map property to AgentBehavior for lazy SpatialMemory access - Fix vllm_backend.py generate() signature to match BaseBackend superclass - Fix local_llm_behavior.py log_step() arg type and Optional style - Fix inspect_agent.py return type annotation - Apply black formatting to local_llm_behavior.py, inspect_agent.py, run_foraging_demo.py Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 646efb0 commit 95c0df2

9 files changed

Lines changed: 910 additions & 86 deletions

File tree

python/agent_runtime/behavior.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,15 @@ def decide(self, observation: Observation, tools: list[ToolSchema]) -> AgentDeci
8585
_agent_id: str | None = None
8686
_current_trace: Optional["ReasoningTrace"] = None
8787

88+
@property
89+
def world_map(self) -> "SpatialMemory":
90+
"""Public accessor for spatial memory. Lazily creates if needed."""
91+
if self._world_map is None:
92+
from .memory.spatial import SpatialMemory
93+
94+
self._world_map = SpatialMemory()
95+
return self._world_map
96+
8897
def _set_trace_context(self, agent_id: str, tick: int) -> None:
8998
"""Set trace context before decide(). Called by the IPC server."""
9099
self._agent_id = agent_id

python/agent_runtime/local_llm_behavior.py

Lines changed: 98 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,18 @@
1010
"""
1111

1212
import logging
13-
from typing import TYPE_CHECKING, Optional
13+
from typing import TYPE_CHECKING
1414

1515
from .behavior import AgentBehavior
1616
from .memory import SlidingWindowMemory
17-
from .schemas import AgentDecision, Observation, ToolSchema
18-
from .reasoning_trace import TraceStore, TraceStepName, get_global_trace_store
1917

2018
# Backwards compatibility
21-
from .reasoning_trace import PromptInspector, InspectorStage, get_global_inspector
19+
from .reasoning_trace import (
20+
PromptInspector,
21+
TraceStepName,
22+
get_global_inspector,
23+
)
24+
from .schemas import AgentDecision, Observation, ToolSchema
2225

2326
if TYPE_CHECKING:
2427
from backends.base import BaseBackend
@@ -62,7 +65,7 @@ def __init__(
6265
memory_capacity: int = 10,
6366
temperature: float = 0.7,
6467
max_tokens: int = 256,
65-
inspector: Optional[PromptInspector] = None,
68+
inspector: PromptInspector | None = None,
6669
):
6770
"""
6871
Initialize the local LLM behavior.
@@ -121,59 +124,64 @@ def decide(self, observation: Observation, tools: list[ToolSchema]) -> AgentDeci
121124

122125
# Capture observation stage
123126
if capture:
124-
capture.add_step(TraceStepName.OBSERVATION, {
125-
"agent_id": observation.agent_id,
126-
"tick": observation.tick,
127-
"position": observation.position,
128-
"health": observation.health,
129-
"energy": observation.energy,
130-
"nearby_resources": [
131-
{
132-
"name": r.name,
133-
"type": r.type,
134-
"distance": r.distance,
135-
"position": r.position
136-
}
137-
for r in observation.nearby_resources
138-
],
139-
"nearby_hazards": [
140-
{
141-
"name": h.name,
142-
"type": h.type,
143-
"distance": h.distance,
144-
"damage": h.damage
145-
}
146-
for h in observation.nearby_hazards
147-
],
148-
"inventory": [
149-
{"name": item.name, "quantity": item.quantity}
150-
for item in observation.inventory
151-
]
152-
})
127+
capture.add_step(
128+
TraceStepName.OBSERVATION,
129+
{
130+
"agent_id": observation.agent_id,
131+
"tick": observation.tick,
132+
"position": observation.position,
133+
"health": observation.health,
134+
"energy": observation.energy,
135+
"nearby_resources": [
136+
{
137+
"name": r.name,
138+
"type": r.type,
139+
"distance": r.distance,
140+
"position": r.position,
141+
}
142+
for r in observation.nearby_resources
143+
],
144+
"nearby_hazards": [
145+
{
146+
"name": h.name,
147+
"type": h.type,
148+
"distance": h.distance,
149+
"damage": h.damage,
150+
}
151+
for h in observation.nearby_hazards
152+
],
153+
"inventory": [
154+
{"name": item.name, "quantity": item.quantity}
155+
for item in observation.inventory
156+
],
157+
},
158+
)
153159

154160
# Build prompt from system prompt, memory, and observation
155161
prompt = self._build_prompt(observation, tools)
156162
logger.debug(f"Prompt length: {len(prompt)} chars (~{len(prompt) // 4} tokens)")
157163

158164
# Log prompt (if tracing enabled)
159-
self.log_step("prompt", prompt)
165+
self.log_step("prompt", {"text": prompt})
160166

161167
# Capture prompt building stage
162168
if capture:
163169
memory_items = self.memory.retrieve(limit=5)
164-
capture.add_step(TraceStepName.PROMPT_BUILDING, {
165-
"system_prompt": self.system_prompt,
166-
"memory_context": {
167-
"count": len(memory_items),
168-
"items": [
169-
{"tick": obs.tick, "position": obs.position}
170-
for obs in memory_items
171-
]
170+
capture.add_step(
171+
TraceStepName.PROMPT_BUILDING,
172+
{
173+
"system_prompt": self.system_prompt,
174+
"memory_context": {
175+
"count": len(memory_items),
176+
"items": [
177+
{"tick": obs.tick, "position": obs.position} for obs in memory_items
178+
],
179+
},
180+
"final_prompt": prompt,
181+
"prompt_length": len(prompt),
182+
"estimated_tokens": len(prompt) // 4,
172183
},
173-
"final_prompt": prompt,
174-
"prompt_length": len(prompt),
175-
"estimated_tokens": len(prompt) // 4
176-
})
184+
)
177185

178186
# Convert tools to dict format for backend
179187
tool_dicts = [
@@ -187,13 +195,16 @@ def decide(self, observation: Observation, tools: list[ToolSchema]) -> AgentDeci
187195

188196
# Capture LLM request stage
189197
if capture:
190-
capture.add_step(TraceStepName.LLM_REQUEST, {
191-
"model": getattr(self.backend, 'model_name', 'unknown'),
192-
"prompt": prompt,
193-
"tools": tool_dicts,
194-
"temperature": self.temperature,
195-
"max_tokens": self.max_tokens
196-
})
198+
capture.add_step(
199+
TraceStepName.LLM_REQUEST,
200+
{
201+
"model": getattr(self.backend, "model_name", "unknown"),
202+
"prompt": prompt,
203+
"tools": tool_dicts,
204+
"temperature": self.temperature,
205+
"max_tokens": self.max_tokens,
206+
},
207+
)
197208

198209
# Generate response using backend
199210
import time
@@ -207,13 +218,16 @@ def decide(self, observation: Observation, tools: list[ToolSchema]) -> AgentDeci
207218

208219
# Capture LLM response stage
209220
if capture:
210-
capture.add_step(TraceStepName.LLM_RESPONSE, {
211-
"raw_text": result.text,
212-
"tokens_used": result.tokens_used,
213-
"finish_reason": result.finish_reason,
214-
"metadata": result.metadata,
215-
"latency_ms": elapsed_ms
216-
})
221+
capture.add_step(
222+
TraceStepName.LLM_RESPONSE,
223+
{
224+
"raw_text": result.text,
225+
"tokens_used": result.tokens_used,
226+
"finish_reason": result.finish_reason,
227+
"metadata": result.metadata,
228+
"latency_ms": elapsed_ms,
229+
},
230+
)
217231

218232
# Check for generation errors
219233
if result.finish_reason == "error":
@@ -223,13 +237,16 @@ def decide(self, observation: Observation, tools: list[ToolSchema]) -> AgentDeci
223237

224238
# Capture error decision
225239
if capture:
226-
capture.add_step(TraceStepName.DECISION, {
227-
"tool": decision.tool,
228-
"params": decision.params,
229-
"reasoning": decision.reasoning,
230-
"total_latency_ms": elapsed_ms,
231-
"error": error_msg
232-
})
240+
capture.add_step(
241+
TraceStepName.DECISION,
242+
{
243+
"tool": decision.tool,
244+
"params": decision.params,
245+
"reasoning": decision.reasoning,
246+
"total_latency_ms": elapsed_ms,
247+
"error": error_msg,
248+
},
249+
)
233250

234251
self.inspector.finish_capture(observation.agent_id, observation.tick)
235252
self._current_trace = None # Clear for next decision
@@ -277,12 +294,15 @@ def decide(self, observation: Observation, tools: list[ToolSchema]) -> AgentDeci
277294

278295
# Capture final decision stage
279296
if capture:
280-
capture.add_step(TraceStepName.DECISION, {
281-
"tool": decision.tool,
282-
"params": decision.params,
283-
"reasoning": decision.reasoning,
284-
"total_latency_ms": elapsed_ms
285-
})
297+
capture.add_step(
298+
TraceStepName.DECISION,
299+
{
300+
"tool": decision.tool,
301+
"params": decision.params,
302+
"reasoning": decision.reasoning,
303+
"total_latency_ms": elapsed_ms,
304+
},
305+
)
286306

287307
logger.info(
288308
f"Agent {observation.agent_id} decided: {decision.tool} - {decision.reasoning} "
@@ -300,12 +320,10 @@ def decide(self, observation: Observation, tools: list[ToolSchema]) -> AgentDeci
300320

301321
# Capture error in inspector
302322
if capture:
303-
capture.add_step(TraceStepName.DECISION, {
304-
"tool": "idle",
305-
"params": {},
306-
"reasoning": f"Error: {e}",
307-
"error": str(e)
308-
})
323+
capture.add_step(
324+
TraceStepName.DECISION,
325+
{"tool": "idle", "params": {}, "reasoning": f"Error: {e}", "error": str(e)},
326+
)
309327
self.inspector.finish_capture(observation.agent_id, observation.tick)
310328
self._current_trace = None # Clear for next decision
311329

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
"""
2+
Agent memory implementations.
3+
4+
Memory Types:
5+
- SlidingWindowMemory: Simple FIFO buffer of recent observations
6+
- SpatialMemory: World mapping for tracking object positions
7+
"""
8+
9+
from .base import AgentMemory
10+
from .sliding_window import SlidingWindowMemory
11+
from .spatial import SpatialMemory, SpatialQueryResult
12+
13+
__all__ = [
14+
"AgentMemory",
15+
"SlidingWindowMemory",
16+
"SpatialMemory",
17+
"SpatialQueryResult",
18+
]
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
"""
2+
Base memory interface for agents.
3+
"""
4+
5+
from abc import ABC, abstractmethod
6+
from typing import TYPE_CHECKING
7+
8+
if TYPE_CHECKING:
9+
from ..schemas import Observation
10+
11+
12+
class AgentMemory(ABC):
13+
"""
14+
Abstract base class for agent memory systems.
15+
16+
Memory systems store and retrieve observations to provide context
17+
for agent decision-making.
18+
"""
19+
20+
@abstractmethod
21+
def store(self, observation: "Observation") -> None:
22+
"""Store an observation in memory."""
23+
pass
24+
25+
@abstractmethod
26+
def retrieve(self, query: str | None = None, limit: int | None = None) -> list["Observation"]:
27+
"""Retrieve observations from memory."""
28+
pass
29+
30+
@abstractmethod
31+
def summarize(self) -> str:
32+
"""Create a text summary of memory contents for LLM context."""
33+
pass
34+
35+
@abstractmethod
36+
def clear(self) -> None:
37+
"""Clear all stored memories."""
38+
pass
39+
40+
@abstractmethod
41+
def dump(self) -> dict:
42+
"""Dump full memory state for inspection/debugging."""
43+
pass
44+
45+
def __len__(self) -> int:
46+
"""Get number of observations in memory."""
47+
return len(self.retrieve())

0 commit comments

Comments
 (0)