arkitekt-runtime 7.2.0__tar.gz → 7.3.0__tar.gz
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.
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/PKG-INFO +2 -2
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/agents/base.py +30 -1
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/agents/hooks/materialize.py +9 -2
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/agents/hooks/startup.py +79 -2
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/pyproject.toml +1 -1
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/.gitignore +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/README.md +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/__init__.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/actors/__init__.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/actors/actify.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/actors/base.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/actors/build.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/actors/debug.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/actors/dependency.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/actors/errors.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/actors/functional.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/actors/helper.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/actors/interactions.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/actors/types.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/agents/__init__.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/agents/backend.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/agents/dataclasses.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/agents/hardware.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/agents/hooks/__init__.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/agents/hooks/background.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/agents/hooks/shutdown.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/agents/journal.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/agents/lock.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/agents/policy.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/agents/transport/__init__.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/agents/transport/base.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/agents/transport/types.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/calls.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/constants.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/invoke.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/local.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/messages.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/postmans/__init__.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/postmans/errors.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/postmans/types.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/qt/__init__.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/qt/builders.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/state/__init__.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/state/expand.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/state/gate.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/state/observable.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/state/readonly.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/state/shrink.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/state/write.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/structures/__init__.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/structures/serialization/__init__.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/structures/serialization/actor.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/structures/serialization/batching.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/structures/serialization/context.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/structures/serialization/expand.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/structures/serialization/memory.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/structures/serialization/port_errors.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/structures/serialization/postman.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/structures/serialization/predication.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/structures/serialization/shrink.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/task.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/task_scope.py +0 -0
- {arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/types.py +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: arkitekt-runtime
|
|
3
|
-
Version: 7.
|
|
3
|
+
Version: 7.3.0
|
|
4
4
|
Summary: the execution core of an Arkitekt app: agent, actors, tasks, journal, state and structure serialization, shared by every runtime
|
|
5
5
|
Author-email: jhnnsrs <jhnnsrs@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
Requires-Python: >=3.11
|
|
8
|
-
Requires-Dist: arkitekt-spec>=6.
|
|
8
|
+
Requires-Dist: arkitekt-spec>=6.3.1
|
|
9
9
|
Requires-Dist: janus>=2.0.0
|
|
10
10
|
Requires-Dist: jsonpatch>=1.33
|
|
11
11
|
Requires-Dist: koil>=3.3.6
|
|
@@ -318,6 +318,8 @@ class BaseAgent(KoiledModel):
|
|
|
318
318
|
default_factory=lambda: {}
|
|
319
319
|
)
|
|
320
320
|
_ran_startup_hooks: bool = PrivateAttr(default=False)
|
|
321
|
+
_entered_lifecycles: list[Any] = PrivateAttr(default_factory=list)
|
|
322
|
+
"""The lifecycle hooks started in this run, in order: what follows their yield is owed."""
|
|
321
323
|
# A stopped run reaches teardown from several places (the loop, leaving the
|
|
322
324
|
# agent); it runs once per connect.
|
|
323
325
|
_torn_down: bool = PrivateAttr(default=False)
|
|
@@ -1824,6 +1826,8 @@ class BaseAgent(KoiledModel):
|
|
|
1824
1826
|
for key, hook in self._collected_startup_hooks.items():
|
|
1825
1827
|
try:
|
|
1826
1828
|
previous = await self._aprevious_state_kwargs(hook)
|
|
1829
|
+
if hasattr(hook, "afinish"):
|
|
1830
|
+
self._entered_lifecycles.append(hook)
|
|
1827
1831
|
answer = await asyncio.wait_for(
|
|
1828
1832
|
hook.arun(
|
|
1829
1833
|
app_context=app_context,
|
|
@@ -1887,6 +1891,25 @@ class BaseAgent(KoiledModel):
|
|
|
1887
1891
|
hook_error.__cause__ = e
|
|
1888
1892
|
logger.error(hook_error, exc_info=hook_error)
|
|
1889
1893
|
|
|
1894
|
+
# After the shutdown hooks: they may take what these set up.
|
|
1895
|
+
await self._afinish_lifecycles()
|
|
1896
|
+
|
|
1897
|
+
async def _afinish_lifecycles(self) -> None:
|
|
1898
|
+
"""Run what follows each lifecycle hook's yield, the last one entered first.
|
|
1899
|
+
|
|
1900
|
+
Like a shutdown hook, one that fails or times out is logged, and the rest still run.
|
|
1901
|
+
"""
|
|
1902
|
+
from arkitekt_spec.declare.agents.hooks.errors import ShutdownHookError
|
|
1903
|
+
|
|
1904
|
+
entered, self._entered_lifecycles = self._entered_lifecycles, []
|
|
1905
|
+
for hook in reversed(entered):
|
|
1906
|
+
try:
|
|
1907
|
+
await asyncio.wait_for(hook.afinish(), timeout=self.shutdown_hook_timeout)
|
|
1908
|
+
except Exception as e:
|
|
1909
|
+
hook_error = ShutdownHookError(f"Lifecycle hook {hook.func.__name__} failed")
|
|
1910
|
+
hook_error.__cause__ = e
|
|
1911
|
+
logger.error(hook_error, exc_info=hook_error)
|
|
1912
|
+
|
|
1890
1913
|
@property
|
|
1891
1914
|
def registered_agent_id(self) -> str | None:
|
|
1892
1915
|
"""The id this agent's backend assigned it, once registered.
|
|
@@ -1928,7 +1951,13 @@ class BaseAgent(KoiledModel):
|
|
|
1928
1951
|
"""
|
|
1929
1952
|
# A state is built unrestricted by its startup hook; adopting it (in
|
|
1930
1953
|
# ainit_states) is what makes later changes need their locks.
|
|
1931
|
-
|
|
1954
|
+
try:
|
|
1955
|
+
hook_return = await self.arun_startup_hooks(app_context=app_context)
|
|
1956
|
+
except BaseException:
|
|
1957
|
+
# No shutdown hook runs for a start that failed here, but the lifecycle
|
|
1958
|
+
# hooks before the failing one are entered.
|
|
1959
|
+
await self._afinish_lifecycles()
|
|
1960
|
+
raise
|
|
1932
1961
|
# From here on the app has set up resources, so teardown owes it the
|
|
1933
1962
|
# shutdown hooks (even if the rest of the startup fails).
|
|
1934
1963
|
self._ran_startup_hooks = True
|
{arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/agents/hooks/materialize.py
RENAMED
|
@@ -4,6 +4,7 @@ from typing import Any
|
|
|
4
4
|
|
|
5
5
|
from arkitekt_spec.declare.agents.hooks.declare import (
|
|
6
6
|
BackgroundDeclaration,
|
|
7
|
+
LifecycleHookDeclaration,
|
|
7
8
|
ShutdownHookDeclaration,
|
|
8
9
|
StartupHookDeclaration,
|
|
9
10
|
)
|
|
@@ -12,7 +13,11 @@ from arkitekt_runtime.agents.hooks.background import (
|
|
|
12
13
|
WrappedThreadedBackgroundTask,
|
|
13
14
|
)
|
|
14
15
|
from arkitekt_runtime.agents.hooks.shutdown import ThreadedShutdownHook, WrappedShutdownHook
|
|
15
|
-
from arkitekt_runtime.agents.hooks.startup import
|
|
16
|
+
from arkitekt_runtime.agents.hooks.startup import (
|
|
17
|
+
ThreadedStartupHook,
|
|
18
|
+
WrappedLifecycleHook,
|
|
19
|
+
WrappedStartupHook,
|
|
20
|
+
)
|
|
16
21
|
|
|
17
22
|
|
|
18
23
|
def materialize_hook(declaration: Any) -> Any: # noqa: ANN401
|
|
@@ -24,7 +29,9 @@ def materialize_hook(declaration: Any) -> Any: # noqa: ANN401
|
|
|
24
29
|
if hasattr(declaration, "arun"):
|
|
25
30
|
return declaration
|
|
26
31
|
func, registry = declaration.func, declaration.structure_registry
|
|
27
|
-
if isinstance(declaration,
|
|
32
|
+
if isinstance(declaration, LifecycleHookDeclaration):
|
|
33
|
+
cls = WrappedLifecycleHook
|
|
34
|
+
elif isinstance(declaration, StartupHookDeclaration):
|
|
28
35
|
cls = ThreadedStartupHook if declaration.threaded else WrappedStartupHook
|
|
29
36
|
elif isinstance(declaration, BackgroundDeclaration):
|
|
30
37
|
cls = WrappedThreadedBackgroundTask if declaration.threaded else WrappedBackgroundTask
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
"""Hooks for the agent"""
|
|
2
2
|
|
|
3
|
+
import asyncio
|
|
3
4
|
from typing import (
|
|
4
5
|
TYPE_CHECKING,
|
|
5
6
|
Any,
|
|
6
7
|
)
|
|
7
8
|
from arkitekt_spec.declare.agents.types import BoundApp
|
|
8
9
|
from koil.bridge import run_threaded
|
|
9
|
-
from arkitekt_spec.declare.agents.hooks.errors import StartupHookError
|
|
10
|
+
from arkitekt_spec.declare.agents.hooks.errors import ShutdownHookError, StartupHookError
|
|
10
11
|
from arkitekt_spec.declare.agents.hooks.registry import (
|
|
11
12
|
StartupHookReturns,
|
|
12
13
|
)
|
|
13
|
-
from arkitekt_spec.declare.agents.hooks.declare import
|
|
14
|
+
from arkitekt_spec.declare.agents.hooks.declare import (
|
|
15
|
+
LifecycleWithVariables,
|
|
16
|
+
StartupWithVariables,
|
|
17
|
+
)
|
|
14
18
|
|
|
15
19
|
if TYPE_CHECKING:
|
|
16
20
|
from arkitekt_spec.declare.structures.registry import StructureRegistry
|
|
@@ -118,4 +122,77 @@ class ThreadedStartupHook(StartupWithVariables):
|
|
|
118
122
|
return _split_returns(self, ensure_return_as_tuple(parsed_returns))
|
|
119
123
|
|
|
120
124
|
|
|
125
|
+
class WrappedLifecycleHook(LifecycleWithVariables):
|
|
126
|
+
"""A lifecycle hook, driven by a task of its own.
|
|
127
|
+
|
|
128
|
+
The agent starts and tears down in different tasks, and what an async generator enters
|
|
129
|
+
(a cancel scope, ``asyncio.timeout``) must be left in the task that entered it.
|
|
130
|
+
"""
|
|
131
|
+
|
|
132
|
+
def __init__(
|
|
133
|
+
self, func: Any, structure_registry: "StructureRegistry | None" = None # noqa: ANN401
|
|
134
|
+
) -> None:
|
|
135
|
+
super().__init__(func, structure_registry)
|
|
136
|
+
self._task: asyncio.Task[None] | None = None
|
|
137
|
+
self._stop = asyncio.Event()
|
|
138
|
+
|
|
139
|
+
async def arun(
|
|
140
|
+
self,
|
|
141
|
+
app_context: Any, # noqa: ANN401
|
|
142
|
+
bound_app: BoundApp | None = None,
|
|
143
|
+
previous_states: dict[str, Any] | None = None,
|
|
144
|
+
) -> StartupHookReturns:
|
|
145
|
+
"""Run the hook up to its yield, in its own task."""
|
|
146
|
+
kwargs = self.get_startup_kwargs(app_context, bound_app, previous_states)
|
|
147
|
+
entered: asyncio.Future[Any] = asyncio.get_running_loop().create_future()
|
|
148
|
+
self._stop = asyncio.Event()
|
|
149
|
+
self._task = asyncio.create_task(self._adrive(kwargs, entered))
|
|
150
|
+
try:
|
|
151
|
+
yielded = await entered
|
|
152
|
+
except BaseException:
|
|
153
|
+
# Its setup failed, or ran out of time: there is nothing left to finish.
|
|
154
|
+
task, self._task = self._task, None
|
|
155
|
+
task.cancel()
|
|
156
|
+
await asyncio.gather(task, return_exceptions=True)
|
|
157
|
+
raise
|
|
158
|
+
return _split_returns(self, ensure_return_as_tuple(yielded))
|
|
159
|
+
|
|
160
|
+
async def afinish(self) -> None:
|
|
161
|
+
"""Run what follows the yield. Nothing if the hook is not entered."""
|
|
162
|
+
task, self._task = self._task, None
|
|
163
|
+
if task is None:
|
|
164
|
+
return
|
|
165
|
+
self._stop.set()
|
|
166
|
+
await task
|
|
167
|
+
|
|
168
|
+
async def _adrive(self, kwargs: dict[str, Any], entered: "asyncio.Future[Any]") -> None:
|
|
169
|
+
name = self.func.__name__
|
|
170
|
+
generator = self.func(**kwargs)
|
|
171
|
+
try:
|
|
172
|
+
try:
|
|
173
|
+
yielded = await anext(generator)
|
|
174
|
+
except StopAsyncIteration:
|
|
175
|
+
if not entered.done():
|
|
176
|
+
entered.set_exception(StartupHookError(f"Lifecycle hook {name} did not yield"))
|
|
177
|
+
return
|
|
178
|
+
except asyncio.CancelledError:
|
|
179
|
+
entered.cancel()
|
|
180
|
+
raise
|
|
181
|
+
except Exception as e:
|
|
182
|
+
if not entered.done():
|
|
183
|
+
entered.set_exception(e)
|
|
184
|
+
return
|
|
185
|
+
if entered.done():
|
|
186
|
+
return
|
|
187
|
+
entered.set_result(yielded)
|
|
188
|
+
await self._stop.wait()
|
|
189
|
+
try:
|
|
190
|
+
await anext(generator)
|
|
191
|
+
except StopAsyncIteration:
|
|
192
|
+
return
|
|
193
|
+
raise ShutdownHookError(f"Lifecycle hook {name} yielded more than once")
|
|
194
|
+
finally:
|
|
195
|
+
await generator.aclose()
|
|
196
|
+
|
|
197
|
+
|
|
121
198
|
# --- Implementation ---
|
|
@@ -10,7 +10,7 @@ license = "MIT"
|
|
|
10
10
|
# server) executes actions the same way, so this is what they share. It carries no
|
|
11
11
|
# transport and no client: no websockets, no rath, no graphql-core.
|
|
12
12
|
dependencies = [
|
|
13
|
-
"arkitekt-spec>=6.
|
|
13
|
+
"arkitekt-spec>=6.3.1",
|
|
14
14
|
"pydantic>=2.11",
|
|
15
15
|
"koil>=3.3.6",
|
|
16
16
|
"janus>=2.0.0",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/agents/hooks/background.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/agents/transport/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/agents/transport/types.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{arkitekt_runtime-7.2.0 → arkitekt_runtime-7.3.0}/arkitekt_runtime/structures/serialization/actor.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|