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.
Files changed (87) hide show
  1. plexspaces/__init__.py +80 -0
  2. plexspaces/actor_id.py +114 -0
  3. plexspaces/decorators.py +691 -0
  4. plexspaces/generated/__init__.py +0 -0
  5. plexspaces/generated/buf/__init__.py +0 -0
  6. plexspaces/generated/buf/validate/__init__.py +3799 -0
  7. plexspaces/generated/google/__init__.py +0 -0
  8. plexspaces/generated/google/api/__init__.py +379 -0
  9. plexspaces/generated/grpc/__init__.py +0 -0
  10. plexspaces/generated/grpc/gateway/__init__.py +0 -0
  11. plexspaces/generated/grpc/gateway/protoc_gen_openapiv2/__init__.py +0 -0
  12. plexspaces/generated/grpc/gateway/protoc_gen_openapiv2/options/__init__.py +1022 -0
  13. plexspaces/generated/plexspaces/__init__.py +0 -0
  14. plexspaces/generated/plexspaces/actor/__init__.py +0 -0
  15. plexspaces/generated/plexspaces/actor/v1/__init__.py +3538 -0
  16. plexspaces/generated/plexspaces/application/__init__.py +0 -0
  17. plexspaces/generated/plexspaces/application/v1/__init__.py +827 -0
  18. plexspaces/generated/plexspaces/behaviors/__init__.py +0 -0
  19. plexspaces/generated/plexspaces/behaviors/v1/__init__.py +63 -0
  20. plexspaces/generated/plexspaces/channel/__init__.py +0 -0
  21. plexspaces/generated/plexspaces/channel/v1/__init__.py +915 -0
  22. plexspaces/generated/plexspaces/circuitbreaker/__init__.py +0 -0
  23. plexspaces/generated/plexspaces/circuitbreaker/prv/__init__.py +398 -0
  24. plexspaces/generated/plexspaces/common/__init__.py +0 -0
  25. plexspaces/generated/plexspaces/common/v1/__init__.py +691 -0
  26. plexspaces/generated/plexspaces/config/__init__.py +0 -0
  27. plexspaces/generated/plexspaces/config/v1/__init__.py +109 -0
  28. plexspaces/generated/plexspaces/dashboard/__init__.py +0 -0
  29. plexspaces/generated/plexspaces/dashboard/v1/__init__.py +647 -0
  30. plexspaces/generated/plexspaces/facets/__init__.py +0 -0
  31. plexspaces/generated/plexspaces/facets/v1/__init__.py +333 -0
  32. plexspaces/generated/plexspaces/firecracker/__init__.py +0 -0
  33. plexspaces/generated/plexspaces/firecracker/v1/__init__.py +890 -0
  34. plexspaces/generated/plexspaces/grpc/__init__.py +0 -0
  35. plexspaces/generated/plexspaces/grpc/v1/__init__.py +679 -0
  36. plexspaces/generated/plexspaces/journaling/__init__.py +0 -0
  37. plexspaces/generated/plexspaces/journaling/v1/__init__.py +1006 -0
  38. plexspaces/generated/plexspaces/keyvalue/__init__.py +0 -0
  39. plexspaces/generated/plexspaces/keyvalue/v1/__init__.py +1007 -0
  40. plexspaces/generated/plexspaces/locks/__init__.py +0 -0
  41. plexspaces/generated/plexspaces/locks/prv/__init__.py +122 -0
  42. plexspaces/generated/plexspaces/mailbox/__init__.py +0 -0
  43. plexspaces/generated/plexspaces/mailbox/v1/__init__.py +246 -0
  44. plexspaces/generated/plexspaces/metrics/__init__.py +0 -0
  45. plexspaces/generated/plexspaces/metrics/v1/__init__.py +878 -0
  46. plexspaces/generated/plexspaces/node/__init__.py +0 -0
  47. plexspaces/generated/plexspaces/node/v1/__init__.py +2564 -0
  48. plexspaces/generated/plexspaces/object_registry/__init__.py +0 -0
  49. plexspaces/generated/plexspaces/object_registry/v1/__init__.py +759 -0
  50. plexspaces/generated/plexspaces/persistence/__init__.py +0 -0
  51. plexspaces/generated/plexspaces/persistence/prv/__init__.py +539 -0
  52. plexspaces/generated/plexspaces/pool/__init__.py +0 -0
  53. plexspaces/generated/plexspaces/pool/v1/__init__.py +791 -0
  54. plexspaces/generated/plexspaces/processgroups/__init__.py +0 -0
  55. plexspaces/generated/plexspaces/processgroups/v1/__init__.py +622 -0
  56. plexspaces/generated/plexspaces/scheduling/__init__.py +0 -0
  57. plexspaces/generated/plexspaces/scheduling/v1/__init__.py +364 -0
  58. plexspaces/generated/plexspaces/security/__init__.py +0 -0
  59. plexspaces/generated/plexspaces/security/v1/__init__.py +254 -0
  60. plexspaces/generated/plexspaces/services/__init__.py +0 -0
  61. plexspaces/generated/plexspaces/services/prv/__init__.py +112 -0
  62. plexspaces/generated/plexspaces/storage/__init__.py +0 -0
  63. plexspaces/generated/plexspaces/storage/v1/__init__.py +747 -0
  64. plexspaces/generated/plexspaces/supervision/__init__.py +0 -0
  65. plexspaces/generated/plexspaces/supervision/v1/__init__.py +536 -0
  66. plexspaces/generated/plexspaces/system/__init__.py +0 -0
  67. plexspaces/generated/plexspaces/system/v1/__init__.py +1602 -0
  68. plexspaces/generated/plexspaces/timer/__init__.py +0 -0
  69. plexspaces/generated/plexspaces/timer/v1/__init__.py +402 -0
  70. plexspaces/generated/plexspaces/tuplespace/__init__.py +0 -0
  71. plexspaces/generated/plexspaces/tuplespace/v1/__init__.py +869 -0
  72. plexspaces/generated/plexspaces/wasm/__init__.py +0 -0
  73. plexspaces/generated/plexspaces/wasm/v1/__init__.py +920 -0
  74. plexspaces/generated/plexspaces/workflow/__init__.py +0 -0
  75. plexspaces/generated/plexspaces/workflow/v1/__init__.py +870 -0
  76. plexspaces/host.py +2093 -0
  77. plexspaces/leader_worker.py +148 -0
  78. plexspaces/proto_wire.py +1357 -0
  79. plexspaces/runtime.py +284 -0
  80. plexspaces/workflow.py +105 -0
  81. plexspaces-0.1.1.dist-info/METADATA +505 -0
  82. plexspaces-0.1.1.dist-info/RECORD +87 -0
  83. plexspaces-0.1.1.dist-info/WHEEL +5 -0
  84. plexspaces-0.1.1.dist-info/entry_points.txt +2 -0
  85. plexspaces-0.1.1.dist-info/top_level.txt +2 -0
  86. plexspaces_cli/__init__.py +8 -0
  87. 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)