Source code for langgoap.graph.state

"""LangGraph state schema for GOAP execution."""

from __future__ import annotations

import operator
from dataclasses import dataclass, field
from typing import Annotated, Any

from typing_extensions import TypedDict

from langgoap.goals import GoalSpec, MultiGoal
from langgoap.planner.types import Plan


[docs] @dataclass class ActionResult: """Result of executing a single GOAP action. Attributes: action_name: Name of the action that was executed. success: Whether the action completed successfully. state_before: World state before execution. state_after: World state after execution. error: Error message if the action failed. """ action_name: str success: bool state_before: dict[str, Any] = field(default_factory=dict) state_after: dict[str, Any] = field(default_factory=dict) error: str | None = None
def successful_action_names(result: GoapState) -> list[str]: """Return names of successfully executed actions from a GOAP result. Convenience utility that replaces the common pattern:: [h.action_name for h in result["execution_history"] if h.success] Args: result: A :class:`GoapState` (or compatible dict) containing an ``execution_history`` key. Returns: List of action names that completed successfully, in execution order. """ return [h.action_name for h in result["execution_history"] if h.success]
[docs] class GoapState(TypedDict, total=False): """LangGraph state schema for the GOAP execution loop. Keys ---- ``world_state`` Current world state as a flat dictionary. ``goal`` The goal specification to achieve. May be a single :class:`GoalSpec` or a :class:`MultiGoal` wrapping several sub-goals. ``plan`` The current action plan (set by planner node). ``current_step`` Index of the next action to execute. ``execution_history`` Append-only log of action results. ``replan_count`` How many times the planner has been invoked for the current (sub-)goal. Reset to zero when a :class:`MultiGoal` advances between sequential sub-goals so each sub-goal receives its own ``max_replans`` budget. ``replan_reason`` Why the last replan was triggered. One of ``"action_failed"``, ``"state_deviation"``, ``"every_action_replan"``, ``"plan_exhausted"``, ``"max_replans_exceeded"``, or ``"subgoal_achieved"`` (only emitted when a :class:`MultiGoal` advances between sequential sub-goals). ``status`` Current execution status. ``blacklisted_actions`` Action names the planner must skip. ``action_failure_counts`` Per-action cumulative failure counts. ``current_subgoal_index`` When ``goal`` is a :class:`MultiGoal` running in ``sequential`` mode, the 0-based index of the sub-goal being worked on. Defaults to 0. ``no_plan_explanation`` Serialised :class:`~langgoap.planner.explain.NoPlanExplanation` (via ``.to_dict()``) produced when A* returns ``None``. ``None`` when a plan was found. Use :meth:`~langgoap.planner.explain.NoPlanExplanation.from_dict` to reconstruct the object. ``reflection_context`` Ordered list of verbal reflection summaries produced by :class:`~langgoap.reflexion.ReflexionTracer` after action failures. Each entry is a human-readable string of the form ``"[action_name] reflection → suggestion"``. Set by the planner node on each planning round so downstream LLM-evaluated conditions can read them without coupling directly to the tracer. """ world_state: dict[str, Any] goal: GoalSpec | MultiGoal plan: Plan | None current_step: int execution_history: Annotated[list[ActionResult], operator.add] replan_count: int replan_reason: str | None status: str blacklisted_actions: list[str] action_failure_counts: dict[str, int] current_subgoal_index: int no_plan_explanation: dict[str, Any] | None reflection_context: list[str] wall_clock_started_at: float | None