docs: add docs to CB
Pretty much every class and method should have documentation now. ref: N25B-295
This commit is contained in:
@@ -30,19 +30,30 @@ class AgentDirectory:
|
||||
|
||||
class BaseAgent(ABC):
|
||||
"""
|
||||
Abstract base class for all agents. To make a new agent, inherit from
|
||||
`control_backend.agents.BaseAgent`, not this class. That ensures that a
|
||||
logger is present with the correct name pattern.
|
||||
Abstract base class for all agents in the system.
|
||||
|
||||
When subclassing, the `setup()` method needs to be overwritten. To handle
|
||||
messages from other agents, overwrite the `handle_message()` method. To
|
||||
send messages to other agents, use the `send()` method. To add custom
|
||||
behaviors/tasks to the agent, use the `add_background_task()` method.
|
||||
This class provides the foundational infrastructure for agent lifecycle management, messaging
|
||||
(both intra-process and inter-process via ZMQ), and asynchronous behavior execution.
|
||||
|
||||
.. warning::
|
||||
Do not inherit from this class directly for creating new agents. Instead, inherit from
|
||||
:class:`control_backend.agents.base.BaseAgent`, which ensures proper logger configuration.
|
||||
|
||||
:ivar name: The unique name of the agent.
|
||||
:ivar inbox: The queue for receiving internal messages.
|
||||
:ivar _tasks: A set of currently running asynchronous tasks/behaviors.
|
||||
:ivar _running: A boolean flag indicating if the agent is currently running.
|
||||
:ivar logger: The logger instance for the agent.
|
||||
"""
|
||||
|
||||
logger: logging.Logger
|
||||
|
||||
def __init__(self, name: str):
|
||||
"""
|
||||
Initialize the BaseAgent.
|
||||
|
||||
:param name: The unique identifier for this agent.
|
||||
"""
|
||||
self.name = name
|
||||
self.inbox: asyncio.Queue[InternalMessage] = asyncio.Queue()
|
||||
self._tasks: set[asyncio.Task] = set()
|
||||
@@ -53,11 +64,27 @@ class BaseAgent(ABC):
|
||||
|
||||
@abstractmethod
|
||||
async def setup(self):
|
||||
"""Overwrite this to initialize resources."""
|
||||
"""
|
||||
Initialize agent-specific resources.
|
||||
|
||||
This method must be overridden by subclasses. It is called after the agent has started
|
||||
and the ZMQ sockets have been initialized. Use this method to:
|
||||
|
||||
* Initialize connections (databases, APIs, etc.)
|
||||
* Add initial behaviors using :meth:`add_behavior`
|
||||
"""
|
||||
pass
|
||||
|
||||
async def start(self):
|
||||
"""Starts the agent and its loops."""
|
||||
"""
|
||||
Start the agent and its internal loops.
|
||||
|
||||
This method:
|
||||
1. Sets the running state to True.
|
||||
2. Initializes ZeroMQ PUB/SUB sockets for inter-process communication.
|
||||
3. Calls the user-defined :meth:`setup` method.
|
||||
4. Starts the inbox processing loop and the ZMQ receiver loop.
|
||||
"""
|
||||
self.logger.info(f"Starting agent {self.name}")
|
||||
self._running = True
|
||||
|
||||
@@ -79,7 +106,11 @@ class BaseAgent(ABC):
|
||||
await self.add_behavior(self._receive_internal_zmq_loop())
|
||||
|
||||
async def stop(self):
|
||||
"""Stops the agent."""
|
||||
"""
|
||||
Stop the agent.
|
||||
|
||||
Sets the running state to False and cancels all running background tasks.
|
||||
"""
|
||||
self._running = False
|
||||
for task in self._tasks:
|
||||
task.cancel()
|
||||
@@ -87,7 +118,16 @@ class BaseAgent(ABC):
|
||||
|
||||
async def send(self, message: InternalMessage):
|
||||
"""
|
||||
Sends a message to another agent.
|
||||
Send a message to another agent.
|
||||
|
||||
This method intelligently routes the message:
|
||||
|
||||
* If the target agent is in the same process (found in :class:`AgentDirectory`),
|
||||
the message is put directly into its inbox.
|
||||
* If the target agent is not found locally, the message is serialized and sent
|
||||
via ZeroMQ to the internal publication address.
|
||||
|
||||
:param message: The message to send.
|
||||
"""
|
||||
target = AgentDirectory.get(message.to)
|
||||
if target:
|
||||
@@ -101,7 +141,11 @@ class BaseAgent(ABC):
|
||||
self.logger.debug(f"Sent message {message.body} to {message.to} via ZMQ.")
|
||||
|
||||
async def _process_inbox(self):
|
||||
"""Default loop: equivalent to a CyclicBehaviour receiving messages."""
|
||||
"""
|
||||
Internal loop that processes messages from the inbox.
|
||||
|
||||
Reads messages from ``self.inbox`` and passes them to :meth:`handle_message`.
|
||||
"""
|
||||
while self._running:
|
||||
msg = await self.inbox.get()
|
||||
self.logger.debug(f"Received message from {msg.sender}.")
|
||||
@@ -109,8 +153,11 @@ class BaseAgent(ABC):
|
||||
|
||||
async def _receive_internal_zmq_loop(self):
|
||||
"""
|
||||
Listens for internal messages sent from agents on another process via ZMQ
|
||||
and puts them into the normal inbox.
|
||||
Internal loop that listens for ZMQ messages.
|
||||
|
||||
Subscribes to ``internal/<agent_name>`` topics. When a message is received,
|
||||
it is deserialized into an :class:`InternalMessage` and put into the local inbox.
|
||||
This bridges the gap between inter-process ZMQ communication and the intra-process inbox.
|
||||
"""
|
||||
while self._running:
|
||||
try:
|
||||
@@ -125,15 +172,24 @@ class BaseAgent(ABC):
|
||||
self.logger.exception("Could not process ZMQ message.")
|
||||
|
||||
async def handle_message(self, msg: InternalMessage):
|
||||
"""Override this to handle incoming messages."""
|
||||
"""
|
||||
Handle an incoming message.
|
||||
|
||||
This method must be overridden by subclasses to define how the agent reacts to messages.
|
||||
|
||||
:param msg: The received message.
|
||||
:raises NotImplementedError: If not overridden by the subclass.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
async def add_behavior(self, coro: Coroutine):
|
||||
"""
|
||||
Helper to add a behavior to the agent. To add asynchronous behavior to an agent, define
|
||||
an `async` function and add it to the task list by calling :func:`add_behavior`
|
||||
with it. This should happen in the :func:`setup` method of the agent. For an example, see:
|
||||
:func:`~control_backend.agents.bdi.BDICoreAgent`.
|
||||
Add a background behavior (task) to the agent.
|
||||
|
||||
This is the preferred way to run continuous loops or long-running tasks within an agent.
|
||||
The task is tracked and will be automatically cancelled when :meth:`stop` is called.
|
||||
|
||||
:param coro: The coroutine to execute as a task.
|
||||
"""
|
||||
task = asyncio.create_task(coro)
|
||||
self._tasks.add(task)
|
||||
|
||||
@@ -3,6 +3,16 @@ from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
||||
|
||||
class ZMQSettings(BaseModel):
|
||||
"""
|
||||
Configuration for ZeroMQ (ZMQ) addresses used for inter-process communication.
|
||||
|
||||
:ivar internal_pub_address: Address for the internal PUB socket.
|
||||
:ivar internal_sub_address: Address for the internal SUB socket.
|
||||
:ivar ri_command_address: Address for sending commands to the Robot Interface.
|
||||
:ivar ri_communication_address: Address for receiving communication from the Robot Interface.
|
||||
:ivar vad_agent_address: Address for the Voice Activity Detection (VAD) agent.
|
||||
"""
|
||||
|
||||
internal_pub_address: str = "tcp://localhost:5560"
|
||||
internal_sub_address: str = "tcp://localhost:5561"
|
||||
ri_command_address: str = "tcp://localhost:0000"
|
||||
@@ -11,6 +21,21 @@ class ZMQSettings(BaseModel):
|
||||
|
||||
|
||||
class AgentSettings(BaseModel):
|
||||
"""
|
||||
Names of the various agents in the system. These names are used for routing messages.
|
||||
|
||||
:ivar bdi_core_name: Name of the BDI Core Agent.
|
||||
:ivar bdi_belief_collector_name: Name of the Belief Collector Agent.
|
||||
:ivar bdi_program_manager_name: Name of the BDI Program Manager Agent.
|
||||
:ivar text_belief_extractor_name: Name of the Text Belief Extractor Agent.
|
||||
:ivar vad_name: Name of the Voice Activity Detection (VAD) Agent.
|
||||
:ivar llm_name: Name of the Large Language Model (LLM) Agent.
|
||||
:ivar test_name: Name of the Test Agent.
|
||||
:ivar transcription_name: Name of the Transcription Agent.
|
||||
:ivar ri_communication_name: Name of the RI Communication Agent.
|
||||
:ivar robot_speech_name: Name of the Robot Speech Agent.
|
||||
"""
|
||||
|
||||
# agent names
|
||||
bdi_core_name: str = "bdi_core_agent"
|
||||
bdi_belief_collector_name: str = "belief_collector_agent"
|
||||
@@ -25,6 +50,21 @@ class AgentSettings(BaseModel):
|
||||
|
||||
|
||||
class BehaviourSettings(BaseModel):
|
||||
"""
|
||||
Configuration for agent behaviors and parameters.
|
||||
|
||||
:ivar sleep_s: Default sleep time in seconds for loops.
|
||||
:ivar comm_setup_max_retries: Maximum number of retries for setting up communication.
|
||||
:ivar socket_poller_timeout_ms: Timeout in milliseconds for socket polling.
|
||||
:ivar vad_prob_threshold: Probability threshold for Voice Activity Detection.
|
||||
:ivar vad_initial_since_speech: Initial value for 'since speech' counter in VAD.
|
||||
:ivar vad_non_speech_patience_chunks: Number of non-speech chunks to wait before speech ended.
|
||||
:ivar transcription_max_concurrent_tasks: Maximum number of concurrent transcription tasks.
|
||||
:ivar transcription_words_per_minute: Estimated words per minute for transcription timing.
|
||||
:ivar transcription_words_per_token: Estimated words per token for transcription timing.
|
||||
:ivar transcription_token_buffer: Buffer for transcription tokens.
|
||||
"""
|
||||
|
||||
sleep_s: float = 1.0
|
||||
comm_setup_max_retries: int = 5
|
||||
socket_poller_timeout_ms: int = 100
|
||||
@@ -42,24 +82,60 @@ class BehaviourSettings(BaseModel):
|
||||
|
||||
|
||||
class LLMSettings(BaseModel):
|
||||
local_llm_url: str = "http://localhost:1234/v1/chat/completions"
|
||||
local_llm_model: str = "openai/gpt-oss-20b"
|
||||
request_timeout_s: int = 120
|
||||
"""
|
||||
Configuration for the Large Language Model (LLM).
|
||||
|
||||
:ivar local_llm_url: URL for the local LLM API.
|
||||
:ivar local_llm_model: Name of the local LLM model to use.
|
||||
:ivar request_timeout_s: Timeout in seconds for LLM requests.
|
||||
"""
|
||||
|
||||
local_llm_url: str = "http://localhost:11434/v1/chat/completions"
|
||||
local_llm_model: str = "gpt-oss"
|
||||
request_timeout_s: int = 10
|
||||
|
||||
|
||||
class VADSettings(BaseModel):
|
||||
"""
|
||||
Configuration for Voice Activity Detection (VAD) model.
|
||||
|
||||
:ivar repo_or_dir: Repository or directory for the VAD model.
|
||||
:ivar model_name: Name of the VAD model.
|
||||
:ivar sample_rate_hz: Sample rate in Hz for the VAD model.
|
||||
"""
|
||||
|
||||
repo_or_dir: str = "snakers4/silero-vad"
|
||||
model_name: str = "silero_vad"
|
||||
sample_rate_hz: int = 16000
|
||||
|
||||
|
||||
class SpeechModelSettings(BaseModel):
|
||||
"""
|
||||
Configuration for speech recognition models.
|
||||
|
||||
:ivar mlx_model_name: Model name for MLX-based speech recognition.
|
||||
:ivar openai_model_name: Model name for OpenAI-based speech recognition.
|
||||
"""
|
||||
|
||||
# model identifiers for speech recognition
|
||||
mlx_model_name: str = "mlx-community/whisper-small.en-mlx"
|
||||
openai_model_name: str = "small.en"
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
"""
|
||||
Global application settings.
|
||||
|
||||
:ivar app_title: Title of the application.
|
||||
:ivar ui_url: URL of the frontend UI.
|
||||
:ivar zmq_settings: ZMQ configuration.
|
||||
:ivar agent_settings: Agent name configuration.
|
||||
:ivar behaviour_settings: Behavior configuration.
|
||||
:ivar vad_settings: VAD model configuration.
|
||||
:ivar speech_model_settings: Speech model configuration.
|
||||
:ivar llm_settings: LLM configuration.
|
||||
"""
|
||||
|
||||
app_title: str = "PepperPlus"
|
||||
|
||||
ui_url: str = "http://localhost:5173"
|
||||
|
||||
Reference in New Issue
Block a user