docs: add missing docs

ref: N25B-115
This commit is contained in:
2026-01-16 15:35:41 +01:00
parent 41bd3ffc50
commit 7f7e0c542e
17 changed files with 191 additions and 73 deletions

View File

@@ -1 +1,5 @@
"""
This package contains all agent implementations for the PepperPlus Control Backend.
"""
from .base import BaseAgent as BaseAgent from .base import BaseAgent as BaseAgent

View File

@@ -1,2 +1,6 @@
"""
Agents responsible for controlling the robot's physical actions, such as speech and gestures.
"""
from .robot_gesture_agent import RobotGestureAgent as RobotGestureAgent from .robot_gesture_agent import RobotGestureAgent as RobotGestureAgent
from .robot_speech_agent import RobotSpeechAgent as RobotSpeechAgent from .robot_speech_agent import RobotSpeechAgent as RobotSpeechAgent

View File

@@ -1,3 +1,8 @@
"""
Agents and utilities for the BDI (Belief-Desire-Intention) reasoning system,
implementing AgentSpeak(L) logic.
"""
from control_backend.agents.bdi.bdi_core_agent import BDICoreAgent as BDICoreAgent from control_backend.agents.bdi.bdi_core_agent import BDICoreAgent as BDICoreAgent
from .text_belief_extractor_agent import ( from .text_belief_extractor_agent import (

View File

@@ -80,7 +80,7 @@ class AstTerm(AstExpression, ABC):
@dataclass(eq=False) @dataclass(eq=False)
class AstAtom(AstTerm): class AstAtom(AstTerm):
""" """
Grounded expression in all lowercase. Represents a grounded atom in AgentSpeak (e.g., lowercase constants).
""" """
value: str value: str
@@ -92,7 +92,7 @@ class AstAtom(AstTerm):
@dataclass(eq=False) @dataclass(eq=False)
class AstVar(AstTerm): class AstVar(AstTerm):
""" """
Ungrounded variable expression. First letter capitalized. Represents an ungrounded variable in AgentSpeak (e.g., capitalized names).
""" """
name: str name: str
@@ -103,6 +103,10 @@ class AstVar(AstTerm):
@dataclass(eq=False) @dataclass(eq=False)
class AstNumber(AstTerm): class AstNumber(AstTerm):
"""
Represents a numeric constant in AgentSpeak.
"""
value: int | float value: int | float
def _to_agentspeak(self) -> str: def _to_agentspeak(self) -> str:
@@ -111,6 +115,10 @@ class AstNumber(AstTerm):
@dataclass(eq=False) @dataclass(eq=False)
class AstString(AstTerm): class AstString(AstTerm):
"""
Represents a string literal in AgentSpeak.
"""
value: str value: str
def _to_agentspeak(self) -> str: def _to_agentspeak(self) -> str:
@@ -119,6 +127,10 @@ class AstString(AstTerm):
@dataclass(eq=False) @dataclass(eq=False)
class AstLiteral(AstTerm): class AstLiteral(AstTerm):
"""
Represents a literal (functor and terms) in AgentSpeak.
"""
functor: str functor: str
terms: list[AstTerm] = field(default_factory=list) terms: list[AstTerm] = field(default_factory=list)
@@ -142,6 +154,10 @@ class BinaryOperatorType(StrEnum):
@dataclass @dataclass
class AstBinaryOp(AstExpression): class AstBinaryOp(AstExpression):
"""
Represents a binary logical or relational operation in AgentSpeak.
"""
left: AstExpression left: AstExpression
operator: BinaryOperatorType operator: BinaryOperatorType
right: AstExpression right: AstExpression
@@ -167,6 +183,10 @@ class AstBinaryOp(AstExpression):
@dataclass @dataclass
class AstLogicalExpression(AstExpression): class AstLogicalExpression(AstExpression):
"""
Represents a logical expression, potentially negated, in AgentSpeak.
"""
expression: AstExpression expression: AstExpression
negated: bool = False negated: bool = False
@@ -208,6 +228,10 @@ class AstStatement(AstNode):
@dataclass @dataclass
class AstRule(AstNode): class AstRule(AstNode):
"""
Represents an inference rule in AgentSpeak. If there is no condition, it always holds.
"""
result: AstExpression result: AstExpression
condition: AstExpression | None = None condition: AstExpression | None = None
@@ -231,6 +255,10 @@ class TriggerType(StrEnum):
@dataclass @dataclass
class AstPlan(AstNode): class AstPlan(AstNode):
"""
Represents a plan in AgentSpeak, consisting of a trigger, context, and body.
"""
type: TriggerType type: TriggerType
trigger_literal: AstExpression trigger_literal: AstExpression
context: list[AstExpression] context: list[AstExpression]
@@ -260,6 +288,10 @@ class AstPlan(AstNode):
@dataclass @dataclass
class AstProgram(AstNode): class AstProgram(AstNode):
"""
Represents a full AgentSpeak program, consisting of rules and plans.
"""
rules: list[AstRule] = field(default_factory=list) rules: list[AstRule] = field(default_factory=list)
plans: list[AstPlan] = field(default_factory=list) plans: list[AstPlan] = field(default_factory=list)

View File

@@ -40,9 +40,23 @@ from control_backend.schemas.program import (
class AgentSpeakGenerator: class AgentSpeakGenerator:
"""
Generator class that translates a high-level :class:`~control_backend.schemas.program.Program`
into AgentSpeak(L) source code.
It handles the conversion of phases, norms, goals, and triggers into AgentSpeak rules and plans,
ensuring the robot follows the defined behavioral logic.
"""
_asp: AstProgram _asp: AstProgram
def generate(self, program: Program) -> str: def generate(self, program: Program) -> str:
"""
Translates a Program object into an AgentSpeak source string.
:param program: The behavior program to translate.
:return: The generated AgentSpeak code as a string.
"""
self._asp = AstProgram() self._asp = AstProgram()
if program.phases: if program.phases:

View File

@@ -18,6 +18,12 @@ type JSONLike = None | bool | int | float | str | list["JSONLike"] | dict[str, "
class BeliefState(BaseModel): class BeliefState(BaseModel):
"""
Represents the state of inferred semantic beliefs.
Maintains sets of beliefs that are currently considered true or false.
"""
true: set[InternalBelief] = set() true: set[InternalBelief] = set()
false: set[InternalBelief] = set() false: set[InternalBelief] = set()
@@ -338,7 +344,7 @@ class TextBeliefExtractorAgent(BaseAgent):
class SemanticBeliefInferrer: class SemanticBeliefInferrer:
""" """
Class that handles only prompting an LLM for semantic beliefs. Infers semantic beliefs from conversation history using an LLM.
""" """
def __init__( def __init__(
@@ -464,6 +470,10 @@ Respond with a JSON similar to the following, but with the property names as giv
class GoalAchievementInferrer(SemanticBeliefInferrer): class GoalAchievementInferrer(SemanticBeliefInferrer):
"""
Infers whether specific conversational goals have been achieved using an LLM.
"""
def __init__(self, llm: TextBeliefExtractorAgent.LLM): def __init__(self, llm: TextBeliefExtractorAgent.LLM):
super().__init__(llm) super().__init__(llm)
self.goals: set[BaseGoal] = set() self.goals: set[BaseGoal] = set()

View File

@@ -1 +1,5 @@
"""
Agents responsible for external communication and service discovery.
"""
from .ri_communication_agent import RICommunicationAgent as RICommunicationAgent from .ri_communication_agent import RICommunicationAgent as RICommunicationAgent

View File

@@ -1 +1,5 @@
"""
Agents that interface with Large Language Models for natural language processing and generation.
"""
from .llm_agent import LLMAgent as LLMAgent from .llm_agent import LLMAgent as LLMAgent

View File

@@ -1,3 +1,8 @@
"""
Agents responsible for processing sensory input, such as audio transcription and voice activity
detection.
"""
from .transcription_agent.transcription_agent import ( from .transcription_agent.transcription_agent import (
TranscriptionAgent as TranscriptionAgent, TranscriptionAgent as TranscriptionAgent,
) )

View File

@@ -74,7 +74,7 @@ class TranscriptionAgent(BaseAgent):
def _connect_audio_in_socket(self): def _connect_audio_in_socket(self):
""" """
Helper to connect the ZMQ SUB socket for audio input. Connects the ZMQ SUB socket for receiving audio data.
""" """
self.audio_in_socket = azmq.Context.instance().socket(zmq.SUB) self.audio_in_socket = azmq.Context.instance().socket(zmq.SUB)
self.audio_in_socket.setsockopt_string(zmq.SUBSCRIBE, "") self.audio_in_socket.setsockopt_string(zmq.SUBSCRIBE, "")

View File

@@ -50,10 +50,8 @@ class UserInterruptAgent(BaseAgent):
async def setup(self): async def setup(self):
""" """
Initialize the agent. Initialize the agent by setting up ZMQ sockets for receiving button events and
publishing updates.
Connects the internal ZMQ SUB socket and subscribes to the 'button_pressed' topic.
Starts the background behavior to receive the user interrupts.
""" """
context = Context.instance() context = Context.instance()
@@ -68,18 +66,15 @@ class UserInterruptAgent(BaseAgent):
async def _receive_button_event(self): async def _receive_button_event(self):
""" """
The behaviour of the UserInterruptAgent. Main loop to receive and process button press events from the UI.
Continuous loop that receives button_pressed events from the button_pressed HTTP endpoint.
These events contain a type and a context.
These are the different types and contexts: Handles different event types:
- type: "speech", context: string that the robot has to say. - `speech`: Triggers immediate robot speech.
- type: "gesture", context: single gesture name that the robot has to perform. - `gesture`: Triggers an immediate robot gesture.
- type: "override", context: id that belongs to the goal/trigger/conditional norm. - `override`: Forces a belief, trigger, or goal completion in the BDI core.
- type: "override_unachieve", context: id that belongs to the conditional norm to unachieve. - `override_unachieve`: Removes a belief from the BDI core.
- type: "next_phase", context: None, indicates to the BDI Core to - `pause`: Toggles the system's pause state.
- type: "pause", context: boolean indicating whether to pause - `next_phase` / `reset_phase`: Controls experiment flow.
- type: "reset_phase", context: None, indicates to the BDI Core to
""" """
while True: while True:
topic, body = await self.sub_socket.recv_multipart() topic, body = await self.sub_socket.recv_multipart()
@@ -172,7 +167,10 @@ class UserInterruptAgent(BaseAgent):
async def handle_message(self, msg: InternalMessage): async def handle_message(self, msg: InternalMessage):
""" """
Handle commands received from other internal Python agents. Handles internal messages from other agents, such as program updates or trigger
notifications.
:param msg: The incoming :class:`~control_backend.core.agent_system.InternalMessage`.
""" """
match msg.thread: match msg.thread:
case "new_program": case "new_program":
@@ -217,8 +215,9 @@ class UserInterruptAgent(BaseAgent):
async def _broadcast_cond_norms(self, active_slugs: list[str]): async def _broadcast_cond_norms(self, active_slugs: list[str]):
""" """
Sends the current state of all conditional norms to the UI. Broadcasts the current activation state of all conditional norms to the UI.
:param active_slugs: A list of slugs (strings) currently active in the BDI core.
:param active_slugs: A list of sluggified norm names currently active in the BDI core.
""" """
updates = [] updates = []
for asl_slug, ui_id in self._cond_norm_reverse_map.items(): for asl_slug, ui_id in self._cond_norm_reverse_map.items():
@@ -235,7 +234,9 @@ class UserInterruptAgent(BaseAgent):
def _create_mapping(self, program_json: str): def _create_mapping(self, program_json: str):
""" """
Create mappings between UI IDs and ASL slugs for triggers, goals, and conditional norms Creates a bidirectional mapping between UI identifiers and AgentSpeak slugs.
:param program_json: The JSON representation of the behavioral program.
""" """
try: try:
program = Program.model_validate_json(program_json) program = Program.model_validate_json(program_json)
@@ -277,8 +278,10 @@ class UserInterruptAgent(BaseAgent):
async def _send_experiment_update(self, data, should_log: bool = True): async def _send_experiment_update(self, data, should_log: bool = True):
""" """
Sends an update to the 'experiment' topic. Publishes an experiment state update to the internal ZMQ bus for the UI.
The SSE endpoint will pick this up and push it to the UI.
:param data: The update payload.
:param should_log: Whether to log the update.
""" """
if self.pub_socket: if self.pub_socket:
topic = b"experiment" topic = b"experiment"

View File

@@ -1,12 +0,0 @@
from fastapi import APIRouter, Request
router = APIRouter()
# TODO: implement
@router.get("/sse")
async def sse(request: Request):
"""
Placeholder for future Server-Sent Events endpoint.
"""
pass

View File

@@ -22,10 +22,22 @@ class AgentDirectory:
@staticmethod @staticmethod
def register(name: str, agent: "BaseAgent"): def register(name: str, agent: "BaseAgent"):
"""
Registers an agent instance with a unique name.
:param name: The name of the agent.
:param agent: The :class:`BaseAgent` instance.
"""
_agent_directory[name] = agent _agent_directory[name] = agent
@staticmethod @staticmethod
def get(name: str) -> "BaseAgent | None": def get(name: str) -> "BaseAgent | None":
"""
Retrieves a registered agent instance by name.
:param name: The name of the agent to retrieve.
:return: The :class:`BaseAgent` instance, or None if not found.
"""
return _agent_directory.get(name) return _agent_directory.get(name)

View File

@@ -16,4 +16,10 @@ class BeliefList(BaseModel):
class GoalList(BaseModel): class GoalList(BaseModel):
"""
Represents a list of goals, used for communicating multiple goals between agents.
:ivar goals: The list of goals.
"""
goals: list[BaseGoal] goals: list[BaseGoal]

View File

@@ -2,9 +2,22 @@ from pydantic import BaseModel
class ChatMessage(BaseModel): class ChatMessage(BaseModel):
"""
Represents a single message in a conversation.
:ivar role: The role of the speaker (e.g., 'user', 'assistant').
:ivar content: The text content of the message.
"""
role: str role: str
content: str content: str
class ChatHistory(BaseModel): class ChatHistory(BaseModel):
"""
Represents a sequence of chat messages, forming a conversation history.
:ivar messages: An ordered list of :class:`ChatMessage` objects.
"""
messages: list[ChatMessage] messages: list[ChatMessage]

View File

@@ -2,5 +2,13 @@ from pydantic import BaseModel
class ButtonPressedEvent(BaseModel): class ButtonPressedEvent(BaseModel):
"""
Represents a button press event from the UI.
:ivar type: The type of event (e.g., 'speech', 'gesture', 'override').
:ivar context: Additional data associated with the event (e.g., speech text, gesture name,
or ID).
"""
type: str type: str
context: str context: str

View File

@@ -20,6 +20,10 @@ class ProgramElement(BaseModel):
class LogicalOperator(Enum): class LogicalOperator(Enum):
"""
Logical operators for combining beliefs.
"""
AND = "AND" AND = "AND"
OR = "OR" OR = "OR"
@@ -30,9 +34,9 @@ type BasicBelief = KeywordBelief | SemanticBelief
class KeywordBelief(ProgramElement): class KeywordBelief(ProgramElement):
""" """
Represents a belief that is set when the user spoken text contains a certain keyword. Represents a belief that is activated when a specific keyword is detected in the user's speech.
:ivar keyword: The keyword on which this belief gets set. :ivar keyword: The string to look for in the transcription.
""" """
name: str = "" name: str = ""
@@ -41,9 +45,11 @@ class KeywordBelief(ProgramElement):
class SemanticBelief(ProgramElement): class SemanticBelief(ProgramElement):
""" """
Represents a belief that is set by semantic LLM validation. Represents a belief whose truth value is determined by an LLM analyzing the conversation
context.
:ivar description: Description of how to form the belief, used by the LLM. :ivar description: A natural language description of what this belief represents,
used as a prompt for the LLM.
""" """
description: str description: str
@@ -51,13 +57,11 @@ class SemanticBelief(ProgramElement):
class InferredBelief(ProgramElement): class InferredBelief(ProgramElement):
""" """
Represents a belief that gets formed by combining two beliefs with a logical AND or OR. Represents a belief derived from other beliefs using logical operators.
These beliefs can also be :class:`InferredBelief`, leading to arbitrarily deep nesting. :ivar operator: The :class:`LogicalOperator` (AND/OR) to apply.
:ivar left: The left operand (another belief).
:ivar operator: The logical operator to apply. :ivar right: The right operand (another belief).
:ivar left: The left part of the logical expression.
:ivar right: The right part of the logical expression.
""" """
name: str = "" name: str = ""
@@ -67,6 +71,13 @@ class InferredBelief(ProgramElement):
class Norm(ProgramElement): class Norm(ProgramElement):
"""
Base class for behavioral norms that guide the robot's interactions.
:ivar norm: The textual description of the norm.
:ivar critical: Whether this norm is considered critical and should be strictly enforced.
"""
name: str = "" name: str = ""
norm: str norm: str
critical: bool = False critical: bool = False
@@ -74,10 +85,7 @@ class Norm(ProgramElement):
class BasicNorm(Norm): class BasicNorm(Norm):
""" """
Represents a behavioral norm. A simple behavioral norm that is always considered for activation when its phase is active.
:ivar norm: The actual norm text describing the behavior.
:ivar critical: When true, this norm should absolutely not be violated (checked separately).
""" """
pass pass
@@ -85,9 +93,9 @@ class BasicNorm(Norm):
class ConditionalNorm(Norm): class ConditionalNorm(Norm):
""" """
Represents a norm that is only active when a condition is met (i.e., a certain belief holds). A behavioral norm that is only active when a specific condition (belief) is met.
:ivar condition: When to activate this norm. :ivar condition: The :class:`Belief` that must hold for this norm to be active.
""" """
condition: Belief condition: Belief
@@ -140,9 +148,9 @@ type Action = SpeechAction | GestureAction | LLMAction
class SpeechAction(ProgramElement): class SpeechAction(ProgramElement):
""" """
Represents the action of the robot speaking a literal text. An action where the robot speaks a predefined literal text.
:ivar text: The text to speak. :ivar text: The text content to be spoken.
""" """
name: str = "" name: str = ""
@@ -151,11 +159,10 @@ class SpeechAction(ProgramElement):
class Gesture(BaseModel): class Gesture(BaseModel):
""" """
Represents a gesture to be performed. Can be either a single gesture, Defines a physical gesture for the robot to perform.
or a random gesture from a category (tag).
:ivar type: The type of the gesture, "tag" or "single". :ivar type: Whether to use a specific "single" gesture or a random one from a "tag" category.
:ivar name: The name of the single gesture or tag. :ivar name: The identifier for the gesture or tag.
""" """
type: Literal["tag", "single"] type: Literal["tag", "single"]
@@ -164,9 +171,9 @@ class Gesture(BaseModel):
class GestureAction(ProgramElement): class GestureAction(ProgramElement):
""" """
Represents the action of the robot performing a gesture. An action where the robot performs a physical gesture.
:ivar gesture: The gesture to perform. :ivar gesture: The :class:`Gesture` definition.
""" """
name: str = "" name: str = ""
@@ -175,10 +182,9 @@ class GestureAction(ProgramElement):
class LLMAction(ProgramElement): class LLMAction(ProgramElement):
""" """
Represents the action of letting an LLM generate a reply based on its chat history An action that triggers an LLM-generated conversational response.
and an additional goal added in the prompt.
:ivar goal: The extra (temporary) goal to add to the LLM. :ivar goal: A temporary conversational goal to guide the LLM's response generation.
""" """
name: str = "" name: str = ""
@@ -187,10 +193,10 @@ class LLMAction(ProgramElement):
class Trigger(ProgramElement): class Trigger(ProgramElement):
""" """
Represents a belief-based trigger. When a belief is set, the corresponding plan is executed. Defines a reactive behavior: when the condition (belief) is met, the plan is executed.
:ivar condition: When to activate the trigger. :ivar condition: The :class:`Belief` that triggers this behavior.
:ivar plan: The plan to execute. :ivar plan: The :class:`Plan` to execute upon activation.
""" """
condition: Belief condition: Belief
@@ -199,11 +205,11 @@ class Trigger(ProgramElement):
class Phase(ProgramElement): class Phase(ProgramElement):
""" """
A distinct phase within a program, containing norms, goals, and triggers. A logical stage in the interaction program, grouping norms, goals, and triggers.
:ivar norms: List of norms active in this phase. :ivar norms: List of norms active during this phase.
:ivar goals: List of goals to pursue in this phase. :ivar goals: List of goals the robot pursues in this phase.
:ivar triggers: List of triggers that define transitions out of this phase. :ivar triggers: List of reactive behaviors defined for this phase.
""" """
name: str = "" name: str = ""
@@ -214,9 +220,9 @@ class Phase(ProgramElement):
class Program(BaseModel): class Program(BaseModel):
""" """
Represents a complete interaction program, consisting of a sequence or set of phases. The top-level container for a complete robot behavior definition.
:ivar phases: The list of phases that make up the program. :ivar phases: An ordered list of :class:`Phase` objects defining the interaction flow.
""" """
phases: list[Phase] phases: list[Phase]