aethergraph 0.1.0a2__py3-none-any.whl → 0.1.0a4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aethergraph/__main__.py +3 -0
- aethergraph/api/v1/artifacts.py +23 -4
- aethergraph/api/v1/schemas.py +7 -0
- aethergraph/api/v1/session.py +123 -4
- aethergraph/config/config.py +2 -0
- aethergraph/config/search.py +49 -0
- aethergraph/contracts/services/channel.py +18 -1
- aethergraph/contracts/services/execution.py +58 -0
- aethergraph/contracts/services/llm.py +26 -0
- aethergraph/contracts/services/memory.py +10 -4
- aethergraph/contracts/services/planning.py +53 -0
- aethergraph/contracts/storage/event_log.py +8 -0
- aethergraph/contracts/storage/search_backend.py +47 -0
- aethergraph/contracts/storage/vector_index.py +73 -0
- aethergraph/core/graph/action_spec.py +76 -0
- aethergraph/core/graph/graph_fn.py +75 -2
- aethergraph/core/graph/graphify.py +74 -2
- aethergraph/core/runtime/graph_runner.py +2 -1
- aethergraph/core/runtime/node_context.py +66 -3
- aethergraph/core/runtime/node_services.py +8 -0
- aethergraph/core/runtime/run_manager.py +263 -271
- aethergraph/core/runtime/run_types.py +54 -1
- aethergraph/core/runtime/runtime_env.py +35 -14
- aethergraph/core/runtime/runtime_services.py +308 -18
- aethergraph/plugins/agents/default_chat_agent.py +266 -74
- aethergraph/plugins/agents/default_chat_agent_v2.py +487 -0
- aethergraph/plugins/channel/adapters/webui.py +69 -21
- aethergraph/plugins/channel/routes/webui_routes.py +8 -48
- aethergraph/runtime/__init__.py +12 -0
- aethergraph/server/app_factory.py +10 -1
- aethergraph/server/ui_static/assets/index-CFktGdbW.js +4913 -0
- aethergraph/server/ui_static/assets/index-DcfkFlTA.css +1 -0
- aethergraph/server/ui_static/index.html +2 -2
- aethergraph/services/artifacts/facade.py +157 -21
- aethergraph/services/artifacts/types.py +35 -0
- aethergraph/services/artifacts/utils.py +42 -0
- aethergraph/services/channel/channel_bus.py +3 -1
- aethergraph/services/channel/event_hub copy.py +55 -0
- aethergraph/services/channel/event_hub.py +81 -0
- aethergraph/services/channel/factory.py +3 -2
- aethergraph/services/channel/session.py +709 -74
- aethergraph/services/container/default_container.py +69 -7
- aethergraph/services/execution/__init__.py +0 -0
- aethergraph/services/execution/local_python.py +118 -0
- aethergraph/services/indices/__init__.py +0 -0
- aethergraph/services/indices/global_indices.py +21 -0
- aethergraph/services/indices/scoped_indices.py +292 -0
- aethergraph/services/llm/generic_client.py +342 -46
- aethergraph/services/llm/generic_embed_client.py +359 -0
- aethergraph/services/llm/types.py +3 -1
- aethergraph/services/memory/distillers/llm_long_term.py +60 -109
- aethergraph/services/memory/distillers/llm_long_term_v1.py +180 -0
- aethergraph/services/memory/distillers/llm_meta_summary.py +57 -266
- aethergraph/services/memory/distillers/llm_meta_summary_v1.py +342 -0
- aethergraph/services/memory/distillers/long_term.py +48 -131
- aethergraph/services/memory/distillers/long_term_v1.py +170 -0
- aethergraph/services/memory/facade/chat.py +18 -8
- aethergraph/services/memory/facade/core.py +159 -19
- aethergraph/services/memory/facade/distillation.py +86 -31
- aethergraph/services/memory/facade/retrieval.py +100 -1
- aethergraph/services/memory/factory.py +4 -1
- aethergraph/services/planning/__init__.py +0 -0
- aethergraph/services/planning/action_catalog.py +271 -0
- aethergraph/services/planning/bindings.py +56 -0
- aethergraph/services/planning/dependency_index.py +65 -0
- aethergraph/services/planning/flow_validator.py +263 -0
- aethergraph/services/planning/graph_io_adapter.py +150 -0
- aethergraph/services/planning/input_parser.py +312 -0
- aethergraph/services/planning/missing_inputs.py +28 -0
- aethergraph/services/planning/node_planner.py +613 -0
- aethergraph/services/planning/orchestrator.py +112 -0
- aethergraph/services/planning/plan_executor.py +506 -0
- aethergraph/services/planning/plan_types.py +321 -0
- aethergraph/services/planning/planner.py +617 -0
- aethergraph/services/planning/planner_service.py +369 -0
- aethergraph/services/planning/planning_context_builder.py +43 -0
- aethergraph/services/planning/quick_actions.py +29 -0
- aethergraph/services/planning/routers/__init__.py +0 -0
- aethergraph/services/planning/routers/simple_router.py +26 -0
- aethergraph/services/rag/facade.py +0 -3
- aethergraph/services/scope/scope.py +30 -30
- aethergraph/services/scope/scope_factory.py +15 -7
- aethergraph/services/skills/__init__.py +0 -0
- aethergraph/services/skills/skill_registry.py +465 -0
- aethergraph/services/skills/skills.py +220 -0
- aethergraph/services/skills/utils.py +194 -0
- aethergraph/storage/artifacts/artifact_index_jsonl.py +16 -10
- aethergraph/storage/artifacts/artifact_index_sqlite.py +12 -2
- aethergraph/storage/docstore/sqlite_doc_sync.py +1 -1
- aethergraph/storage/memory/event_persist.py +42 -2
- aethergraph/storage/memory/fs_persist.py +32 -2
- aethergraph/storage/search_backend/__init__.py +0 -0
- aethergraph/storage/search_backend/generic_vector_backend.py +230 -0
- aethergraph/storage/search_backend/null_backend.py +34 -0
- aethergraph/storage/search_backend/sqlite_lexical_backend.py +387 -0
- aethergraph/storage/search_backend/utils.py +31 -0
- aethergraph/storage/search_factory.py +75 -0
- aethergraph/storage/vector_index/faiss_index.py +72 -4
- aethergraph/storage/vector_index/sqlite_index.py +521 -52
- aethergraph/storage/vector_index/sqlite_index_vanila.py +311 -0
- aethergraph/storage/vector_index/utils.py +22 -0
- {aethergraph-0.1.0a2.dist-info → aethergraph-0.1.0a4.dist-info}/METADATA +1 -1
- {aethergraph-0.1.0a2.dist-info → aethergraph-0.1.0a4.dist-info}/RECORD +108 -64
- {aethergraph-0.1.0a2.dist-info → aethergraph-0.1.0a4.dist-info}/WHEEL +1 -1
- aethergraph/plugins/agents/default_chat_agent copy.py +0 -90
- aethergraph/server/ui_static/assets/index-BR5GtXcZ.css +0 -1
- aethergraph/server/ui_static/assets/index-CQ0HZZ83.js +0 -400
- aethergraph/services/eventhub/event_hub.py +0 -76
- aethergraph/services/llm/generic_client copy.py +0 -691
- aethergraph/services/prompts/file_store.py +0 -41
- {aethergraph-0.1.0a2.dist-info → aethergraph-0.1.0a4.dist-info}/entry_points.txt +0 -0
- {aethergraph-0.1.0a2.dist-info → aethergraph-0.1.0a4.dist-info}/licenses/LICENSE +0 -0
- {aethergraph-0.1.0a2.dist-info → aethergraph-0.1.0a4.dist-info}/licenses/NOTICE +0 -0
- {aethergraph-0.1.0a2.dist-info → aethergraph-0.1.0a4.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Awaitable, Callable
|
|
4
|
+
from dataclasses import asdict, dataclass, field
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Literal
|
|
6
|
+
|
|
7
|
+
from aethergraph.services.planning.plan_executor import ExecutionEvent
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING: # avoid runtime circular import
|
|
10
|
+
from .action_catalog import ActionCatalog
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class PlanStep:
|
|
15
|
+
"""
|
|
16
|
+
Represents a single step in a plan, referencing an action and its inputs.
|
|
17
|
+
|
|
18
|
+
Attributes:
|
|
19
|
+
id (str): Unique identifier for the plan step.
|
|
20
|
+
action: short, human/LLM-readable name for the action
|
|
21
|
+
action_ref (str): Reference to the associated action specification. This is a canonicial identifier used in AetherGraph.
|
|
22
|
+
inputs (Dict[str, Any]): Input parameters for the action.
|
|
23
|
+
|
|
24
|
+
Methods:
|
|
25
|
+
from_dict(data): Creates a PlanStep instance from a dictionary.
|
|
26
|
+
to_dict(): Serializes the PlanStep instance to a dictionary.
|
|
27
|
+
|
|
28
|
+
Notes:
|
|
29
|
+
- this class is usually not accessed unless you are building or inspecting plans directly.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
id: str
|
|
33
|
+
action: str
|
|
34
|
+
action_ref: str # must match ActionSpec.ref
|
|
35
|
+
inputs: dict[str, Any] = field(default_factory=dict)
|
|
36
|
+
extras: dict[str, Any] = field(default_factory=dict)
|
|
37
|
+
|
|
38
|
+
@classmethod
|
|
39
|
+
def from_dict(cls, data: dict[str, Any]) -> PlanStep:
|
|
40
|
+
# v1 compatibility: some plans only have action_ref
|
|
41
|
+
return cls(
|
|
42
|
+
id=data["id"],
|
|
43
|
+
action=data.get("action"),
|
|
44
|
+
action_ref=data.get("action_ref"),
|
|
45
|
+
inputs=data.get("inputs", {}) or {},
|
|
46
|
+
extras=data.get("extras", {}) or {},
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
def to_dict(self) -> dict[str, Any]:
|
|
50
|
+
return asdict(self)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass
|
|
54
|
+
class CandidatePlan:
|
|
55
|
+
"""
|
|
56
|
+
CandidatePlan represents a plan consisting of multiple steps and additional metadata.
|
|
57
|
+
|
|
58
|
+
Attributes:
|
|
59
|
+
steps (list[PlanStep]): A list of steps that make up the plan.
|
|
60
|
+
extras (dict[str, Any]): Additional metadata or information associated with the plan.
|
|
61
|
+
|
|
62
|
+
Methods:
|
|
63
|
+
from_dict(data: dict[str, Any]) -> CandidatePlan:
|
|
64
|
+
Creates an instance of CandidatePlan from a dictionary representation.
|
|
65
|
+
to_dict() -> dict[str, Any]:
|
|
66
|
+
Converts the CandidatePlan instance into a dictionary representation.
|
|
67
|
+
resolve_actions(
|
|
68
|
+
include_global: bool = True
|
|
69
|
+
Ensures that each step in the plan has both `action` (name) and `action_ref`
|
|
70
|
+
(canonical reference) resolved. Resolves missing fields using the provided
|
|
71
|
+
action catalog.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
steps: list[PlanStep]
|
|
75
|
+
extras: dict[str, Any] = field(default_factory=dict)
|
|
76
|
+
|
|
77
|
+
@classmethod
|
|
78
|
+
def from_dict(cls, data: dict[str, Any]) -> CandidatePlan:
|
|
79
|
+
steps_data = data.get("steps", []) or []
|
|
80
|
+
steps = [PlanStep.from_dict(step) for step in steps_data]
|
|
81
|
+
extras = data.get("extras", {}) or {}
|
|
82
|
+
return cls(steps=steps, extras=extras)
|
|
83
|
+
|
|
84
|
+
def to_dict(self) -> dict[str, Any]:
|
|
85
|
+
return {
|
|
86
|
+
"steps": [step.to_dict() for step in self.steps],
|
|
87
|
+
"extras": self.extras,
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
def resolve_actions(
|
|
91
|
+
self,
|
|
92
|
+
catalog: ActionCatalog,
|
|
93
|
+
flow_ids: list[str] | None = None,
|
|
94
|
+
include_global: bool = True,
|
|
95
|
+
) -> None:
|
|
96
|
+
"""
|
|
97
|
+
Ensure every step has both action (name) and action_ref (canonical ref).
|
|
98
|
+
|
|
99
|
+
- If only action is set → look up spec by name & fill action_ref.
|
|
100
|
+
- If only action_ref is set → look up spec by ref & fill action.
|
|
101
|
+
- If both are set → we leave them as-is (could optionally assert they match).
|
|
102
|
+
"""
|
|
103
|
+
for step in self.steps:
|
|
104
|
+
# already fully polulated
|
|
105
|
+
if step.action and step.action_ref:
|
|
106
|
+
continue
|
|
107
|
+
|
|
108
|
+
# only action name present: resolve to ref
|
|
109
|
+
if step.action and not step.action_ref:
|
|
110
|
+
spec = catalog.get_action_by_name(
|
|
111
|
+
step.action,
|
|
112
|
+
flow_ids=flow_ids,
|
|
113
|
+
include_global=include_global,
|
|
114
|
+
)
|
|
115
|
+
if spec is None:
|
|
116
|
+
raise ValueError(
|
|
117
|
+
f"Could not resolve action name '{step.action}' to an action spec."
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
step.action_ref = spec.ref
|
|
121
|
+
|
|
122
|
+
# only action_ref present: resolve to name
|
|
123
|
+
elif step.action_ref and not step.action:
|
|
124
|
+
spec = catalog.get_action(step.action_ref)
|
|
125
|
+
if spec is None:
|
|
126
|
+
# keep action_ref, but we lose the nice name
|
|
127
|
+
continue
|
|
128
|
+
step.action = spec.name
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@dataclass
|
|
132
|
+
class ValidationIssue:
|
|
133
|
+
"""
|
|
134
|
+
Represents a validation issue encountered in a planning process.
|
|
135
|
+
|
|
136
|
+
Attributes:
|
|
137
|
+
kind (str): The type of validation issue. Possible values include
|
|
138
|
+
'missing_input', 'unknown_action', 'type_mismatch', 'cycle', etc.
|
|
139
|
+
step_id (str): The identifier of the plan step where the issue occurred.
|
|
140
|
+
field (str): The name of the input, output, or other field related to the issue.
|
|
141
|
+
message (str): A human-readable message describing the issue.
|
|
142
|
+
details (dict[str, Any]): Additional details about the issue, provided as a dictionary.
|
|
143
|
+
Defaults to an empty dictionary.
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
kind: str # 'missing_input', 'unknown_action', 'type_mismatch', 'cycle', ...
|
|
147
|
+
step_id: str # plan step id where the issue occurred
|
|
148
|
+
field: str # input name, output name, etc.
|
|
149
|
+
message: str # human-readable message describing the issue
|
|
150
|
+
details: dict[str, Any] = field(default_factory=dict)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
@dataclass
|
|
154
|
+
class ValidationResult:
|
|
155
|
+
"""
|
|
156
|
+
ValidationResult represents the result of validating a plan, including its validity,
|
|
157
|
+
any structural errors, and missing user-provided values.
|
|
158
|
+
|
|
159
|
+
Attributes:
|
|
160
|
+
ok (bool): Indicates whether the plan is valid (True) or invalid (False).
|
|
161
|
+
issues (list[ValidationIssue]): A list of validation issues found in the plan.
|
|
162
|
+
has_structural_errors (bool): Indicates if the plan has structural errors.
|
|
163
|
+
missing_user_bindings (dict[str, list[str]]): A dictionary mapping keys to lists of
|
|
164
|
+
locations where user-provided values are missing.
|
|
165
|
+
Methods:
|
|
166
|
+
is_partial_ok() -> bool:
|
|
167
|
+
Checks if the plan is structurally valid but requires user-provided values
|
|
168
|
+
before execution.
|
|
169
|
+
summary() -> str:
|
|
170
|
+
Generates a summary string describing the validity of the plan and any issues
|
|
171
|
+
found. If the plan is valid, the summary states "Plan is valid." If invalid,
|
|
172
|
+
the summary lists the issues with their details.
|
|
173
|
+
"""
|
|
174
|
+
|
|
175
|
+
ok: bool # True if plan is valid, False otherwise
|
|
176
|
+
issues: list[ValidationIssue] # list of validation issues found in the plan
|
|
177
|
+
|
|
178
|
+
# differentiate structural vs “needs user values”
|
|
179
|
+
has_structural_errors: bool = False
|
|
180
|
+
|
|
181
|
+
# key → list of locations (e.g. ["load.dataset_path", "eval.target_metric"])
|
|
182
|
+
missing_user_bindings: dict[str, list[str]] = field(default_factory=dict)
|
|
183
|
+
|
|
184
|
+
def is_partial_ok(self) -> bool:
|
|
185
|
+
"""
|
|
186
|
+
Structurally valid, but requires user-provided values
|
|
187
|
+
(e.g. ${user.dataset_path}) before execution.
|
|
188
|
+
"""
|
|
189
|
+
return (not self.has_structural_errors) and bool(self.missing_user_bindings)
|
|
190
|
+
|
|
191
|
+
def summary(self) -> str:
|
|
192
|
+
"""
|
|
193
|
+
Returns a summary string describing the validity of the plan and any issues found.
|
|
194
|
+
|
|
195
|
+
Example:
|
|
196
|
+
- If valid: "Plan is valid."
|
|
197
|
+
- If invalid:
|
|
198
|
+
Plan is invalid:
|
|
199
|
+
- [missing_input] step=step1 field=inputA: Input 'inputA' is missing.
|
|
200
|
+
- [unknown_action] step=step2: Action 'actionX' is not recognized.
|
|
201
|
+
|
|
202
|
+
"""
|
|
203
|
+
if self.ok:
|
|
204
|
+
return "Plan is valid."
|
|
205
|
+
lines = ["Plan is invalid:"]
|
|
206
|
+
for issue in self.issues:
|
|
207
|
+
prefix = f"[{issue.kind}]"
|
|
208
|
+
if issue.step_id:
|
|
209
|
+
prefix += f" step={issue.step_id}"
|
|
210
|
+
if issue.field:
|
|
211
|
+
prefix += f" field={issue.field}"
|
|
212
|
+
lines.append(f"- {prefix}: {issue.message}")
|
|
213
|
+
return "\n".join(lines)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
PlanningPhase = Literal[
|
|
217
|
+
"start",
|
|
218
|
+
"llm_request",
|
|
219
|
+
"llm_response",
|
|
220
|
+
"validation",
|
|
221
|
+
"success",
|
|
222
|
+
"failure",
|
|
223
|
+
]
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
@dataclass
|
|
227
|
+
class PlanningEvent:
|
|
228
|
+
"""
|
|
229
|
+
Represents a lightweight event emitted during the planning process.
|
|
230
|
+
This class is intended for use in logging, debugging, and updating UI progress
|
|
231
|
+
(e.g., updating a spinner or status text). It provides optional structured
|
|
232
|
+
payloads for additional context.
|
|
233
|
+
|
|
234
|
+
Attributes:
|
|
235
|
+
phase (PlanningPhase): The current phase of the planning process.
|
|
236
|
+
iteration (int): The iteration number within the planning phase.
|
|
237
|
+
message (str | None): An optional message describing the event.
|
|
238
|
+
raw_llm_output (dict[str, Any] | None): An optional dictionary containing
|
|
239
|
+
raw output from the language model.
|
|
240
|
+
plan_dict (dict[str, Any] | None): An optional dictionary representing the
|
|
241
|
+
plan structure.
|
|
242
|
+
validation (ValidationResult | None): An optional validation result
|
|
243
|
+
associated with the event.
|
|
244
|
+
"""
|
|
245
|
+
|
|
246
|
+
phase: PlanningPhase
|
|
247
|
+
iteration: int
|
|
248
|
+
message: str | None = None
|
|
249
|
+
|
|
250
|
+
# Optional structured payloads:
|
|
251
|
+
raw_llm_output: dict[str, Any] | None = None
|
|
252
|
+
plan_dict: dict[str, Any] | None = None
|
|
253
|
+
validation: ValidationResult | None = None
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
@dataclass
|
|
257
|
+
class PlanningContext:
|
|
258
|
+
"""
|
|
259
|
+
Represents the context for planning, including user inputs, external slots,
|
|
260
|
+
memory snippets, and other parameters that guide the planning process.
|
|
261
|
+
|
|
262
|
+
Attributes:
|
|
263
|
+
goal (str): The goal or objective of the planning process.
|
|
264
|
+
user_inputs (dict[str, Any]): A dictionary of user-provided inputs.
|
|
265
|
+
external_slots (dict[str, Any]): A dictionary of external slots or parameters.
|
|
266
|
+
memory_snippets (list[str]): A list of memory snippets for narrow, LLM-facing context.
|
|
267
|
+
artifact_snippets (list[str]): A list of artifact snippets for narrow, LLM-facing context.
|
|
268
|
+
flow_ids (list[str] | None): A list of flow IDs representing the tools/graphs
|
|
269
|
+
that are allowed to be used. Defaults to None.
|
|
270
|
+
instruction (str | None): A richer agent context, used by planner code but not
|
|
271
|
+
directly dumped. Can be parsed from skills. Defaults to None.
|
|
272
|
+
allow_partial_plans (bool): Indicates whether the planner should accept
|
|
273
|
+
structurally valid but missing user input plans. Defaults to True.
|
|
274
|
+
preferred_external_keys (list[str] | None): A list of keys that are allowed or
|
|
275
|
+
preferred as `${user.<key>}`. Defaults to None.
|
|
276
|
+
"""
|
|
277
|
+
|
|
278
|
+
goal: str
|
|
279
|
+
user_inputs: dict[str, Any]
|
|
280
|
+
external_slots: dict[str, Any]
|
|
281
|
+
|
|
282
|
+
# Narrow, LLM-facing context
|
|
283
|
+
memory_snippets: list[str] = field(default_factory=list)
|
|
284
|
+
artifact_snippets: list[str] = field(default_factory=list)
|
|
285
|
+
|
|
286
|
+
# What tools/graphs we’re allowed to use
|
|
287
|
+
flow_ids: list[str] | None = None
|
|
288
|
+
|
|
289
|
+
# richer agent context (used by planner code, not directly dumped), can be parsed from skills
|
|
290
|
+
instruction: str | None = None
|
|
291
|
+
|
|
292
|
+
# should planner accept structurally-valid-but-missing-user-input plans?
|
|
293
|
+
allow_partial_plans: bool = True
|
|
294
|
+
|
|
295
|
+
# planner hint – which keys are *allowed / preferred* as ${user.<key>}
|
|
296
|
+
preferred_external_keys: list[str] | None = None
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
@dataclass
|
|
300
|
+
class PlanResult:
|
|
301
|
+
"""
|
|
302
|
+
PlanResult is a data class that encapsulates the result of a planning operation.
|
|
303
|
+
|
|
304
|
+
Attributes:
|
|
305
|
+
plan (CandidatePlan | None): The candidate plan resulting from the planning operation.
|
|
306
|
+
It can be None if no valid plan was generated.
|
|
307
|
+
validation (ValidationResult | None): The validation result associated with the plan.
|
|
308
|
+
It can be None if validation was not performed or not applicable.
|
|
309
|
+
events (list[PlanningEvent]): A list of planning events that occurred during the planning process.
|
|
310
|
+
Defaults to an empty list.
|
|
311
|
+
"""
|
|
312
|
+
|
|
313
|
+
plan: CandidatePlan | None
|
|
314
|
+
validation: ValidationResult | None
|
|
315
|
+
events: list[PlanningEvent] = field(default_factory=list)
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
PlanningEventCallback = Callable[[PlanningEvent], None] | Callable[[PlanningEvent], Awaitable[None]]
|
|
319
|
+
ExecutionEventCallback = (
|
|
320
|
+
Callable[[ExecutionEvent], None] | Callable[[ExecutionEvent], Awaitable[None]]
|
|
321
|
+
)
|