Merge branch 'fix/trigger-subgoal-error' into 'main'

Allow subgoals in triggers and empty plan

See merge request ics/sp/2025/n25b/pepperplus-cb!50
This commit was merged in pull request #50.
This commit is contained in:
2026-01-30 19:53:51 +00:00
2 changed files with 23 additions and 7 deletions

View File

@@ -4,6 +4,7 @@ University within the Software Project course.
© Copyright Utrecht University (Department of Information and Computing Sciences) © Copyright Utrecht University (Department of Information and Computing Sciences)
""" """
import logging
from functools import singledispatchmethod from functools import singledispatchmethod
from slugify import slugify from slugify import slugify
@@ -66,6 +67,7 @@ class AgentSpeakGenerator:
""" """
_asp: AstProgram _asp: AstProgram
logger = logging.getLogger(__name__)
def generate(self, program: Program) -> str: def generate(self, program: Program) -> str:
""" """
@@ -479,7 +481,8 @@ class AgentSpeakGenerator:
:param main_goal: Whether this is a main goal (for UI notification purposes). :param main_goal: Whether this is a main goal (for UI notification purposes).
""" """
context: list[AstExpression] = [self._astify(phase)] context: list[AstExpression] = [self._astify(phase)]
context.append(~self._astify(goal, achieved=True)) if goal.can_fail:
context.append(~self._astify(goal, achieved=True))
if previous_goal and previous_goal.can_fail: if previous_goal and previous_goal.can_fail:
context.append(self._astify(previous_goal, achieved=True)) context.append(self._astify(previous_goal, achieved=True))
if not continues_response: if not continues_response:
@@ -503,6 +506,10 @@ class AgentSpeakGenerator:
if not goal.can_fail and not continues_response: if not goal.can_fail and not continues_response:
body.append(AstStatement(StatementType.ADD_BELIEF, self._astify(goal, achieved=True))) body.append(AstStatement(StatementType.ADD_BELIEF, self._astify(goal, achieved=True)))
if len(body) == 0:
self.logger.warning("Goal with no plan detected: %s", goal.name)
body.append(AstStatement(StatementType.EMPTY, AstLiteral("true")))
self._asp.plans.append(AstPlan(TriggerType.ADDED_GOAL, self._astify(goal), context, body)) self._asp.plans.append(AstPlan(TriggerType.ADDED_GOAL, self._astify(goal), context, body))
self._asp.plans.append( self._asp.plans.append(
@@ -563,10 +570,10 @@ class AgentSpeakGenerator:
) )
) )
for step in trigger.plan.steps: for step in trigger.plan.steps:
body.append(self._step_to_statement(step))
if isinstance(step, Goal): if isinstance(step, Goal):
step.can_fail = False # triggers are continuous sequence new_step = step.model_copy(update={"can_fail": False}) # triggers are sequence
subgoals.append(step) subgoals.append(new_step)
body.append(self._step_to_statement(step))
# Arbitrary wait for UI to display nicely # Arbitrary wait for UI to display nicely
body.append( body.append(

View File

@@ -7,7 +7,7 @@ University within the Software Project course.
from enum import Enum from enum import Enum
from typing import Literal from typing import Literal
from pydantic import UUID4, BaseModel from pydantic import UUID4, BaseModel, field_validator
class ProgramElement(BaseModel): class ProgramElement(BaseModel):
@@ -24,6 +24,13 @@ class ProgramElement(BaseModel):
# To make program elements hashable # To make program elements hashable
model_config = {"frozen": True} model_config = {"frozen": True}
@field_validator("name")
@classmethod
def name_must_not_start_with_number(cls, v: str) -> str:
if v and v[0].isdigit():
raise ValueError('Field "name" must not start with a number.')
return v
class LogicalOperator(Enum): class LogicalOperator(Enum):
""" """
@@ -105,6 +112,7 @@ class InferredBelief(ProgramElement):
left: Belief left: Belief
right: Belief right: Belief
class EmotionBelief(ProgramElement): class EmotionBelief(ProgramElement):
""" """
Represents a belief that is set when a certain emotion is detected. Represents a belief that is set when a certain emotion is detected.
@@ -115,6 +123,7 @@ class EmotionBelief(ProgramElement):
name: str = "" name: str = ""
emotion: str emotion: str
class Norm(ProgramElement): class Norm(ProgramElement):
""" """
Base class for behavioral norms that guide the robot's interactions. Base class for behavioral norms that guide the robot's interactions.