plexspaces 0.1.1__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.
- plexspaces/__init__.py +80 -0
- plexspaces/actor_id.py +114 -0
- plexspaces/decorators.py +691 -0
- plexspaces/generated/__init__.py +0 -0
- plexspaces/generated/buf/__init__.py +0 -0
- plexspaces/generated/buf/validate/__init__.py +3799 -0
- plexspaces/generated/google/__init__.py +0 -0
- plexspaces/generated/google/api/__init__.py +379 -0
- plexspaces/generated/grpc/__init__.py +0 -0
- plexspaces/generated/grpc/gateway/__init__.py +0 -0
- plexspaces/generated/grpc/gateway/protoc_gen_openapiv2/__init__.py +0 -0
- plexspaces/generated/grpc/gateway/protoc_gen_openapiv2/options/__init__.py +1022 -0
- plexspaces/generated/plexspaces/__init__.py +0 -0
- plexspaces/generated/plexspaces/actor/__init__.py +0 -0
- plexspaces/generated/plexspaces/actor/v1/__init__.py +3538 -0
- plexspaces/generated/plexspaces/application/__init__.py +0 -0
- plexspaces/generated/plexspaces/application/v1/__init__.py +827 -0
- plexspaces/generated/plexspaces/behaviors/__init__.py +0 -0
- plexspaces/generated/plexspaces/behaviors/v1/__init__.py +63 -0
- plexspaces/generated/plexspaces/channel/__init__.py +0 -0
- plexspaces/generated/plexspaces/channel/v1/__init__.py +915 -0
- plexspaces/generated/plexspaces/circuitbreaker/__init__.py +0 -0
- plexspaces/generated/plexspaces/circuitbreaker/prv/__init__.py +398 -0
- plexspaces/generated/plexspaces/common/__init__.py +0 -0
- plexspaces/generated/plexspaces/common/v1/__init__.py +691 -0
- plexspaces/generated/plexspaces/config/__init__.py +0 -0
- plexspaces/generated/plexspaces/config/v1/__init__.py +109 -0
- plexspaces/generated/plexspaces/dashboard/__init__.py +0 -0
- plexspaces/generated/plexspaces/dashboard/v1/__init__.py +647 -0
- plexspaces/generated/plexspaces/facets/__init__.py +0 -0
- plexspaces/generated/plexspaces/facets/v1/__init__.py +333 -0
- plexspaces/generated/plexspaces/firecracker/__init__.py +0 -0
- plexspaces/generated/plexspaces/firecracker/v1/__init__.py +890 -0
- plexspaces/generated/plexspaces/grpc/__init__.py +0 -0
- plexspaces/generated/plexspaces/grpc/v1/__init__.py +679 -0
- plexspaces/generated/plexspaces/journaling/__init__.py +0 -0
- plexspaces/generated/plexspaces/journaling/v1/__init__.py +1006 -0
- plexspaces/generated/plexspaces/keyvalue/__init__.py +0 -0
- plexspaces/generated/plexspaces/keyvalue/v1/__init__.py +1007 -0
- plexspaces/generated/plexspaces/locks/__init__.py +0 -0
- plexspaces/generated/plexspaces/locks/prv/__init__.py +122 -0
- plexspaces/generated/plexspaces/mailbox/__init__.py +0 -0
- plexspaces/generated/plexspaces/mailbox/v1/__init__.py +246 -0
- plexspaces/generated/plexspaces/metrics/__init__.py +0 -0
- plexspaces/generated/plexspaces/metrics/v1/__init__.py +878 -0
- plexspaces/generated/plexspaces/node/__init__.py +0 -0
- plexspaces/generated/plexspaces/node/v1/__init__.py +2564 -0
- plexspaces/generated/plexspaces/object_registry/__init__.py +0 -0
- plexspaces/generated/plexspaces/object_registry/v1/__init__.py +759 -0
- plexspaces/generated/plexspaces/persistence/__init__.py +0 -0
- plexspaces/generated/plexspaces/persistence/prv/__init__.py +539 -0
- plexspaces/generated/plexspaces/pool/__init__.py +0 -0
- plexspaces/generated/plexspaces/pool/v1/__init__.py +791 -0
- plexspaces/generated/plexspaces/processgroups/__init__.py +0 -0
- plexspaces/generated/plexspaces/processgroups/v1/__init__.py +622 -0
- plexspaces/generated/plexspaces/scheduling/__init__.py +0 -0
- plexspaces/generated/plexspaces/scheduling/v1/__init__.py +364 -0
- plexspaces/generated/plexspaces/security/__init__.py +0 -0
- plexspaces/generated/plexspaces/security/v1/__init__.py +254 -0
- plexspaces/generated/plexspaces/services/__init__.py +0 -0
- plexspaces/generated/plexspaces/services/prv/__init__.py +112 -0
- plexspaces/generated/plexspaces/storage/__init__.py +0 -0
- plexspaces/generated/plexspaces/storage/v1/__init__.py +747 -0
- plexspaces/generated/plexspaces/supervision/__init__.py +0 -0
- plexspaces/generated/plexspaces/supervision/v1/__init__.py +536 -0
- plexspaces/generated/plexspaces/system/__init__.py +0 -0
- plexspaces/generated/plexspaces/system/v1/__init__.py +1602 -0
- plexspaces/generated/plexspaces/timer/__init__.py +0 -0
- plexspaces/generated/plexspaces/timer/v1/__init__.py +402 -0
- plexspaces/generated/plexspaces/tuplespace/__init__.py +0 -0
- plexspaces/generated/plexspaces/tuplespace/v1/__init__.py +869 -0
- plexspaces/generated/plexspaces/wasm/__init__.py +0 -0
- plexspaces/generated/plexspaces/wasm/v1/__init__.py +920 -0
- plexspaces/generated/plexspaces/workflow/__init__.py +0 -0
- plexspaces/generated/plexspaces/workflow/v1/__init__.py +870 -0
- plexspaces/host.py +2093 -0
- plexspaces/leader_worker.py +148 -0
- plexspaces/proto_wire.py +1357 -0
- plexspaces/runtime.py +284 -0
- plexspaces/workflow.py +105 -0
- plexspaces-0.1.1.dist-info/METADATA +505 -0
- plexspaces-0.1.1.dist-info/RECORD +87 -0
- plexspaces-0.1.1.dist-info/WHEEL +5 -0
- plexspaces-0.1.1.dist-info/entry_points.txt +2 -0
- plexspaces-0.1.1.dist-info/top_level.txt +2 -0
- plexspaces_cli/__init__.py +8 -0
- plexspaces_cli/build.py +374 -0
plexspaces/__init__.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
# Copyright (C) 2025 PlexSpaces Contributors
|
|
3
|
+
#
|
|
4
|
+
# PlexSpaces Python SDK
|
|
5
|
+
#
|
|
6
|
+
# Provides a Pythonic interface for building PlexSpaces actors with minimal boilerplate.
|
|
7
|
+
# Inspired by Ray's @ray.remote and Temporal's @workflow.defn decorators.
|
|
8
|
+
#
|
|
9
|
+
# Example:
|
|
10
|
+
# from plexspaces import actor, event_actor, state, handler
|
|
11
|
+
#
|
|
12
|
+
# @event_actor # GenEvent-style (fire-and-forget)
|
|
13
|
+
# class SensorStream:
|
|
14
|
+
# @handler("ingest")
|
|
15
|
+
# def ingest(self, sensor_id: str, value: str) -> str: ...
|
|
16
|
+
#
|
|
17
|
+
# @actor # GenServer-style (request-reply)
|
|
18
|
+
# class BankAccount:
|
|
19
|
+
# balance: int = state(default=0)
|
|
20
|
+
#
|
|
21
|
+
# @handler("deposit")
|
|
22
|
+
# def deposit(self, amount: int) -> dict:
|
|
23
|
+
# self.balance += amount
|
|
24
|
+
# return {"balance": self.balance}
|
|
25
|
+
#
|
|
26
|
+
# Build with:
|
|
27
|
+
# plexspaces-py build bank_account.py -o bank_account_actor.wasm
|
|
28
|
+
|
|
29
|
+
"""
|
|
30
|
+
PlexSpaces Python SDK - Build actors with minimal boilerplate.
|
|
31
|
+
|
|
32
|
+
This SDK provides:
|
|
33
|
+
- @actor: Decorator to define a PlexSpaces actor class
|
|
34
|
+
- @handler: Decorator to define message handlers
|
|
35
|
+
- state(): Define persistent state fields (auto-serialized)
|
|
36
|
+
- host: Access to PlexSpaces host functions (send, log, process_groups, etc.)
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
__version__ = "0.1.0"
|
|
40
|
+
|
|
41
|
+
from .decorators import (
|
|
42
|
+
actor,
|
|
43
|
+
event_actor,
|
|
44
|
+
gen_server_actor,
|
|
45
|
+
fsm_actor,
|
|
46
|
+
workflow_actor,
|
|
47
|
+
handler,
|
|
48
|
+
state,
|
|
49
|
+
init_handler,
|
|
50
|
+
run_handler,
|
|
51
|
+
signal_handler,
|
|
52
|
+
query_handler,
|
|
53
|
+
)
|
|
54
|
+
from .host import host, pg_first, ServiceHttpClient
|
|
55
|
+
from .workflow import default_retry_config, with_retry
|
|
56
|
+
from .leader_worker import LeaderWorkerClient, list_worker_node_ids
|
|
57
|
+
from .actor_id import ActorID
|
|
58
|
+
|
|
59
|
+
__all__ = [
|
|
60
|
+
"ActorID",
|
|
61
|
+
"actor",
|
|
62
|
+
"default_retry_config",
|
|
63
|
+
"event_actor",
|
|
64
|
+
"gen_server_actor",
|
|
65
|
+
"fsm_actor",
|
|
66
|
+
"workflow_actor",
|
|
67
|
+
"handler",
|
|
68
|
+
"state",
|
|
69
|
+
"init_handler",
|
|
70
|
+
"run_handler",
|
|
71
|
+
"signal_handler",
|
|
72
|
+
"query_handler",
|
|
73
|
+
"host",
|
|
74
|
+
"pg_first",
|
|
75
|
+
"ServiceHttpClient",
|
|
76
|
+
"with_retry",
|
|
77
|
+
"LeaderWorkerClient",
|
|
78
|
+
"list_worker_node_ids",
|
|
79
|
+
"__version__",
|
|
80
|
+
]
|
plexspaces/actor_id.py
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
# Copyright (C) 2025 PlexSpaces Contributors
|
|
3
|
+
#
|
|
4
|
+
# ActorID — parsed representation of a canonical PlexSpaces actor ID.
|
|
5
|
+
#
|
|
6
|
+
# Canonical format: {name}//{actor_type}::{namespace}@{node_id}
|
|
7
|
+
#
|
|
8
|
+
# The name is typically a stable role name for supervisor-spawned actors,
|
|
9
|
+
# or a ULID for dynamically spawned actors.
|
|
10
|
+
#
|
|
11
|
+
# Usage — peer/sibling discovery from within a WASM actor:
|
|
12
|
+
#
|
|
13
|
+
# self_id = ActorID.parse(host.self_id())
|
|
14
|
+
# budget_id = self_id.sibling("budget_manager")
|
|
15
|
+
# reply = host.ask(budget_id.to_str(), payload, 5000)
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
from dataclasses import dataclass
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class ActorID:
|
|
23
|
+
"""Parsed form of a canonical PlexSpaces actor ID.
|
|
24
|
+
|
|
25
|
+
Canonical string format: {name}//{actor_type}::{namespace}@{node_id}
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
name: str
|
|
29
|
+
"""Unique instance identifier (role name or ULID)."""
|
|
30
|
+
|
|
31
|
+
actor_type: str
|
|
32
|
+
"""Behavior type registered in the application (e.g. 'budget_manager')."""
|
|
33
|
+
|
|
34
|
+
namespace: str
|
|
35
|
+
"""Application namespace (e.g. 'go-resource-aware-inference')."""
|
|
36
|
+
|
|
37
|
+
node_id: str
|
|
38
|
+
"""Node hosting the actor (e.g. 'test-node-8091'). May be empty."""
|
|
39
|
+
|
|
40
|
+
@staticmethod
|
|
41
|
+
def parse(id: str) -> "ActorID":
|
|
42
|
+
"""Parse a canonical actor ID string into an ActorID.
|
|
43
|
+
|
|
44
|
+
Expected format: {name}//{actor_type}::{namespace}@{node_id}
|
|
45
|
+
|
|
46
|
+
Raises ValueError if the string does not contain the expected separators.
|
|
47
|
+
"""
|
|
48
|
+
slash_idx = id.find("//")
|
|
49
|
+
if slash_idx < 0:
|
|
50
|
+
raise ValueError(f"parse_actor_id: missing '//' in {id!r}")
|
|
51
|
+
|
|
52
|
+
name = id[:slash_idx]
|
|
53
|
+
rest = id[slash_idx + 2:] # "{actor_type}::{namespace}@{node_id}"
|
|
54
|
+
|
|
55
|
+
# Split on "@" to separate type::namespace from node_id
|
|
56
|
+
at_parts = rest.split("@", 1)
|
|
57
|
+
node_id = at_parts[1] if len(at_parts) == 2 else ""
|
|
58
|
+
type_ns = at_parts[0] # "{actor_type}::{namespace}"
|
|
59
|
+
|
|
60
|
+
# Split on "::" to separate actor_type from namespace
|
|
61
|
+
colon_parts = type_ns.split("::", 1)
|
|
62
|
+
actor_type = colon_parts[0]
|
|
63
|
+
namespace = colon_parts[1] if len(colon_parts) == 2 else ""
|
|
64
|
+
|
|
65
|
+
return ActorID(name=name, actor_type=actor_type, namespace=namespace, node_id=node_id)
|
|
66
|
+
|
|
67
|
+
def to_str(self) -> str:
|
|
68
|
+
"""Return the canonical actor ID string: {name}//{actor_type}::{namespace}@{node_id}."""
|
|
69
|
+
if self.node_id:
|
|
70
|
+
return f"{self.name}//{self.actor_type}::{self.namespace}@{self.node_id}"
|
|
71
|
+
return f"{self.name}//{self.actor_type}::{self.namespace}"
|
|
72
|
+
|
|
73
|
+
def __str__(self) -> str:
|
|
74
|
+
return self.to_str()
|
|
75
|
+
|
|
76
|
+
def sibling(self, name: str, actor_type: str = "") -> "ActorID":
|
|
77
|
+
"""Return a canonical ID for a same-application sibling actor.
|
|
78
|
+
|
|
79
|
+
For supervisor-spawned actors where name == type (the common case)::
|
|
80
|
+
|
|
81
|
+
peer = self_id.sibling("inference_worker_a", "inference_worker")
|
|
82
|
+
# -> "inference_worker_a//inference_worker::namespace@node"
|
|
83
|
+
|
|
84
|
+
If actor_type is omitted, name is used as the type::
|
|
85
|
+
|
|
86
|
+
peer = self_id.sibling("pipeline_supervisor")
|
|
87
|
+
# -> "pipeline_supervisor//pipeline_supervisor::namespace@node"
|
|
88
|
+
"""
|
|
89
|
+
t = actor_type if actor_type else name
|
|
90
|
+
return ActorID(name=name, actor_type=t, namespace=self.namespace, node_id=self.node_id)
|
|
91
|
+
|
|
92
|
+
def with_type_and_name(self, actor_type: str, name: str) -> "ActorID":
|
|
93
|
+
"""Return a copy with an explicit actor_type and name.
|
|
94
|
+
|
|
95
|
+
Use this to build a canonical ID for a peer actor with the given type and name,
|
|
96
|
+
keeping the same namespace and node.
|
|
97
|
+
|
|
98
|
+
For supervisor-spawned actors with stable role names (name == type == role)::
|
|
99
|
+
|
|
100
|
+
peer = self.with_type_and_name("budget_manager", "budget_manager")
|
|
101
|
+
|
|
102
|
+
For actors where name and type differ (e.g. ULID-named workers of a shared type)::
|
|
103
|
+
|
|
104
|
+
peer = self.with_type_and_name("inference_worker", ulid)
|
|
105
|
+
"""
|
|
106
|
+
return ActorID(name=name, actor_type=actor_type, namespace=self.namespace, node_id=self.node_id)
|
|
107
|
+
|
|
108
|
+
def with_name(self, name: str) -> "ActorID":
|
|
109
|
+
"""Return a copy with a different name."""
|
|
110
|
+
return ActorID(name=name, actor_type=self.actor_type, namespace=self.namespace, node_id=self.node_id)
|
|
111
|
+
|
|
112
|
+
def with_type(self, name: str, actor_type: str) -> "ActorID":
|
|
113
|
+
"""Return a copy with a different name and actor_type."""
|
|
114
|
+
return ActorID(name=name, actor_type=actor_type, namespace=self.namespace, node_id=self.node_id)
|