4b6817381b
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
CI / coverage-report (push) Has been cancelled
CI / test-kubernetes (push) Has been cancelled
CI / should-run-thorough (push) Has been cancelled
CI / test-thorough (cloudwatch-demo) (push) Has been cancelled
CI / test-thorough (flink-ecs) (push) Has been cancelled
CI / test-thorough (upstream-lambda) (push) Has been cancelled
CI / test-thorough (prefect-ecs-fargate) (push) Has been cancelled
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Has been cancelled
Benchmark image — build + push to ECR (any adapter) / build + push (push) Has been cancelled
CI / quality (ubuntu-latest) (push) Has been cancelled
CI / test (tools-runtime) (push) Has been cancelled
CI / test (e2e-general) (push) Has been cancelled
CI / test (cli-runtime) (push) Has been cancelled
CI / test (e2e-provider-and-openclaw) (push) Has been cancelled
CI / test (integrations-and-misc) (push) Has been cancelled
Release / verify (push) Has been cancelled
Release / build-python-dist (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Has been cancelled
Release / publish-release (push) Has been cancelled
Release / publish-main-release (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Has been cancelled
Release / prepare (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Has been cancelled
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Has been cancelled
185 lines
6.7 KiB
Python
185 lines
6.7 KiB
Python
"""AgentState TypedDict and its Pydantic validator model.
|
|
|
|
``AgentStateModel`` is the single source of truth for field defaults and
|
|
validation. ``AgentState`` composes investigation slices from
|
|
:mod:`core.state.runtime_slices` and chat slice from
|
|
:mod:`core.state.slices`; the runtime dict remains flat.
|
|
|
|
Whenever you add or remove a field, update ``AgentStateModel`` and the
|
|
appropriate slice in ``runtime_slices.py`` or ``slices.py``.
|
|
``tests/core/state/test_agent_state_sync.py`` asserts slice keys and Pydantic
|
|
fields stay aligned with ``AgentState``.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any, cast
|
|
|
|
from pydantic import ConfigDict, Field
|
|
|
|
from config.constants.investigation import MAX_INVESTIGATION_LOOPS
|
|
from config.strict_config import StrictConfigModel
|
|
from core.domain.types.retrieval import RetrievalControlsMap
|
|
from core.state.runtime_slices import (
|
|
AlertInputSlice,
|
|
CallerMetadataSlice,
|
|
DeliveryContextSlice,
|
|
DeliveryOutputSlice,
|
|
DiagnosisSlice,
|
|
EvalHarnessSlice,
|
|
InvestigationPlanSlice,
|
|
InvestigationRuntimeSlice,
|
|
MaskingSlice,
|
|
)
|
|
from core.state.slices import ChatStateSlice
|
|
from core.state.types import AgentMode, ChatMessage, ChatMessageModel
|
|
|
|
|
|
class AgentState(
|
|
CallerMetadataSlice,
|
|
ChatStateSlice,
|
|
AlertInputSlice,
|
|
InvestigationPlanSlice,
|
|
InvestigationRuntimeSlice,
|
|
DiagnosisSlice,
|
|
MaskingSlice,
|
|
DeliveryContextSlice,
|
|
DeliveryOutputSlice,
|
|
EvalHarnessSlice,
|
|
total=False,
|
|
):
|
|
"""Unified flat state for chat and investigation modes.
|
|
|
|
Chat mode primarily uses ``ChatStateSlice`` + ``CallerMetadataSlice``.
|
|
Investigation mode uses alert, plan, runtime, diagnosis, and delivery slices.
|
|
See :mod:`core.state.runtime_slices` for investigation field groupings.
|
|
"""
|
|
|
|
|
|
InvestigationState = AgentState
|
|
|
|
|
|
class AgentStateModel(StrictConfigModel):
|
|
"""Runtime-validated state envelope used by state constructors."""
|
|
|
|
model_config = ConfigDict(extra="forbid", protected_namespaces=(), populate_by_name=True)
|
|
|
|
mode: AgentMode = "chat"
|
|
route: str = ""
|
|
org_id: str = ""
|
|
user_id: str = ""
|
|
user_email: str = ""
|
|
user_name: str = ""
|
|
organization_slug: str = ""
|
|
messages: list[ChatMessageModel] = Field(default_factory=list)
|
|
is_noise: bool = False
|
|
alert_name: str = ""
|
|
pipeline_name: str = ""
|
|
severity: str = ""
|
|
alert_source: str = ""
|
|
raw_alert: str | dict[str, Any] = Field(default_factory=lambda: {})
|
|
alert_json: dict[str, Any] = Field(default_factory=dict)
|
|
planned_actions: list[str] = Field(default_factory=list)
|
|
plan_rationale: str = ""
|
|
retrieval_controls: RetrievalControlsMap | None = None
|
|
available_sources: dict[str, dict[str, Any]] = Field(default_factory=dict)
|
|
available_action_names: list[str] = Field(default_factory=list)
|
|
tool_budget: int = Field(
|
|
default=10, ge=1, le=50, description="Maximum tools to select per step"
|
|
)
|
|
plan_audit: dict[str, Any] = Field(
|
|
default_factory=dict, description="Audit trail for planning step"
|
|
)
|
|
resolved_integrations: dict[str, Any] = Field(default_factory=dict)
|
|
context: dict[str, Any] = Field(
|
|
default_factory=dict,
|
|
description=(
|
|
"Legacy investigation evidence envelope. Not for REPL session state, "
|
|
"prompt grounding, or generic runtime request metadata."
|
|
),
|
|
)
|
|
evidence: dict[str, Any] = Field(default_factory=dict)
|
|
correlation: dict[str, Any] = Field(default_factory=dict)
|
|
root_cause: str = ""
|
|
root_cause_category: str = ""
|
|
validated_claims: list[dict[str, Any]] = Field(default_factory=list)
|
|
non_validated_claims: list[dict[str, Any]] = Field(default_factory=list)
|
|
validity_score: float = 0.0
|
|
investigation_recommendations: list[str] = Field(default_factory=list)
|
|
remediation_steps: list[str] = Field(default_factory=list)
|
|
triage_summary: str = ""
|
|
incident_status: str = ""
|
|
investigation_hypotheses: list[str] = Field(default_factory=list)
|
|
verification_summary: list[str] = Field(default_factory=list)
|
|
follow_up_questions: list[str] = Field(default_factory=list)
|
|
remediation_tradeoffs: str = ""
|
|
investigation_loop_count: int = 0
|
|
investigation_iteration_cap: int = MAX_INVESTIGATION_LOOPS
|
|
hypotheses: list[str] = Field(default_factory=list)
|
|
executed_hypotheses: list[dict[str, Any]] = Field(default_factory=list)
|
|
evidence_entries: list[dict[str, Any]] = Field(default_factory=list)
|
|
hypothesis_results: list[dict[str, Any]] = Field(default_factory=list)
|
|
action_to_run: str = ""
|
|
investigation_started_at: float = 0.0
|
|
incident_window: dict[str, Any] | None = None
|
|
incident_window_history: list[dict[str, Any]] | None = None
|
|
masking_map: dict[str, str] = Field(default_factory=dict)
|
|
slack_context: dict[str, Any] = Field(default_factory=dict)
|
|
discord_context: dict[str, Any] = Field(default_factory=dict)
|
|
telegram_context: dict[str, Any] = Field(default_factory=dict)
|
|
whatsapp_context: dict[str, Any] = Field(default_factory=dict)
|
|
twilio_sms_context: dict[str, Any] = Field(default_factory=dict)
|
|
openclaw_context: dict[str, Any] = Field(default_factory=dict)
|
|
thread_id: str = ""
|
|
run_id: str = ""
|
|
auth_token: str = Field(default="", alias="_auth_token", exclude=True)
|
|
slack_message: str = ""
|
|
problem_md: str = ""
|
|
summary: str = ""
|
|
problem_report: dict[str, Any] = Field(default_factory=dict)
|
|
report: str = ""
|
|
opensre_evaluate: bool = False
|
|
opensre_eval_rubric: str = ""
|
|
opensre_llm_eval: dict[str, Any] = Field(default_factory=dict)
|
|
|
|
|
|
def model_default_payload(*exclude: str) -> dict[str, Any]:
|
|
"""Return default field values from ``AgentStateModel``, omitting ``exclude`` keys."""
|
|
skip = frozenset(exclude)
|
|
model = AgentStateModel()
|
|
dumped = model.model_dump(mode="python", by_alias=True, exclude_none=True)
|
|
return {key: value for key, value in dumped.items() if key not in skip}
|
|
|
|
|
|
def make_chat_state(
|
|
org_id: str = "",
|
|
user_id: str = "",
|
|
user_email: str = "",
|
|
user_name: str = "",
|
|
organization_slug: str = "",
|
|
messages: list[ChatMessage] | None = None,
|
|
) -> AgentState:
|
|
"""Create initial state for chat mode."""
|
|
state = AgentStateModel.model_validate(
|
|
{
|
|
"mode": "chat",
|
|
"org_id": org_id,
|
|
"user_id": user_id,
|
|
"user_email": user_email,
|
|
"user_name": user_name,
|
|
"organization_slug": organization_slug,
|
|
"messages": messages or [],
|
|
"context": {},
|
|
}
|
|
)
|
|
return cast(AgentState, state.model_dump(mode="python", by_alias=True, exclude_none=True))
|
|
|
|
|
|
__all__ = [
|
|
"AgentState",
|
|
"AgentStateModel",
|
|
"InvestigationState",
|
|
"make_chat_state",
|
|
"model_default_payload",
|
|
]
|