aethergraph 0.1.0a3__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.
Files changed (113) hide show
  1. aethergraph/api/v1/artifacts.py +23 -4
  2. aethergraph/api/v1/schemas.py +7 -0
  3. aethergraph/api/v1/session.py +123 -4
  4. aethergraph/config/config.py +2 -0
  5. aethergraph/config/search.py +49 -0
  6. aethergraph/contracts/services/channel.py +18 -1
  7. aethergraph/contracts/services/execution.py +58 -0
  8. aethergraph/contracts/services/llm.py +26 -0
  9. aethergraph/contracts/services/memory.py +10 -4
  10. aethergraph/contracts/services/planning.py +53 -0
  11. aethergraph/contracts/storage/event_log.py +8 -0
  12. aethergraph/contracts/storage/search_backend.py +47 -0
  13. aethergraph/contracts/storage/vector_index.py +73 -0
  14. aethergraph/core/graph/action_spec.py +76 -0
  15. aethergraph/core/graph/graph_fn.py +75 -2
  16. aethergraph/core/graph/graphify.py +74 -2
  17. aethergraph/core/runtime/graph_runner.py +2 -1
  18. aethergraph/core/runtime/node_context.py +66 -3
  19. aethergraph/core/runtime/node_services.py +8 -0
  20. aethergraph/core/runtime/run_manager.py +263 -271
  21. aethergraph/core/runtime/run_types.py +54 -1
  22. aethergraph/core/runtime/runtime_env.py +35 -14
  23. aethergraph/core/runtime/runtime_services.py +308 -18
  24. aethergraph/plugins/agents/default_chat_agent.py +266 -74
  25. aethergraph/plugins/agents/default_chat_agent_v2.py +487 -0
  26. aethergraph/plugins/channel/adapters/webui.py +69 -21
  27. aethergraph/plugins/channel/routes/webui_routes.py +8 -48
  28. aethergraph/runtime/__init__.py +12 -0
  29. aethergraph/server/app_factory.py +3 -0
  30. aethergraph/server/ui_static/assets/index-CFktGdbW.js +4913 -0
  31. aethergraph/server/ui_static/assets/index-DcfkFlTA.css +1 -0
  32. aethergraph/server/ui_static/index.html +2 -2
  33. aethergraph/services/artifacts/facade.py +157 -21
  34. aethergraph/services/artifacts/types.py +35 -0
  35. aethergraph/services/artifacts/utils.py +42 -0
  36. aethergraph/services/channel/channel_bus.py +3 -1
  37. aethergraph/services/channel/event_hub copy.py +55 -0
  38. aethergraph/services/channel/event_hub.py +81 -0
  39. aethergraph/services/channel/factory.py +3 -2
  40. aethergraph/services/channel/session.py +709 -74
  41. aethergraph/services/container/default_container.py +69 -7
  42. aethergraph/services/execution/__init__.py +0 -0
  43. aethergraph/services/execution/local_python.py +118 -0
  44. aethergraph/services/indices/__init__.py +0 -0
  45. aethergraph/services/indices/global_indices.py +21 -0
  46. aethergraph/services/indices/scoped_indices.py +292 -0
  47. aethergraph/services/llm/generic_client.py +342 -46
  48. aethergraph/services/llm/generic_embed_client.py +359 -0
  49. aethergraph/services/llm/types.py +3 -1
  50. aethergraph/services/memory/distillers/llm_long_term.py +60 -109
  51. aethergraph/services/memory/distillers/llm_long_term_v1.py +180 -0
  52. aethergraph/services/memory/distillers/llm_meta_summary.py +57 -266
  53. aethergraph/services/memory/distillers/llm_meta_summary_v1.py +342 -0
  54. aethergraph/services/memory/distillers/long_term.py +48 -131
  55. aethergraph/services/memory/distillers/long_term_v1.py +170 -0
  56. aethergraph/services/memory/facade/chat.py +18 -8
  57. aethergraph/services/memory/facade/core.py +159 -19
  58. aethergraph/services/memory/facade/distillation.py +86 -31
  59. aethergraph/services/memory/facade/retrieval.py +100 -1
  60. aethergraph/services/memory/factory.py +4 -1
  61. aethergraph/services/planning/__init__.py +0 -0
  62. aethergraph/services/planning/action_catalog.py +271 -0
  63. aethergraph/services/planning/bindings.py +56 -0
  64. aethergraph/services/planning/dependency_index.py +65 -0
  65. aethergraph/services/planning/flow_validator.py +263 -0
  66. aethergraph/services/planning/graph_io_adapter.py +150 -0
  67. aethergraph/services/planning/input_parser.py +312 -0
  68. aethergraph/services/planning/missing_inputs.py +28 -0
  69. aethergraph/services/planning/node_planner.py +613 -0
  70. aethergraph/services/planning/orchestrator.py +112 -0
  71. aethergraph/services/planning/plan_executor.py +506 -0
  72. aethergraph/services/planning/plan_types.py +321 -0
  73. aethergraph/services/planning/planner.py +617 -0
  74. aethergraph/services/planning/planner_service.py +369 -0
  75. aethergraph/services/planning/planning_context_builder.py +43 -0
  76. aethergraph/services/planning/quick_actions.py +29 -0
  77. aethergraph/services/planning/routers/__init__.py +0 -0
  78. aethergraph/services/planning/routers/simple_router.py +26 -0
  79. aethergraph/services/rag/facade.py +0 -3
  80. aethergraph/services/scope/scope.py +30 -30
  81. aethergraph/services/scope/scope_factory.py +15 -7
  82. aethergraph/services/skills/__init__.py +0 -0
  83. aethergraph/services/skills/skill_registry.py +465 -0
  84. aethergraph/services/skills/skills.py +220 -0
  85. aethergraph/services/skills/utils.py +194 -0
  86. aethergraph/storage/artifacts/artifact_index_jsonl.py +16 -10
  87. aethergraph/storage/artifacts/artifact_index_sqlite.py +12 -2
  88. aethergraph/storage/docstore/sqlite_doc_sync.py +1 -1
  89. aethergraph/storage/memory/event_persist.py +42 -2
  90. aethergraph/storage/memory/fs_persist.py +32 -2
  91. aethergraph/storage/search_backend/__init__.py +0 -0
  92. aethergraph/storage/search_backend/generic_vector_backend.py +230 -0
  93. aethergraph/storage/search_backend/null_backend.py +34 -0
  94. aethergraph/storage/search_backend/sqlite_lexical_backend.py +387 -0
  95. aethergraph/storage/search_backend/utils.py +31 -0
  96. aethergraph/storage/search_factory.py +75 -0
  97. aethergraph/storage/vector_index/faiss_index.py +72 -4
  98. aethergraph/storage/vector_index/sqlite_index.py +521 -52
  99. aethergraph/storage/vector_index/sqlite_index_vanila.py +311 -0
  100. aethergraph/storage/vector_index/utils.py +22 -0
  101. {aethergraph-0.1.0a3.dist-info → aethergraph-0.1.0a4.dist-info}/METADATA +1 -1
  102. {aethergraph-0.1.0a3.dist-info → aethergraph-0.1.0a4.dist-info}/RECORD +107 -63
  103. {aethergraph-0.1.0a3.dist-info → aethergraph-0.1.0a4.dist-info}/WHEEL +1 -1
  104. aethergraph/plugins/agents/default_chat_agent copy.py +0 -90
  105. aethergraph/server/ui_static/assets/index-BR5GtXcZ.css +0 -1
  106. aethergraph/server/ui_static/assets/index-CQ0HZZ83.js +0 -400
  107. aethergraph/services/eventhub/event_hub.py +0 -76
  108. aethergraph/services/llm/generic_client copy.py +0 -691
  109. aethergraph/services/prompts/file_store.py +0 -41
  110. {aethergraph-0.1.0a3.dist-info → aethergraph-0.1.0a4.dist-info}/entry_points.txt +0 -0
  111. {aethergraph-0.1.0a3.dist-info → aethergraph-0.1.0a4.dist-info}/licenses/LICENSE +0 -0
  112. {aethergraph-0.1.0a3.dist-info → aethergraph-0.1.0a4.dist-info}/licenses/NOTICE +0 -0
  113. {aethergraph-0.1.0a3.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
+ )