feat: make the pipeline work with Program and AgentSpeak

ref: N25B-429
This commit is contained in:
Twirre Meulenbelt
2026-01-06 15:26:44 +01:00
parent a357b6990b
commit 3406e9ac2f
7 changed files with 88 additions and 38 deletions

View File

@@ -42,9 +42,9 @@ class AgentSpeakGenerator:
def generate(self, program: Program) -> str:
self._asp = AstProgram()
self._asp.rules.append(AstRule(AstLiteral("phase", [AstString("start")])))
self._asp.rules.append(AstRule(self._astify(program.phases[0])))
self._add_keyword_inference()
self._add_response_goal()
self._add_default_plans()
self._process_phases(program.phases)
@@ -66,11 +66,16 @@ class AgentSpeakGenerator:
)
)
def _add_response_goal(self):
def _add_default_plans(self):
self._add_reply_with_goal_plan()
self._add_say_plan()
self._add_reply_plan()
def _add_reply_with_goal_plan(self):
self._asp.plans.append(
AstPlan(
TriggerType.ADDED_GOAL,
AstLiteral("generate_response_with_goal", [AstVar("Goal")]),
AstLiteral("reply_with_goal", [AstVar("Goal")]),
[AstLiteral("user_said", [AstVar("Message")])],
[
AstStatement(StatementType.ADD_BELIEF, AstLiteral("responded_this_turn")),
@@ -91,12 +96,59 @@ class AgentSpeakGenerator:
)
)
def _add_say_plan(self):
self._asp.plans.append(
AstPlan(
TriggerType.ADDED_GOAL,
AstLiteral("say", [AstVar("Text")]),
[],
[
AstStatement(StatementType.ADD_BELIEF, AstLiteral("responded_this_turn")),
AstStatement(StatementType.DO_ACTION, AstLiteral("say", [AstVar("Text")])),
],
)
)
def _add_reply_plan(self):
self._asp.plans.append(
AstPlan(
TriggerType.ADDED_GOAL,
AstLiteral("reply"),
[AstLiteral("user_said", [AstVar("Message")])],
[
AstStatement(StatementType.ADD_BELIEF, AstLiteral("responded_this_turn")),
AstStatement(
StatementType.DO_ACTION,
AstLiteral(
"findall",
[AstVar("Norm"), AstLiteral("norm", [AstVar("Norm")]), AstVar("Norms")],
),
),
AstStatement(
StatementType.DO_ACTION,
AstLiteral("reply", [AstVar("Message"), AstVar("Norms")]),
),
],
)
)
def _process_phases(self, phases: list[Phase]) -> None:
for curr_phase, next_phase in zip([None] + phases, phases + [None], strict=True):
if curr_phase:
self._process_phase(curr_phase)
self._add_phase_transition(curr_phase, next_phase)
# End phase behavior
# When deleting this, the entire `reply` plan and action can be deleted
self._asp.plans.append(
AstPlan(
type=TriggerType.ADDED_BELIEF,
trigger_literal=AstLiteral("user_said", [AstVar("Message")]),
context=[AstLiteral("phase", [AstString("end")])],
body=[AstStatement(StatementType.ACHIEVE_GOAL, AstLiteral("reply"))],
)
)
def _process_phase(self, phase: Phase) -> None:
for norm in phase.norms:
self._process_norm(norm, phase)
@@ -112,14 +164,14 @@ class AgentSpeakGenerator:
self._process_trigger(trigger, phase)
def _add_phase_transition(self, from_phase: Phase | None, to_phase: Phase | None) -> None:
from_phase_ast = (
self._astify(from_phase) if from_phase else AstLiteral("phase", [AstString("start")])
)
if from_phase is None:
return
from_phase_ast = self._astify(from_phase)
to_phase_ast = (
self._astify(to_phase) if to_phase else AstLiteral("phase", [AstString("end")])
)
context = [from_phase_ast]
context = [from_phase_ast, ~AstLiteral("responded_this_turn")]
if from_phase and from_phase.goals:
context.append(self._astify(from_phase.goals[-1], achieved=True))
@@ -221,14 +273,10 @@ class AgentSpeakGenerator:
def _step_to_statement(self, step: PlanElement) -> AstStatement:
match step:
case Goal() as g:
return AstStatement(StatementType.ACHIEVE_GOAL, self._astify(g))
case SpeechAction() | GestureAction() as a:
case Goal() | SpeechAction() | LLMAction() as a:
return AstStatement(StatementType.ACHIEVE_GOAL, self._astify(a))
case GestureAction() as a:
return AstStatement(StatementType.DO_ACTION, self._astify(a))
case LLMAction() as la:
return AstStatement(
StatementType.ACHIEVE_GOAL, self._astify(la)
) # LLM action is a goal in ASL
# TODO: separate handling of keyword and others
def _process_trigger(self, trigger: Trigger, phase: Phase) -> None:
@@ -318,7 +366,7 @@ class AgentSpeakGenerator:
@_astify.register
def _(self, la: LLMAction) -> AstExpression:
return AstLiteral("generate_response_with_goal", [AstString(la.goal)])
return AstLiteral("reply_with_goal", [AstString(la.goal)])
@staticmethod
def _slugify_str(text: str) -> str: