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.
Files changed (114) hide show
  1. aethergraph/__main__.py +3 -0
  2. aethergraph/api/v1/artifacts.py +23 -4
  3. aethergraph/api/v1/schemas.py +7 -0
  4. aethergraph/api/v1/session.py +123 -4
  5. aethergraph/config/config.py +2 -0
  6. aethergraph/config/search.py +49 -0
  7. aethergraph/contracts/services/channel.py +18 -1
  8. aethergraph/contracts/services/execution.py +58 -0
  9. aethergraph/contracts/services/llm.py +26 -0
  10. aethergraph/contracts/services/memory.py +10 -4
  11. aethergraph/contracts/services/planning.py +53 -0
  12. aethergraph/contracts/storage/event_log.py +8 -0
  13. aethergraph/contracts/storage/search_backend.py +47 -0
  14. aethergraph/contracts/storage/vector_index.py +73 -0
  15. aethergraph/core/graph/action_spec.py +76 -0
  16. aethergraph/core/graph/graph_fn.py +75 -2
  17. aethergraph/core/graph/graphify.py +74 -2
  18. aethergraph/core/runtime/graph_runner.py +2 -1
  19. aethergraph/core/runtime/node_context.py +66 -3
  20. aethergraph/core/runtime/node_services.py +8 -0
  21. aethergraph/core/runtime/run_manager.py +263 -271
  22. aethergraph/core/runtime/run_types.py +54 -1
  23. aethergraph/core/runtime/runtime_env.py +35 -14
  24. aethergraph/core/runtime/runtime_services.py +308 -18
  25. aethergraph/plugins/agents/default_chat_agent.py +266 -74
  26. aethergraph/plugins/agents/default_chat_agent_v2.py +487 -0
  27. aethergraph/plugins/channel/adapters/webui.py +69 -21
  28. aethergraph/plugins/channel/routes/webui_routes.py +8 -48
  29. aethergraph/runtime/__init__.py +12 -0
  30. aethergraph/server/app_factory.py +10 -1
  31. aethergraph/server/ui_static/assets/index-CFktGdbW.js +4913 -0
  32. aethergraph/server/ui_static/assets/index-DcfkFlTA.css +1 -0
  33. aethergraph/server/ui_static/index.html +2 -2
  34. aethergraph/services/artifacts/facade.py +157 -21
  35. aethergraph/services/artifacts/types.py +35 -0
  36. aethergraph/services/artifacts/utils.py +42 -0
  37. aethergraph/services/channel/channel_bus.py +3 -1
  38. aethergraph/services/channel/event_hub copy.py +55 -0
  39. aethergraph/services/channel/event_hub.py +81 -0
  40. aethergraph/services/channel/factory.py +3 -2
  41. aethergraph/services/channel/session.py +709 -74
  42. aethergraph/services/container/default_container.py +69 -7
  43. aethergraph/services/execution/__init__.py +0 -0
  44. aethergraph/services/execution/local_python.py +118 -0
  45. aethergraph/services/indices/__init__.py +0 -0
  46. aethergraph/services/indices/global_indices.py +21 -0
  47. aethergraph/services/indices/scoped_indices.py +292 -0
  48. aethergraph/services/llm/generic_client.py +342 -46
  49. aethergraph/services/llm/generic_embed_client.py +359 -0
  50. aethergraph/services/llm/types.py +3 -1
  51. aethergraph/services/memory/distillers/llm_long_term.py +60 -109
  52. aethergraph/services/memory/distillers/llm_long_term_v1.py +180 -0
  53. aethergraph/services/memory/distillers/llm_meta_summary.py +57 -266
  54. aethergraph/services/memory/distillers/llm_meta_summary_v1.py +342 -0
  55. aethergraph/services/memory/distillers/long_term.py +48 -131
  56. aethergraph/services/memory/distillers/long_term_v1.py +170 -0
  57. aethergraph/services/memory/facade/chat.py +18 -8
  58. aethergraph/services/memory/facade/core.py +159 -19
  59. aethergraph/services/memory/facade/distillation.py +86 -31
  60. aethergraph/services/memory/facade/retrieval.py +100 -1
  61. aethergraph/services/memory/factory.py +4 -1
  62. aethergraph/services/planning/__init__.py +0 -0
  63. aethergraph/services/planning/action_catalog.py +271 -0
  64. aethergraph/services/planning/bindings.py +56 -0
  65. aethergraph/services/planning/dependency_index.py +65 -0
  66. aethergraph/services/planning/flow_validator.py +263 -0
  67. aethergraph/services/planning/graph_io_adapter.py +150 -0
  68. aethergraph/services/planning/input_parser.py +312 -0
  69. aethergraph/services/planning/missing_inputs.py +28 -0
  70. aethergraph/services/planning/node_planner.py +613 -0
  71. aethergraph/services/planning/orchestrator.py +112 -0
  72. aethergraph/services/planning/plan_executor.py +506 -0
  73. aethergraph/services/planning/plan_types.py +321 -0
  74. aethergraph/services/planning/planner.py +617 -0
  75. aethergraph/services/planning/planner_service.py +369 -0
  76. aethergraph/services/planning/planning_context_builder.py +43 -0
  77. aethergraph/services/planning/quick_actions.py +29 -0
  78. aethergraph/services/planning/routers/__init__.py +0 -0
  79. aethergraph/services/planning/routers/simple_router.py +26 -0
  80. aethergraph/services/rag/facade.py +0 -3
  81. aethergraph/services/scope/scope.py +30 -30
  82. aethergraph/services/scope/scope_factory.py +15 -7
  83. aethergraph/services/skills/__init__.py +0 -0
  84. aethergraph/services/skills/skill_registry.py +465 -0
  85. aethergraph/services/skills/skills.py +220 -0
  86. aethergraph/services/skills/utils.py +194 -0
  87. aethergraph/storage/artifacts/artifact_index_jsonl.py +16 -10
  88. aethergraph/storage/artifacts/artifact_index_sqlite.py +12 -2
  89. aethergraph/storage/docstore/sqlite_doc_sync.py +1 -1
  90. aethergraph/storage/memory/event_persist.py +42 -2
  91. aethergraph/storage/memory/fs_persist.py +32 -2
  92. aethergraph/storage/search_backend/__init__.py +0 -0
  93. aethergraph/storage/search_backend/generic_vector_backend.py +230 -0
  94. aethergraph/storage/search_backend/null_backend.py +34 -0
  95. aethergraph/storage/search_backend/sqlite_lexical_backend.py +387 -0
  96. aethergraph/storage/search_backend/utils.py +31 -0
  97. aethergraph/storage/search_factory.py +75 -0
  98. aethergraph/storage/vector_index/faiss_index.py +72 -4
  99. aethergraph/storage/vector_index/sqlite_index.py +521 -52
  100. aethergraph/storage/vector_index/sqlite_index_vanila.py +311 -0
  101. aethergraph/storage/vector_index/utils.py +22 -0
  102. {aethergraph-0.1.0a2.dist-info → aethergraph-0.1.0a4.dist-info}/METADATA +1 -1
  103. {aethergraph-0.1.0a2.dist-info → aethergraph-0.1.0a4.dist-info}/RECORD +108 -64
  104. {aethergraph-0.1.0a2.dist-info → aethergraph-0.1.0a4.dist-info}/WHEEL +1 -1
  105. aethergraph/plugins/agents/default_chat_agent copy.py +0 -90
  106. aethergraph/server/ui_static/assets/index-BR5GtXcZ.css +0 -1
  107. aethergraph/server/ui_static/assets/index-CQ0HZZ83.js +0 -400
  108. aethergraph/services/eventhub/event_hub.py +0 -76
  109. aethergraph/services/llm/generic_client copy.py +0 -691
  110. aethergraph/services/prompts/file_store.py +0 -41
  111. {aethergraph-0.1.0a2.dist-info → aethergraph-0.1.0a4.dist-info}/entry_points.txt +0 -0
  112. {aethergraph-0.1.0a2.dist-info → aethergraph-0.1.0a4.dist-info}/licenses/LICENSE +0 -0
  113. {aethergraph-0.1.0a2.dist-info → aethergraph-0.1.0a4.dist-info}/licenses/NOTICE +0 -0
  114. {aethergraph-0.1.0a2.dist-info → aethergraph-0.1.0a4.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,613 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Callable
4
+ from dataclasses import dataclass
5
+ from typing import Any
6
+
7
+ from aethergraph.api.v1.deps import RequestIdentity
8
+ from aethergraph.core.runtime.run_types import RunImportance, RunOrigin, RunVisibility
9
+
10
+ from .plan_executor import BackgroundExecutionHandle, ExecutionEvent, ExecutionResult
11
+ from .plan_types import CandidatePlan, PlanningContext, PlanningEvent
12
+ from .planner_service import PlannerService, PlanResult
13
+
14
+ PlanEventsCallback = Callable[[PlanningEvent], None]
15
+ ExecEventsCallback = Callable[[ExecutionEvent], None]
16
+
17
+
18
+ @dataclass
19
+ class NodePlanner:
20
+ """
21
+ Node-bound facade over PlannerService.
22
+
23
+ - Knows about NodeContext identity/session/app/agent.
24
+ - Delegates to PlannerService but fills execution context automatically.
25
+ """
26
+
27
+ service: PlannerService
28
+ node_ctx: Any # forward reference to NodeContext
29
+
30
+ # ---------- Planning ----------
31
+ async def plan(
32
+ self,
33
+ *,
34
+ goal: str,
35
+ user_inputs: dict[str, Any] | None = None,
36
+ external_slots: dict[str, Any] | None = None,
37
+ flow_ids: list[str] | None = None,
38
+ instruction: str | None = None,
39
+ allow_partial: bool = True,
40
+ preferred_external_keys: list[str] | None = None,
41
+ memory_snippets: list[str] | None = None,
42
+ artifact_snippets: list[str] | None = None,
43
+ on_event: PlanEventsCallback | None = None,
44
+ ) -> PlanResult:
45
+ """
46
+ Generate a plan based on the provided goal and context.
47
+
48
+ This method delegates the planning process to the underlying PlannerService,
49
+ using the provided inputs and optional callbacks for event handling.
50
+
51
+ Examples:
52
+ Basic usage to generate a plan:
53
+ ```python
54
+ result = await node_planner.plan(goal="Optimize workflow")
55
+ ```
56
+
57
+ Providing additional inputs and handling events:
58
+ ```python
59
+ result = await node_planner.plan(
60
+ goal="Generate report",
61
+ user_inputs={"date": "2023-10-01"},
62
+ on_event=lambda event: print(f"Event: {event}")
63
+ )
64
+ ```
65
+
66
+ Args:
67
+ goal: The primary objective or goal for the planning process.
68
+ user_inputs: Optional dictionary of user-provided inputs for the plan.
69
+ external_slots: Optional dictionary of external slot values to consider.
70
+ flow_ids: Optional list of flow identifiers to constrain the planning scope.
71
+ instruction: Optional instruction or guidance for the planner.
72
+ allow_partial: Whether to allow partial plans if the goal cannot be fully satisfied (default: True).
73
+ preferred_external_keys: Optional list of preferred external keys to prioritize.
74
+ memory_snippets: Optional list of memory snippets to include in the planning context.
75
+ artifact_snippets: Optional list of artifact snippets to include in the planning context.
76
+ on_event: Optional callback function to handle planning events.
77
+ The callback should be of type `PlanningEventCallback = Callable[[PlanningEvent], None] | Callable[[PlanningEvent], Awaitable[None]]`.
78
+
79
+ Returns:
80
+ PlanResult: The result of the planning process, including the generated plan and metadata.
81
+
82
+ Notes:
83
+ You can use `on_event` to monitor the planning process and react to intermediate events.
84
+ """
85
+ return await self.service.plan(
86
+ goal=goal,
87
+ user_inputs=user_inputs,
88
+ external_slots=external_slots,
89
+ flow_ids=flow_ids,
90
+ instruction=instruction,
91
+ allow_partial=allow_partial,
92
+ preferred_external_keys=preferred_external_keys,
93
+ memory_snippets=memory_snippets,
94
+ artifact_snippets=artifact_snippets,
95
+ on_event=on_event,
96
+ )
97
+
98
+ async def plan_with_context(
99
+ self,
100
+ ctx: PlanningContext,
101
+ *,
102
+ on_event: PlanEventsCallback | None = None,
103
+ ) -> PlanResult:
104
+ """
105
+ Plan a task using the provided planning context.
106
+
107
+ This method delegates the planning process to the underlying service, allowing
108
+ for the generation of a plan based on the given context and optional event callback.
109
+
110
+ Examples:
111
+ Basic usage to generate a plan:
112
+ ```python
113
+ result = await planner.plan_with_context(context)
114
+ ```
115
+
116
+ Using an event callback to monitor planning progress:
117
+ ```python
118
+ async def on_event(event):
119
+ print(f"Received event: {event}")
120
+
121
+ result = await planner.plan_with_context(context, on_event=on_event)
122
+ ```
123
+
124
+ Args:
125
+ ctx: The `PlanningContext` object containing the necessary information for planning.
126
+ on_event: Optional callback function to handle planning events. Defaults to None.
127
+
128
+ Returns:
129
+ A `PlanResult` object containing the outcome of the planning process.
130
+
131
+ Notes:
132
+ - The `on_event` callback can be used to receive updates or intermediate results
133
+ during the planning process.
134
+ """
135
+ return await self.service.plan_with_context(ctx, on_event=on_event)
136
+
137
+ async def parse_inputs(
138
+ self,
139
+ *,
140
+ message: str,
141
+ missing_keys: list[str],
142
+ instruction: str | None = None,
143
+ ):
144
+ """
145
+ Parse input data and handle missing keys.
146
+
147
+ This method processes the provided input message, identifies any missing keys,
148
+ and optionally uses an instruction to guide the parsing process. It delegates
149
+ the actual parsing logic to the `service.parse_inputs` method.
150
+
151
+ Examples:
152
+ Basic usage to parse inputs:
153
+ ```python
154
+ result = await node_planner.parse_inputs(
155
+ message="Input data with key1 = 0.1 and key2 = dummy string inputs",
156
+ missing_keys=["key1", "key2"]
157
+ )
158
+ ```
159
+
160
+ Parsing with an additional instruction:
161
+ ```python
162
+ result = await node_planner.parse_inputs(
163
+ message="Input data with key1 equals 0.1 and key2 is some dummy string inputs",
164
+ missing_keys=["key1", "key2"],
165
+ instruction="key1 is a float and key2 is a string. Make sure all values are included in a dictionary."
166
+ )
167
+ ```
168
+
169
+ Args:
170
+ message: The input message to be parsed.
171
+ missing_keys: Field names whose values we want to extract.
172
+ instruction: Optional instruction to guide the parsing process.
173
+
174
+ Returns:
175
+ The result of the parsing operation as returned by the `service.parse_inputs` method.
176
+
177
+ Notes:
178
+ This method is asynchronous and relies on the `service.parse_inputs` implementation
179
+ for the actual parsing logic.
180
+ """
181
+ return await self.service.parse_inputs(
182
+ message=message,
183
+ missing_keys=missing_keys,
184
+ instruction=instruction,
185
+ )
186
+
187
+ # ---------- Execution (auto-fill context) ----------
188
+
189
+ async def execute(
190
+ self,
191
+ plan: CandidatePlan,
192
+ *,
193
+ user_inputs: dict[str, Any] | None = None,
194
+ on_event: ExecEventsCallback | None = None,
195
+ identity: RequestIdentity | None = None,
196
+ visibility: RunVisibility = RunVisibility.normal,
197
+ importance: RunImportance = RunImportance.normal,
198
+ session_id: str | None = None,
199
+ agent_id: str | None = None,
200
+ app_id: str | None = None,
201
+ tags: list[str] | None = None,
202
+ origin: RunOrigin | None = None,
203
+ ) -> ExecutionResult:
204
+ """
205
+ Execute a plan with the provided context and parameters.
206
+
207
+ This method uses the NodeContext to auto-fill execution metadata such as
208
+ identity, session, agent, and application IDs. Callers can override these
209
+ defaults by providing explicit keyword arguments.
210
+
211
+ Examples:
212
+ Basic usage to execute a plan:
213
+ ```python
214
+ result = await node_planner.execute(plan)
215
+ ```
216
+
217
+ Executing with additional user inputs and event handling:
218
+ ```python
219
+ result = await node_planner.execute(
220
+ plan,
221
+ user_inputs={"key1": "value1"},
222
+ on_event=lambda event: print(f"Execution event: {event}")
223
+ )
224
+ ```
225
+
226
+ Args:
227
+ plan: The `CandidatePlan` object to execute.
228
+ user_inputs: Optional dictionary of user-provided inputs for the execution. Values referenced as "${user.<key>}"
229
+ on_event: Optional callback function to handle execution events.
230
+ identity: Optional `RequestIdentity` to override the default identity.
231
+ visibility: Visibility level for the execution (default: `RunVisibility.normal`).
232
+ importance: Importance level for the execution (default: `RunImportance.normal`).
233
+ session_id: Optional session ID to override the default session.
234
+ agent_id: Optional agent ID to override the default agent.
235
+ app_id: Optional application ID to override the default application.
236
+ tags: Optional list of tags to associate with the execution.
237
+ origin: Optional `RunOrigin` to specify the origin of the execution.
238
+
239
+ Returns:
240
+ ExecutionResult: The result of the execution process, including status and metadata.
241
+
242
+ Notes:
243
+ Use the `on_event` callback to monitor execution progress and handle intermediate events.
244
+ """
245
+ ctx = self.node_ctx
246
+
247
+ eff_identity = identity or ctx.identity
248
+ eff_session_id = session_id or ctx.session_id
249
+ eff_agent_id = agent_id or ctx.agent_id
250
+ eff_app_id = app_id or ctx.app_id
251
+
252
+ eff_tags = list(tags or [])
253
+ # auto-add some helpful tags
254
+ eff_tags.append(f"graph:{ctx.graph_id}")
255
+ eff_tags.append(f"node:{ctx.node_id}")
256
+ if ctx.run_id:
257
+ eff_tags.append(f"run:{ctx.run_id}")
258
+
259
+ eff_origin = origin
260
+ if eff_origin is None:
261
+ # tiny heuristic; tune later
262
+ eff_origin = RunOrigin.agent if ctx.agent_id else RunOrigin.user
263
+
264
+ return await self.service.execute_plan(
265
+ plan,
266
+ user_inputs=user_inputs,
267
+ on_event=on_event,
268
+ identity=eff_identity,
269
+ visibility=visibility,
270
+ importance=importance,
271
+ session_id=eff_session_id,
272
+ agent_id=eff_agent_id,
273
+ app_id=eff_app_id,
274
+ tags=eff_tags,
275
+ origin=eff_origin,
276
+ )
277
+
278
+ async def plan_and_execute(
279
+ self,
280
+ *,
281
+ goal: str,
282
+ user_inputs: dict[str, Any] | None = None,
283
+ external_slots: dict[str, Any] | None = None,
284
+ flow_ids: list[str] | None = None,
285
+ instruction: str | None = None,
286
+ allow_partial: bool = False,
287
+ preferred_external_keys: list[str] | None = None,
288
+ memory_snippets: list[str] | None = None,
289
+ artifact_snippets: list[str] | None = None,
290
+ planning_events_cb: PlanEventsCallback | None = None,
291
+ execution_events_cb: ExecEventsCallback | None = None,
292
+ identity: RequestIdentity | None = None,
293
+ visibility: RunVisibility = RunVisibility.normal,
294
+ importance: RunImportance = RunImportance.normal,
295
+ session_id: str | None = None,
296
+ agent_id: str | None = None,
297
+ app_id: str | None = None,
298
+ tags: list[str] | None = None,
299
+ origin: RunOrigin | None = None,
300
+ ) -> tuple[PlanResult, ExecutionResult | None]:
301
+ """
302
+ Plan a task and execute the resulting plan with node-bound context.
303
+
304
+ This method combines the planning and execution phases into a single operation.
305
+ It generates a plan based on the provided goal and context, then executes the
306
+ accepted plan using the NodeContext to auto-fill execution metadata.
307
+
308
+ Examples:
309
+ Basic usage to plan and execute:
310
+ ```python
311
+ plan_result, exec_result = await node_planner.plan_and_execute(goal="Optimize workflow")
312
+ ```
313
+
314
+ Providing additional inputs and handling events:
315
+ ```python
316
+ plan_result, exec_result = await node_planner.plan_and_execute(
317
+ goal="Generate report",
318
+ user_inputs={"date": "2023-10-01"},
319
+ planning_events_cb=lambda event: print(f"Planning event: {event}"),
320
+ execution_events_cb=lambda event: print(f"Execution event: {event}")
321
+ )
322
+ ```
323
+
324
+ Args:
325
+ goal: The primary objective or goal for the planning process.
326
+ user_inputs: Optional dictionary of user-provided inputs for the plan and execution.
327
+ external_slots: Optional dictionary of external slot values to consider during planning.
328
+ flow_ids: Optional list of flow identifiers to constrain the planning scope.
329
+ instruction: Optional instruction or guidance for the planner.
330
+ allow_partial: Whether to allow partial plans if the goal cannot be fully satisfied (default: False).
331
+ preferred_external_keys: Optional list of preferred external keys to prioritize during planning.
332
+ memory_snippets: Optional list of memory snippets to include in the planning context.
333
+ artifact_snippets: Optional list of artifact snippets to include in the planning context.
334
+ planning_events_cb: Optional callback function to handle planning events.
335
+ execution_events_cb: Optional callback function to handle execution events.
336
+ identity: Optional `RequestIdentity` to override the default identity.
337
+ visibility: Visibility level for the execution (default: `RunVisibility.normal`).
338
+ importance: Importance level for the execution (default: `RunImportance.normal`).
339
+ session_id: Optional session ID to override the default session.
340
+ agent_id: Optional agent ID to override the default agent.
341
+ app_id: Optional application ID to override the default application.
342
+ tags: Optional list of tags to associate with the execution.
343
+ origin: Optional `RunOrigin` to specify the origin of the execution.
344
+
345
+ Returns:
346
+ tuple[PlanResult, ExecutionResult | None]: A tuple containing the result of the planning process
347
+ and the result of the execution process (if applicable).
348
+
349
+ Notes:
350
+ - Use the `planning_events_cb` and `execution_events_cb` callbacks to monitor progress and handle
351
+ intermediate events during the planning and execution phases.
352
+ """
353
+ ctx = self.node_ctx
354
+
355
+ eff_identity = identity or ctx.identity
356
+ eff_session_id = session_id or ctx.session_id
357
+ eff_agent_id = agent_id or ctx.agent_id
358
+ eff_app_id = app_id or ctx.app_id
359
+
360
+ eff_tags = list(tags or [])
361
+ eff_tags.append(f"graph:{ctx.graph_id}")
362
+ eff_tags.append(f"node:{ctx.node_id}")
363
+ if ctx.run_id:
364
+ eff_tags.append(f"run:{ctx.run_id}")
365
+
366
+ eff_origin = origin
367
+ # if eff_origin is None:
368
+ # eff_origin = RunOrigin.agent if ctx.agent_id else RunOrigin.user
369
+
370
+ return await self.service.plan_and_execute(
371
+ goal=goal,
372
+ user_inputs=user_inputs,
373
+ external_slots=external_slots,
374
+ flow_ids=flow_ids,
375
+ instruction=instruction,
376
+ allow_partial=allow_partial,
377
+ preferred_external_keys=preferred_external_keys,
378
+ memory_snippets=memory_snippets,
379
+ artifact_snippets=artifact_snippets,
380
+ planning_events_cb=planning_events_cb,
381
+ execution_events_cb=execution_events_cb,
382
+ identity=eff_identity,
383
+ visibility=visibility,
384
+ importance=importance,
385
+ session_id=eff_session_id,
386
+ agent_id=eff_agent_id,
387
+ app_id=eff_app_id,
388
+ tags=eff_tags,
389
+ origin=eff_origin,
390
+ )
391
+
392
+ # ---------- Background execution (auto-fill context) ----------
393
+
394
+ async def execute_background(
395
+ self,
396
+ plan: CandidatePlan,
397
+ *,
398
+ user_inputs: dict[str, Any] | None = None,
399
+ on_event: ExecEventsCallback | None = None,
400
+ identity: RequestIdentity | None = None,
401
+ visibility: RunVisibility = RunVisibility.normal,
402
+ importance: RunImportance = RunImportance.normal,
403
+ session_id: str | None = None,
404
+ agent_id: str | None = None,
405
+ app_id: str | None = None,
406
+ tags: list[str] | None = None,
407
+ origin: RunOrigin | None = None,
408
+ on_complete: Callable[[ExecutionResult], Any] | None = None,
409
+ exec_id: str | None = None,
410
+ ) -> BackgroundExecutionHandle:
411
+ """
412
+ Execute a candidate plan in the background, bound to this node.
413
+ This method initiates a fire-and-forget execution of the provided plan,
414
+ leveraging the node's context and metadata. Progress updates can be
415
+ reported via the `on_event` callback, and completion is signaled through
416
+ the `on_complete` callback.
417
+
418
+ Examples:
419
+ Basic usage to execute a plan in the background:
420
+ ```python
421
+ handle = await node_planner.execute_background(plan)
422
+ ```
423
+
424
+ Executing with additional metadata and callbacks:
425
+ ```python
426
+ handle = await node_planner.execute_background(
427
+ user_inputs={"param1": "value1"},
428
+ on_event=event_callback,
429
+ on_complete=completion_callback,
430
+ tags=["custom:tag"],
431
+ )
432
+ ```
433
+
434
+ Args:
435
+ plan: The candidate plan to execute.
436
+ user_inputs: Optional dictionary of user-provided inputs for the execution.
437
+ on_event: Optional callback to handle execution progress events.
438
+ identity: Optional identity to associate with the execution; defaults to the node's context identity.
439
+ visibility: The visibility level of the execution (default: RunVisibility.normal).
440
+ importance: The importance level of the execution (default: RunImportance.normal).
441
+ session_id: Optional session ID to associate with the execution; defaults to the node's context session ID.
442
+ agent_id: Optional agent ID to associate with the execution; defaults to the node's context agent ID.
443
+ app_id: Optional application ID to associate with the execution; defaults to the node's context app ID.
444
+ tags: Optional list of tags to associate with the execution; additional tags are derived from the node's context.
445
+ origin: Optional origin of the execution; defaults to `RunOrigin.agent` if an agent ID is present, otherwise `RunOrigin.user`.
446
+ on_complete: Optional callback to handle execution completion.
447
+ exec_id: Optional explicit execution ID to associate with the execution.
448
+
449
+ Returns:
450
+ BackgroundExecutionHandle: A handle to monitor or interact with the background execution.
451
+
452
+ Notes:
453
+ - Tags automatically include identifiers for the graph, node, and run (if available) from the node's context.
454
+ - The `on_event` callback is invoked with progress updates, while the `on_complete` callback is invoked upon completion.
455
+ """
456
+ ctx = self.node_ctx
457
+
458
+ eff_identity = identity or ctx.identity
459
+ eff_session_id = session_id or ctx.session_id
460
+ eff_agent_id = agent_id or ctx.agent_id
461
+ eff_app_id = app_id or ctx.app_id
462
+
463
+ eff_tags = list(tags or [])
464
+ eff_tags.append(f"graph:{ctx.graph_id}")
465
+ eff_tags.append(f"node:{ctx.node_id}")
466
+ if ctx.run_id:
467
+ eff_tags.append(f"run:{ctx.run_id}")
468
+
469
+ eff_origin = origin
470
+ if eff_origin is None:
471
+ eff_origin = RunOrigin.agent if ctx.agent_id else RunOrigin.user
472
+
473
+ handle = await self.service.execute_background(
474
+ plan,
475
+ user_inputs=user_inputs,
476
+ on_event=on_event,
477
+ identity=eff_identity,
478
+ visibility=visibility,
479
+ importance=importance,
480
+ session_id=eff_session_id,
481
+ agent_id=eff_agent_id,
482
+ app_id=eff_app_id,
483
+ tags=eff_tags,
484
+ origin=eff_origin,
485
+ on_complete=on_complete,
486
+ exec_id=exec_id,
487
+ )
488
+ return handle
489
+
490
+ async def plan_and_execute_background(
491
+ self,
492
+ *,
493
+ goal: str,
494
+ user_inputs: dict[str, Any] | None = None,
495
+ external_slots: dict[str, Any] | None = None,
496
+ flow_ids: list[str] | None = None,
497
+ instruction: str | None = None,
498
+ allow_partial: bool = False,
499
+ preferred_external_keys: list[str] | None = None,
500
+ memory_snippets: list[str] | None = None,
501
+ artifact_snippets: list[str] | None = None,
502
+ planning_events_cb: PlanEventsCallback | None = None,
503
+ execution_events_cb: ExecEventsCallback | None = None,
504
+ identity: RequestIdentity | None = None,
505
+ visibility: RunVisibility = RunVisibility.normal,
506
+ importance: RunImportance = RunImportance.normal,
507
+ session_id: str | None = None,
508
+ agent_id: str | None = None,
509
+ app_id: str | None = None,
510
+ tags: list[str] | None = None,
511
+ origin: RunOrigin | None = None,
512
+ on_complete: Callable[[ExecutionResult], Any] | None = None,
513
+ exec_id: str | None = None,
514
+ ) -> tuple[PlanResult, BackgroundExecutionHandle | None]:
515
+ """
516
+ Plan and execute a goal asynchronously in the background.
517
+ This method delegates the planning and execution of a goal to the service layer,
518
+ while auto-filling the node context and other metadata. It supports callbacks
519
+ for planning and execution events, and allows for customization of execution
520
+ parameters such as visibility, importance, and tags.
521
+
522
+ Examples:
523
+ Basic usage to plan and execute a goal:
524
+ ```python
525
+ plan_result, handle = await node_planner.plan_and_execute_background(
526
+ goal="Achieve X",
527
+ user_inputs={"key": "value"}
528
+ )
529
+ ```
530
+
531
+ Using callbacks and additional metadata:
532
+ ```python
533
+ plan_result, handle = await node_planner.plan_and_execute_background(
534
+ goal="Achieve Y",
535
+ planning_events_cb=on_planning_event,
536
+ execution_events_cb=on_execution_event,
537
+ tags=["custom-tag"],
538
+ visibility=RunVisibility.inline,
539
+ )
540
+ ```
541
+
542
+ Args:
543
+ goal: The goal to achieve during the planning and execution process.
544
+ user_inputs: Optional dictionary of user-provided inputs for planning.
545
+ external_slots: Optional dictionary of external slots to use during planning.
546
+ flow_ids: Optional list of flow IDs to consider during execution.
547
+ instruction: Optional instruction to guide the planning process.
548
+ allow_partial: Whether to allow partial execution if the full goal cannot be achieved.
549
+ preferred_external_keys: Optional list of preferred external keys for planning.
550
+ memory_snippets: Optional list of memory snippets to include in planning.
551
+ artifact_snippets: Optional list of artifact snippets to include in planning.
552
+ planning_events_cb: Optional callback for planning events.
553
+ execution_events_cb: Optional callback for execution events.
554
+ identity: Optional identity to use for the execution context.
555
+ visibility: Visibility level for the execution (default: normal).
556
+ importance: Importance level for the execution (default: normal).
557
+ session_id: Optional session ID to associate with the execution.
558
+ agent_id: Optional agent ID to associate with the execution.
559
+ app_id: Optional application ID to associate with the execution.
560
+ tags: Optional list of tags to associate with the execution.
561
+ origin: Optional origin metadata for the execution.
562
+ on_complete: Optional callback to invoke upon completion of execution.
563
+ exec_id: Optional execution ID to associate with the process.
564
+
565
+ Returns:
566
+ A tuple containing the planning result and an optional background execution handle.
567
+
568
+ Notes:
569
+ - The method automatically appends node-specific tags such as `graph:<graph_id>`
570
+ and `node:<node_id>` to the provided tags.
571
+ - If a `run_id` is available in the node context, it is also appended as a tag.
572
+ """
573
+ ctx = self.node_ctx
574
+
575
+ eff_identity = identity or ctx.identity
576
+ eff_session_id = session_id or ctx.session_id
577
+ eff_agent_id = agent_id or ctx.agent_id
578
+ eff_app_id = app_id or ctx.app_id
579
+
580
+ eff_tags = list(tags or [])
581
+ eff_tags.append(f"graph:{ctx.graph_id}")
582
+ eff_tags.append(f"node:{ctx.node_id}")
583
+ if ctx.run_id:
584
+ eff_tags.append(f"run:{ctx.run_id}")
585
+
586
+ eff_origin = origin
587
+ # keep same behavior as plan_and_execute (no default if None)
588
+
589
+ plan_result, handle = await self.service.plan_and_execute_background(
590
+ goal=goal,
591
+ user_inputs=user_inputs,
592
+ external_slots=external_slots,
593
+ flow_ids=flow_ids,
594
+ instruction=instruction,
595
+ allow_partial=allow_partial,
596
+ preferred_external_keys=preferred_external_keys,
597
+ memory_snippets=memory_snippets,
598
+ artifact_snippets=artifact_snippets,
599
+ planning_events_cb=planning_events_cb,
600
+ execution_events_cb=execution_events_cb,
601
+ identity=eff_identity,
602
+ visibility=visibility,
603
+ importance=importance,
604
+ session_id=eff_session_id,
605
+ agent_id=eff_agent_id,
606
+ app_id=eff_app_id,
607
+ tags=eff_tags,
608
+ origin=eff_origin,
609
+ on_complete=on_complete,
610
+ exec_id=exec_id,
611
+ )
612
+
613
+ return plan_result, handle