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
@@ -0,0 +1,3538 @@
1
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
2
+ # sources: plexspaces/v1/actors/actor_runtime.proto, plexspaces/v1/actors/types.proto
3
+ # plugin: python-betterproto
4
+ # This file has been @generated
5
+
6
+ from dataclasses import dataclass
7
+ from datetime import (
8
+ datetime,
9
+ timedelta,
10
+ )
11
+ from typing import (
12
+ TYPE_CHECKING,
13
+ AsyncIterable,
14
+ AsyncIterator,
15
+ Dict,
16
+ Iterable,
17
+ List,
18
+ Optional,
19
+ Union,
20
+ )
21
+
22
+ import betterproto
23
+ import betterproto.lib.google.protobuf as betterproto_lib_google_protobuf
24
+ import grpclib
25
+ from betterproto.grpc.grpclib_server import ServiceBase
26
+
27
+ from ...common import v1 as __common_v1__
28
+ from ...supervision import v1 as __supervision_v1__
29
+
30
+ if TYPE_CHECKING:
31
+ import grpclib.server
32
+ from betterproto.grpc.grpclib_client import MetadataLike
33
+ from grpclib.metadata import Deadline
34
+
35
+
36
+ class ActorState(betterproto.Enum):
37
+ """
38
+ Actor lifecycle states.
39
+
40
+ ## Purpose
41
+ Defines the state machine for actor lifecycle management and supervision.
42
+
43
+ ## Why This Exists
44
+ - Enables supervision to track actor health and make restart decisions
45
+ - Supports graceful activation/deactivation (Orbit-inspired auto-activation)
46
+ - Allows migration state tracking for mobile agents
47
+ - Provides clear state transitions for debugging and monitoring
48
+
49
+ ## State Transitions
50
+ ```
51
+ CREATING -> ACTIVATING -> ACTIVE -> DEACTIVATING -> INACTIVE
52
+ \-> MIGRATING -> ACTIVE (on new node)
53
+ \-> FAILED -> (supervisor restarts)
54
+ \-> TERMINATED (permanent stop)
55
+ ```
56
+
57
+ ## Design Notes
58
+ - UNSPECIFIED should never occur in valid states (indicates error)
59
+ - ACTIVATING/DEACTIVATING enable graceful lifecycle management
60
+ - MIGRATING supports actor mobility between nodes
61
+ - FAILED triggers supervisor restart policies
62
+ - FAILED state includes error message for debugging (see Actor message for error details)
63
+ """
64
+
65
+ UNSPECIFIED = 0
66
+ CREATING = 1
67
+ ACTIVE = 2
68
+ INACTIVE = 3
69
+ ACTIVATING = 4
70
+ DEACTIVATING = 5
71
+ STOPPING = 6
72
+ MIGRATING = 7
73
+ FAILED = 8
74
+ TERMINATED = 9
75
+
76
+
77
+ class ExitReason(betterproto.Enum):
78
+ """
79
+ Exit reason for actor termination (Erlang/OTP-style)
80
+
81
+ ## Purpose
82
+ Defines why an actor terminated, enabling proper supervision decisions
83
+ and link/monitor propagation semantics.
84
+
85
+ ## Erlang Equivalent
86
+ Maps to Erlang's exit reasons:
87
+ - normal: Normal termination (not an error)
88
+ - shutdown: Graceful shutdown requested
89
+ - kill: Forcefully killed
90
+ - {error, Reason}: Error with message
91
+ - {linked, Pid, Reason}: Linked actor died
92
+
93
+ ## Usage
94
+ - Used in Actor::terminate() and Actor::handle_exit() lifecycle hooks
95
+ - Propagated to linked actors (if trap_exit=false, causes cascading crash)
96
+ - Used by supervisors to decide restart policies
97
+ """
98
+
99
+ UNSPECIFIED = 0
100
+ NORMAL = 1
101
+ SHUTDOWN = 2
102
+ KILLED = 3
103
+ ERROR = 4
104
+ LINKED = 5
105
+
106
+
107
+ class ExitAction(betterproto.Enum):
108
+ """
109
+ Exit action when handling EXIT from linked actor
110
+
111
+ ## Purpose
112
+ Determines how an actor responds to EXIT signal from a linked actor.
113
+ Only relevant when ActorContext.trap_exit = true.
114
+
115
+ ## Erlang Equivalent
116
+ Maps to Erlang's handle_exit behavior:
117
+ - Propagate: Actor will also terminate (default link behavior)
118
+ - Handle: Actor continues running (traps the exit)
119
+ """
120
+
121
+ UNSPECIFIED = 0
122
+ PROPAGATE = 1
123
+ HANDLE = 2
124
+
125
+
126
+ class ActorErrorCode(betterproto.Enum):
127
+ ACTOR_ERROR_UNSPECIFIED = 0
128
+ ACTOR_ERROR_NOT_FOUND = 1
129
+ ACTOR_ERROR_ALREADY_EXISTS = 2
130
+ ACTOR_ERROR_INVALID_STATE = 3
131
+ ACTOR_ERROR_TIMEOUT = 4
132
+ ACTOR_ERROR_TERMINATED = 5
133
+ ACTOR_ERROR_FACET_ERROR = 6
134
+ ACTOR_ERROR_MAILBOX_ERROR = 7
135
+ ACTOR_ERROR_BEHAVIOR_ERROR = 8
136
+ ACTOR_ERROR_JOURNAL_ERROR = 9
137
+ ACTOR_ERROR_PLACEMENT_CONFLICT = 10
138
+
139
+
140
+ class BehaviorErrorCode(betterproto.Enum):
141
+ BEHAVIOR_ERROR_UNSPECIFIED = 0
142
+ BEHAVIOR_ERROR_HANDLER_NOT_FOUND = 1
143
+ BEHAVIOR_ERROR_UNSUPPORTED_MESSAGE = 2
144
+ BEHAVIOR_ERROR_TRANSITION_FAILED = 3
145
+ BEHAVIOR_ERROR_PROCESSING_ERROR = 4
146
+
147
+
148
+ class ActorIdErrorCode(betterproto.Enum):
149
+ ACTOR_ID_ERROR_UNSPECIFIED = 0
150
+ ACTOR_ID_ERROR_INVALID_NAME = 1
151
+ ACTOR_ID_ERROR_INVALID_ACTOR_TYPE = 2
152
+ ACTOR_ID_ERROR_MISSING_FIELD = 3
153
+ ACTOR_ID_ERROR_INVALID_FORMAT = 4
154
+
155
+
156
+ class SupervisorErrorCode(betterproto.Enum):
157
+ SUPERVISOR_ERROR_UNSPECIFIED = 0
158
+ SUPERVISOR_ERROR_CHILD_START_FAILED = 1
159
+ SUPERVISOR_ERROR_MAX_RESTARTS_EXCEEDED = 2
160
+ SUPERVISOR_ERROR_CHILD_NOT_FOUND = 3
161
+ SUPERVISOR_ERROR_SHUTDOWN_TIMEOUT = 4
162
+ SUPERVISOR_ERROR_CONFIG_ERROR = 5
163
+
164
+
165
+ class GenServerErrorCode(betterproto.Enum):
166
+ GEN_SERVER_ERROR_UNSPECIFIED = 0
167
+ GEN_SERVER_ERROR_SERIALIZATION = 1
168
+ GEN_SERVER_ERROR_CALL = 2
169
+ GEN_SERVER_ERROR_CAST = 3
170
+ GEN_SERVER_ERROR_SPAWN = 4
171
+
172
+
173
+ class FsmErrorCode(betterproto.Enum):
174
+ FSM_ERROR_UNSPECIFIED = 0
175
+ FSM_ERROR_SERIALIZATION = 1
176
+ FSM_ERROR_TRANSITION = 2
177
+ FSM_ERROR_QUERY = 3
178
+ FSM_ERROR_SPAWN = 4
179
+
180
+
181
+ class EventErrorCode(betterproto.Enum):
182
+ EVENT_ERROR_UNSPECIFIED = 0
183
+ EVENT_ERROR_SERIALIZATION = 1
184
+ EVENT_ERROR_EMIT = 2
185
+ EVENT_ERROR_SPAWN = 3
186
+
187
+
188
+ class WorkflowRefErrorCode(betterproto.Enum):
189
+ WORKFLOW_REF_ERROR_UNSPECIFIED = 0
190
+ WORKFLOW_REF_ERROR_SERIALIZATION = 1
191
+ WORKFLOW_REF_ERROR_EXECUTION = 2
192
+ WORKFLOW_REF_ERROR_SPAWN = 3
193
+
194
+
195
+ class VirtualActorErrorCode(betterproto.Enum):
196
+ VIRTUAL_ACTOR_ERROR_UNSPECIFIED = 0
197
+ VIRTUAL_ACTOR_ERROR_NOT_FOUND = 1
198
+ VIRTUAL_ACTOR_ERROR_ACTIVATION_FAILED = 2
199
+
200
+
201
+ class PoolServiceErrorCode(betterproto.Enum):
202
+ POOL_SERVICE_ERROR_UNSPECIFIED = 0
203
+ POOL_SERVICE_ERROR_POOL_NOT_FOUND = 1
204
+ POOL_SERVICE_ERROR_CHECKOUT_TIMEOUT = 2
205
+ POOL_SERVICE_ERROR_POOL_EXHAUSTED = 3
206
+ POOL_SERVICE_ERROR_CIRCUIT_OPEN = 4
207
+ POOL_SERVICE_ERROR_POOL_DRAINING = 5
208
+ POOL_SERVICE_ERROR_INVALID_CONFIG = 6
209
+ POOL_SERVICE_ERROR_ACTOR_ERROR = 7
210
+
211
+
212
+ class PlacementStrategy(betterproto.Enum):
213
+ """Placement strategy for actor activation (Orleans-inspired)"""
214
+
215
+ UNSPECIFIED = 0
216
+ RANDOM = 1
217
+ PREFER_LOCAL = 2
218
+ LOAD_BASED = 3
219
+ RESOURCE_BASED = 4
220
+ AFFINITY = 5
221
+ CUSTOM = 99
222
+
223
+
224
+ class LoadBalancingStrategy(betterproto.Enum):
225
+ LOAD_BALANCING_UNSPECIFIED = 0
226
+ LOAD_BALANCING_ROUND_ROBIN = 1
227
+ LOAD_BALANCING_LEAST_LOADED = 2
228
+ LOAD_BALANCING_RANDOM = 3
229
+
230
+
231
+ class PartitionStrategy(betterproto.Enum):
232
+ """Partition strategy for data-parallel actors"""
233
+
234
+ UNSPECIFIED = 0
235
+ HASH = 1
236
+ RANGE = 2
237
+ CONSISTENT_HASH = 3
238
+ CUSTOM = 99
239
+
240
+
241
+ class RebalancePolicy(betterproto.Enum):
242
+ """Rebalancing policy for data-parallel actors"""
243
+
244
+ UNSPECIFIED = 0
245
+ NONE = 1
246
+ ON_SCALE = 2
247
+ LOAD_BASED = 3
248
+
249
+
250
+ class ShardGroupState(betterproto.Enum):
251
+ UNSPECIFIED = 0
252
+ CREATING = 1
253
+ ACTIVE = 2
254
+ REBALANCING = 3
255
+ DRAINING = 4
256
+ STOPPING = 5
257
+ STOPPED = 6
258
+
259
+
260
+ class NodePlacementStrategy(betterproto.Enum):
261
+ """
262
+ Node placement strategy for ShardGroup creation (leader-worker multi-node).
263
+ When absent or SAME_NODE, all shards are created on the node that receives the RPC.
264
+ FROM_REGISTRY uses NodeRegistry.list_nodes (optional cluster filter); NODE_IDS uses explicit list.
265
+ """
266
+
267
+ UNSPECIFIED = 0
268
+ SAME_NODE = 1
269
+ FROM_REGISTRY = 2
270
+ NODE_IDS = 3
271
+
272
+
273
+ class ShardGroupAggregationStrategy(betterproto.Enum):
274
+ """Aggregation strategy for scatter-gather results"""
275
+
276
+ SHARD_GROUP_AGGREGATION_UNSPECIFIED = 0
277
+ SHARD_GROUP_AGGREGATION_CONCAT = 1
278
+ SHARD_GROUP_AGGREGATION_MERGE = 2
279
+ SHARD_GROUP_AGGREGATION_FIRST = 3
280
+ SHARD_GROUP_AGGREGATION_MAJORITY = 4
281
+
282
+
283
+ class CollectiveReduction(betterproto.Enum):
284
+ UNSPECIFIED = 0
285
+ SUM = 1
286
+ MIN = 2
287
+ MAX = 3
288
+ PRODUCT = 4
289
+ CONCAT = 5
290
+ BOOL_AND = 6
291
+ BOOL_OR = 7
292
+
293
+
294
+ class StateMgmtMode(betterproto.Enum):
295
+ """State management mode (lattice-based data-parallel actors)"""
296
+
297
+ UNSPECIFIED = 0
298
+ TRADITIONAL = 1
299
+ LATTICE = 2
300
+
301
+
302
+ class ConsistencyLevel(betterproto.Enum):
303
+ """Consistency level for lattice-based actors"""
304
+
305
+ UNSPECIFIED = 0
306
+ EVENTUAL = 1
307
+ CAUSAL = 2
308
+ READ_COMMITTED = 3
309
+ LINEARIZABLE = 4
310
+
311
+
312
+ class ActorVisibility(betterproto.Enum):
313
+ """
314
+ Request to spawn an actor on a specific remote node (Erlang spawn/4 equivalent)
315
+
316
+ ## Purpose
317
+ Spawns an actor on a specified remote node using pre-deployed actor type.
318
+ Returns an ActorRef for location-transparent messaging.
319
+
320
+ ## Erlang Philosophy
321
+ In Erlang:
322
+ ```erlang
323
+ % Local spawn (current node)
324
+ Pid = spawn(Module, Function, Args)
325
+
326
+ % Remote spawn (specific node)
327
+ Pid = spawn(Node, Module, Function, Args)
328
+ ```
329
+ - Node: Target node (atom, e.g., 'worker@host1')
330
+ - Module: Pre-compiled module on remote node (e.g., 'gen_server')
331
+ - Function: Exported function to run (e.g., 'start_link')
332
+ - Args: Arguments to function (e.g., [initial_state])
333
+ - Returns: Pid that works for local and remote sends (location transparent)
334
+
335
+ ## PlexSpaces Approach
336
+ ```rust
337
+ // Local spawn (current node)
338
+ let actor = node.spawn_actor("worker", state, config).await?;
339
+
340
+ // Remote spawn (specific node)
341
+ let actor_ref = node.spawn_remote("node2", "worker", state, config).await?;
342
+ ```
343
+ - target_node_id: Target node (string, e.g., "node2")
344
+ - actor_type: Pre-deployed actor type on remote node (string, e.g., "worker")
345
+ - initial_state: Serialized initial state (bytes)
346
+ - config: Actor configuration
347
+ - Returns: ActorRef in format "actor_id@node_id" for location-transparent messaging
348
+
349
+ ## Key Assumptions (Erlang-Compatible)
350
+ 1. **Code Pre-Deployed**: The actor_type must already exist on the target node
351
+ - Like Erlang: Module must be loaded on remote node
352
+ - Use CreateActor first to deploy actor type, or ensure it's in node's registry
353
+ 2. **Location Transparency**: Returned ActorRef works the same for local and remote sends
354
+ - Like Erlang: Pid works for both local send (Pid ! Msg) and remote send
355
+ - ActorRef.tell() / ActorRef.ask() automatically routes to correct node
356
+
357
+ ## Extensibility for WASM (Future - Week 11-12)
358
+ Phase 6 will add dynamic WASM deployment support via reserved fields:
359
+ ```protobuf
360
+ oneof code_source {
361
+ string actor_type = 2; // Pre-deployed type (current)
362
+ WasmModule wasm_module = 10; // Deploy WASM on-the-fly (future)
363
+ string wasm_url = 11; // Fetch WASM from URL (future)
364
+ }
365
+ ```
366
+ This enables:
367
+ - **Pre-deployed**: spawn_remote("node2", "worker", ...) - code already on node2
368
+ - **Dynamic WASM**: spawn_remote_wasm("node2", wasm_bytes, ...) - deploy code + spawn
369
+ - **URL-based**: spawn_remote_url("node2", "https://cdn/worker.wasm", ...) - fetch + deploy + spawn
370
+
371
+ ## Use Cases
372
+ 1. **Distributed Testing**: Spawn Byzantine generals on specific nodes
373
+ ```rust
374
+ for (i, node) in nodes.iter().enumerate() {
375
+ node.spawn_remote(&node.id, "general", state, config).await?;
376
+ }
377
+ ```
378
+ 2. **Load Distribution**: Explicitly place workers across cluster
379
+ ```rust
380
+ let worker_node = pick_least_loaded_node();
381
+ worker_node.spawn_remote(&worker_node.id, "worker", state, config).await?;
382
+ ```
383
+ 3. **Data Locality**: Spawn actor near data source
384
+ ```rust
385
+ let db_node = find_node_with_shard(shard_id);
386
+ db_node.spawn_remote(&db_node.id, "processor", state, config).await?;
387
+ ```
388
+ 4. **Affinity**: Co-locate related actors on same node
389
+ ```rust
390
+ let parent_node = get_actor_node(&parent_id);
391
+ parent_node.spawn_remote(&parent_node.id, "child", state, config).await?;
392
+ ```
393
+
394
+ ## Comparison to CreateActor
395
+ | Feature | CreateActor | SpawnActor |
396
+ |---------|-------------|------------------|
397
+ | Node selection | Placement strategy / current node | Explicit target node |
398
+ | Use case | General actor creation | Explicit remote placement |
399
+ | Fallback | Can fall back to other nodes | Fails if target unavailable |
400
+ | Erlang equivalent | spawn/3 (local) | spawn/4 (remote) |
401
+ | Code deployment | Any node can have code | Target node must have code |
402
+
403
+ ## Implementation Flow
404
+ When node1 calls SpawnRemoteActor targeting node2:
405
+ 1. node1 validates target_node_id exists in registry
406
+ 2. node1 sends gRPC SpawnRemoteActor request to node2
407
+ 3. node2 validates actor_type exists in local registry
408
+ 4. node2 spawns actor locally: Actor::spawn(actor_type, state, config)
409
+ 5. node2 returns ActorRef with format "actor-123@node2"
410
+ 6. node1 caches remote ActorRef for future messaging
411
+ 7. Subsequent tell/ask automatically routes via gRPC to node2
412
+
413
+ Who may invoke tell/ask on the spawned actor (enforced in ActorRef).
414
+ UNSPECIFIED is treated as PUBLIC by the runtime.
415
+ """
416
+
417
+ UNSPECIFIED = 0
418
+ PUBLIC = 1
419
+ """Any tenant and namespace may message this actor."""
420
+
421
+ PROTECTED = 2
422
+ """
423
+ Only callers in the same tenant_id may message; namespace may differ within the tenant.
424
+ """
425
+
426
+ PRIVATE = 3
427
+ """Only callers matching both tenant_id and namespace may message."""
428
+
429
+
430
+ class MonitorType(betterproto.Enum):
431
+ """
432
+ / Monitor type (distinguishes Monitor from Link)
433
+ /
434
+ / ## Purpose
435
+ / Distinguishes between one-way monitoring (Monitor) and two-way death propagation (Link).
436
+ / This enables clear API separation and proper handling of each type.
437
+ /
438
+ / ## Erlang Philosophy
439
+ / - **MONITOR**: One-way notification (Erlang `monitor/2`)
440
+ / - Supervisor gets notified when actor dies
441
+ / - Supervisor does NOT die when actor dies
442
+ / - Used for observability, health checks
443
+ /
444
+ / - **LINK**: Two-way death propagation (Erlang `link/1`)
445
+ / - If actor1 dies, actor2 automatically dies (cascading)
446
+ / - If actor2 dies, actor1 automatically dies (cascading)
447
+ / - Used for tight coupling, supervision trees
448
+ /
449
+ / ## Design Notes
450
+ / - Monitors remain separate (existing functionality)
451
+ / - Links enable cascading failures (new functionality)
452
+ / - Supervision uses links internally (cohesive design)
453
+ """
454
+
455
+ UNSPECIFIED = 0
456
+ MONITOR = 1
457
+ """/ One-way monitoring (get notified, don't die)"""
458
+
459
+ LINK = 2
460
+ """/ Two-way link (die together, cascading failures)"""
461
+
462
+
463
+ class LifecycleEventType(betterproto.Enum):
464
+ """Lifecycle event types for filtering"""
465
+
466
+ UNSPECIFIED = 0
467
+ CREATED = 1
468
+ STARTING = 2
469
+ ACTIVATED = 3
470
+ DEACTIVATING = 4
471
+ DEACTIVATED = 5
472
+ TERMINATED = 6
473
+ FAILED = 7
474
+ MIGRATING = 8
475
+
476
+
477
+ class DropPolicy(betterproto.Enum):
478
+ """Drop policy for buffer overflow (JavaNOW-inspired backpressure)"""
479
+
480
+ UNSPECIFIED = 0
481
+ DROP_OLDEST = 1
482
+ DROP_NEWEST = 2
483
+ BLOCK = 3
484
+
485
+
486
+ class ActorRefErrorCode(betterproto.Enum):
487
+ """ActorRef error codes"""
488
+
489
+ UNSPECIFIED = 0
490
+ ACTOR_NOT_FOUND = 1
491
+ SEND_FAILED = 2
492
+ MAILBOX_FULL = 3
493
+ ACTOR_TERMINATED = 4
494
+ TIMEOUT = 5
495
+ REMOTE_NOT_IMPLEMENTED = 6
496
+
497
+
498
+ class ResourceProfile(betterproto.Enum):
499
+ """
500
+ Resource profile for an actor
501
+ Indicates what type of resources the actor primarily consumes
502
+ """
503
+
504
+ UNSPECIFIED = 0
505
+ CPU_INTENSIVE = 1
506
+ MEMORY_INTENSIVE = 2
507
+ IO_INTENSIVE = 3
508
+ NETWORK_INTENSIVE = 4
509
+ BALANCED = 5
510
+
511
+
512
+ class ResourceViolationCode(betterproto.Enum):
513
+ """Resource violation error codes"""
514
+
515
+ UNSPECIFIED = 0
516
+ CPU_EXCEEDED = 1
517
+ MEMORY_EXCEEDED = 2
518
+ IO_EXCEEDED = 3
519
+ NETWORK_EXCEEDED = 4
520
+
521
+
522
+ class ActorHealthStatus(betterproto.Enum):
523
+ """Actor health status"""
524
+
525
+ UNSPECIFIED = 0
526
+ HEALTHY = 1
527
+ DEGRADED = 2
528
+ STUCK = 3
529
+ FAILED = 4
530
+
531
+
532
+ @dataclass(eq=False, repr=False)
533
+ class ExitReasonDetails(betterproto.Message):
534
+ """Exit reason details (for EXIT_REASON_ERROR and EXIT_REASON_LINKED)"""
535
+
536
+ error_message: str = betterproto.string_field(1)
537
+ """Error message (for EXIT_REASON_ERROR)"""
538
+
539
+ linked_actor_id: str = betterproto.string_field(2)
540
+ """Linked actor ID (for EXIT_REASON_LINKED)"""
541
+
542
+ linked_reason: "ExitReason" = betterproto.enum_field(3)
543
+ """Linked actor's exit reason (for EXIT_REASON_LINKED)"""
544
+
545
+
546
+ @dataclass(eq=False, repr=False)
547
+ class Actor(betterproto.Message):
548
+ """
549
+ Actor instance representation.
550
+
551
+ ## Purpose
552
+ Defines the complete state of a durable actor instance including its
553
+ identity, lifecycle state, configuration, resource usage, and attached capabilities.
554
+
555
+ ## Why This Exists
556
+ - Actors are the fundamental unit of computation in PlexSpaces (Pillar 2: Erlang/OTP)
557
+ - Supports durable actors that survive restarts (Pillar 3: Restate durability)
558
+ - Enables resource-aware scheduling (Quickwit-inspired resource contracts)
559
+ - Provides facet extensibility (Static vs Dynamic principle)
560
+ - Tracks metrics for monitoring and health checks
561
+
562
+ ## Design Notes
563
+ - actor_id uses ActorId from common.proto for consistency across services
564
+ - state tracks lifecycle for supervision and recovery logic
565
+ - node_id and vm_id support distributed placement and Firecracker isolation
566
+ - facets enable dynamic capability composition without changing core proto
567
+ - metrics enable health monitoring and auto-scaling decisions
568
+ """
569
+
570
+ actor_id: str = betterproto.string_field(1)
571
+ actor_type: str = betterproto.string_field(2)
572
+ state: "ActorState" = betterproto.enum_field(3)
573
+ node_id: str = betterproto.string_field(4)
574
+ vm_id: str = betterproto.string_field(5)
575
+ actor_state: bytes = betterproto.bytes_field(6)
576
+ metadata: "__common_v1__.Metadata" = betterproto.message_field(7)
577
+ config: "ActorConfig" = betterproto.message_field(8)
578
+ metrics: "ActorMetrics" = betterproto.message_field(9)
579
+ facets: List["__common_v1__.Facet"] = betterproto.message_field(10)
580
+ """
581
+ FACET EXTENSIBILITY: Attached facets provide additional capabilities
582
+ Examples: virtual_actor, otp_genserver, otp_supervisor, durable_execution, workflow
583
+ """
584
+
585
+ actor_state_schema_version: int = betterproto.uint32_field(12)
586
+ """
587
+ Schema version of actor_state serialization for format evolution
588
+
589
+ ## Purpose
590
+ Enables safe actor state evolution across deployments and restarts.
591
+ Actors may be inactive for extended periods (days/weeks/months) and must
592
+ safely load old state when reactivated with newer actor code.
593
+
594
+ ## Why This Exists
595
+ - Actor state schemas evolve over time (new fields, removed fields, type changes)
596
+ - Actors persist state to disk via checkpoints (see journaling.proto)
597
+ - Actors may migrate between nodes with different code versions
598
+ - Loading incompatible state causes corruption or panics
599
+ - Prevents data loss during rolling upgrades
600
+
601
+ ## Version Rules
602
+ - **Version 0** = unversioned (assume version 1, best-effort deserialization)
603
+ - **Version >= 1** = explicit schema version tracked with actor state
604
+ - **Same version** (e.g., both v2) → Load directly, no migration needed
605
+ - **Older version** (state v1, actor v2) → Attempt migration if migration exists, else reject
606
+ - **Newer version** (state v3, actor v2) → **REJECT** with error (upgrade actor code first)
607
+
608
+ ## Migration Strategy
609
+ ```
610
+ Version 1 → 2: Add new field with default value (backward compatible, no migration)
611
+ Version 2 → 3: Remove deprecated field (backward compatible, no migration)
612
+ Version 3 → 4: Change field type (BREAKING - requires explicit migration function)
613
+ Version 4 → 5: Restructure nested fields (BREAKING - requires migration)
614
+ ```
615
+
616
+ ## Example Usage
617
+ ```rust
618
+ // Actor implementation defines current schema version
619
+ impl CounterActor {
620
+ const SCHEMA_VERSION: u32 = 2;
621
+
622
+ fn save_state(&self) -> Result<Actor, PersistenceError> {
623
+ Ok(Actor {
624
+ actor_id: self.id.clone(),
625
+ actor_state: serialize(&self.internal_state)?,
626
+ actor_state_schema_version: Self::SCHEMA_VERSION,
627
+ // ... other fields ...
628
+ })
629
+ }
630
+
631
+ fn load_state(actor: &Actor) -> Result<Self, PersistenceError> {
632
+ // Version compatibility check
633
+ match actor.actor_state_schema_version {
634
+ // Same version - direct load
635
+ v if v == Self::SCHEMA_VERSION => {
636
+ let state: CounterState = deserialize(&actor.actor_state)?;
637
+ Ok(Self::from_state(state))
638
+ }
639
+
640
+ // Older version - migrate forward
641
+ v if v < Self::SCHEMA_VERSION => {
642
+ let migrated = migrate_counter_state(
643
+ &actor.actor_state,
644
+ v,
645
+ Self::SCHEMA_VERSION,
646
+ )?;
647
+ let state: CounterState = deserialize(&migrated)?;
648
+ Ok(Self::from_state(state))
649
+ }
650
+
651
+ // Newer version - reject (cannot load future state)
652
+ v => Err(PersistenceError::IncompatibleSchemaVersion {
653
+ actor_version: Self::SCHEMA_VERSION,
654
+ state_version: v,
655
+ message: format!(
656
+ "Actor state v{} is newer than actor code v{}. Upgrade actor first.",
657
+ v, Self::SCHEMA_VERSION
658
+ ),
659
+ }),
660
+ }
661
+ }
662
+ }
663
+ ```
664
+
665
+ ## Integration with Checkpointing
666
+ This field works in conjunction with `Checkpoint.state_schema_version` from journaling.proto:
667
+ - **Actor.actor_state_schema_version**: Version of in-memory actor state (current)
668
+ - **Checkpoint.state_schema_version**: Version of checkpointed actor state (persistent)
669
+
670
+ When creating checkpoint:
671
+ ```rust
672
+ let checkpoint = Checkpoint {
673
+ actor_id: actor.actor_id.clone(),
674
+ state_data: actor.actor_state.clone(),
675
+ state_schema_version: actor.actor_state_schema_version, // Copy version
676
+ // ...
677
+ };
678
+ ```
679
+
680
+ ## See Also
681
+ - `proto/plexspaces/v1/journaling.proto` - Checkpoint.state_schema_version (lines 241-321)
682
+ - `docs/SCHEMA_VERSIONING_REVIEW.md` - Complete versioning strategy
683
+ - Migration registry pattern for centralized v1→v2→v3 transitions
684
+ """
685
+
686
+ error_message: str = betterproto.string_field(13)
687
+ """
688
+ Error message when actor is in FAILED state
689
+
690
+ ## Purpose
691
+ Provides error details when actor.state == ACTOR_STATE_FAILED.
692
+ Used for debugging, logging, and supervisor restart decisions.
693
+
694
+ ## Usage
695
+ - Only populated when state == ACTOR_STATE_FAILED
696
+ - Empty string when state != ACTOR_STATE_FAILED
697
+ - Contains error message from actor crash or failure
698
+ """
699
+
700
+ namespace: str = betterproto.string_field(14)
701
+ """
702
+ Namespace for this actor's data isolation.
703
+ When actor is part of an application deployment, namespace must match application namespace.
704
+ Source of truth for namespace is the application (when deploying) or actor creation request.
705
+ """
706
+
707
+ name: str = betterproto.string_field(15)
708
+ """
709
+ User-specified actor name. Together with actor_type, namespace, and node_id
710
+ this forms the structured ActorId.
711
+ """
712
+
713
+
714
+ @dataclass(eq=False, repr=False)
715
+ class ActorConfig(betterproto.Message):
716
+ """Actor configuration"""
717
+
718
+ mailbox_timeout: timedelta = betterproto.message_field(1)
719
+ max_mailbox_size: int = betterproto.uint32_field(2)
720
+ enable_persistence: bool = betterproto.bool_field(3)
721
+ checkpoint_interval: timedelta = betterproto.message_field(4)
722
+ restart_policy: "__common_v1__.RetryPolicy" = betterproto.message_field(5)
723
+ supervision_strategy: "__supervision_v1__.SupervisionStrategy" = (
724
+ betterproto.enum_field(6)
725
+ )
726
+ properties: Dict[str, "betterproto_lib_google_protobuf.Any"] = (
727
+ betterproto.map_field(7, betterproto.TYPE_STRING, betterproto.TYPE_MESSAGE)
728
+ )
729
+ stateless_worker_config: "StatelessWorkerConfig" = betterproto.message_field(11)
730
+ """Orleans-inspired stateless worker configuration"""
731
+
732
+ data_parallel_config: "DataParallelConfig" = betterproto.message_field(12)
733
+ """Data-parallel configuration (if part of a shard group)"""
734
+
735
+ state_management_mode: "StateMgmtMode" = betterproto.enum_field(13)
736
+ """State management mode (traditional vs lattice-based)"""
737
+
738
+ consistency_level: "ConsistencyLevel" = betterproto.enum_field(14)
739
+ """Consistency level for lattice-based actors"""
740
+
741
+ resource_requirements: "ActorResourceRequirements" = betterproto.message_field(16)
742
+ """Resource-aware scheduling: Actor resource requirements"""
743
+
744
+ actor_groups: List[str] = betterproto.string_field(17)
745
+ """Shard group IDs (for task routing and co-scheduling)"""
746
+
747
+ config_schema_version: int = betterproto.uint32_field(15)
748
+ """
749
+ Schema version of properties map for configuration evolution
750
+
751
+ ## Purpose
752
+ Enables safe evolution of actor configuration across deployments.
753
+ The `properties` map (field 7) contains opaque configuration that may
754
+ change as actor types evolve, requiring versioning for compatibility.
755
+
756
+ ## Why This Exists
757
+ - Actor configuration schemas evolve (new config options, deprecated settings)
758
+ - Actors may be created with old config and reactivated with new code
759
+ - Configuration migration needed during rolling upgrades
760
+ - Prevents invalid configuration causing actor startup failures
761
+
762
+ ## Version Rules
763
+ - **Version 0** = unversioned (assume version 1)
764
+ - **Version >= 1** = explicit config schema version
765
+ - **Same version** → Use config directly
766
+ - **Older version** → Migrate config forward (e.g., add new defaults, remove deprecated)
767
+ - **Newer version** → REJECT (upgrade actor code first)
768
+
769
+ ## Example Migration
770
+ ```rust
771
+ // Version 1: Simple timeout config
772
+ properties: {"timeout_ms": 5000}
773
+
774
+ // Version 2: Added retry config (backward compatible)
775
+ properties: {
776
+ "timeout_ms": 5000,
777
+ "max_retries": 3, // NEW: defaults to 3
778
+ "retry_backoff_ms": 100 // NEW: defaults to 100ms
779
+ }
780
+
781
+ // Migration v1→v2: Add defaults for new fields
782
+ fn migrate_config_v1_to_v2(config: &mut ActorConfig) {
783
+ config.properties.entry("max_retries").or_insert(Any::from(3u32));
784
+ config.properties.entry("retry_backoff_ms").or_insert(Any::from(100u32));
785
+ config.config_schema_version = 2;
786
+ }
787
+ ```
788
+
789
+ ## See Also
790
+ - `Actor.actor_state_schema_version` - For actor state versioning
791
+ - `docs/SCHEMA_VERSIONING_REVIEW.md` - Complete versioning strategy
792
+ """
793
+
794
+
795
+ @dataclass(eq=False, repr=False)
796
+ class ResourceRequirements(betterproto.Message):
797
+ """Resource requirements for placement"""
798
+
799
+ min_memory_mb: int = betterproto.uint64_field(1)
800
+ min_cpu_cores: float = betterproto.double_field(2)
801
+ required_capabilities: List[str] = betterproto.string_field(3)
802
+ custom_requirements: Dict[str, str] = betterproto.map_field(
803
+ 4, betterproto.TYPE_STRING, betterproto.TYPE_STRING
804
+ )
805
+
806
+
807
+ @dataclass(eq=False, repr=False)
808
+ class ActorResourceRequirements(betterproto.Message):
809
+ """
810
+ Actor placement for resource-aware scheduling. Single source for labels, affinity, and resources.
811
+ Scheduler (crates/scheduler) uses placement.required_labels and placement.resource_requirements to select nodes.
812
+ """
813
+
814
+ placement: "NodePlacement" = betterproto.message_field(1)
815
+ """
816
+ Unified placement (strategy, required_labels, resource_requirements, preferred/avoid node IDs)
817
+ """
818
+
819
+
820
+ @dataclass(eq=False, repr=False)
821
+ class StatelessWorkerConfig(betterproto.Message):
822
+ """Stateless worker configuration (Orleans-inspired)"""
823
+
824
+ max_instances: int = betterproto.uint32_field(1)
825
+ """Maximum number of concurrent instances"""
826
+
827
+ min_instances: int = betterproto.uint32_field(2)
828
+ """Minimum number of instances to keep warm"""
829
+
830
+ strategy: "LoadBalancingStrategy" = betterproto.enum_field(3)
831
+ """Load balancing strategy"""
832
+
833
+
834
+ @dataclass(eq=False, repr=False)
835
+ class DataParallelConfig(betterproto.Message):
836
+ """
837
+ Data-parallel (shard group) strategy; same for all shards in the group.
838
+ Shard identity is implicit (index in ShardGroup.shard_actor_ids), not stored per-actor.
839
+ """
840
+
841
+ group_id: str = betterproto.string_field(1)
842
+ """Shard group ID"""
843
+
844
+ shard_count: int = betterproto.uint32_field(2)
845
+ """Number of shards in the group (1 to 1 billion)"""
846
+
847
+ partition_strategy: "PartitionStrategy" = betterproto.enum_field(4)
848
+ """Partitioning strategy"""
849
+
850
+ rebalance_policy: "RebalancePolicy" = betterproto.enum_field(5)
851
+ """Rebalancing policy"""
852
+
853
+ placement: "NodePlacement" = betterproto.message_field(6)
854
+ """
855
+ Node placement for multi-node leader-worker (same_node, from_registry, node_ids)
856
+ """
857
+
858
+
859
+ @dataclass(eq=False, repr=False)
860
+ class ShardGroup(betterproto.Message):
861
+ """
862
+ Shard group: unified config (DataParallelConfig) plus actor refs and state.
863
+ """
864
+
865
+ config: "DataParallelConfig" = betterproto.message_field(1)
866
+ """
867
+ Strategy for the group (group_id, shard_count, partition_strategy, rebalance_policy, placement)
868
+ """
869
+
870
+ actor_type: str = betterproto.string_field(2)
871
+ shard_actor_ids: List[str] = betterproto.string_field(3)
872
+ state: "ShardGroupState" = betterproto.enum_field(4)
873
+ created_at: datetime = betterproto.message_field(5)
874
+ metadata: Dict[str, str] = betterproto.map_field(
875
+ 6, betterproto.TYPE_STRING, betterproto.TYPE_STRING
876
+ )
877
+ rebalance_status: "RebalanceStatus" = betterproto.message_field(7)
878
+
879
+
880
+ @dataclass(eq=False, repr=False)
881
+ class RebalanceStatus(betterproto.Message):
882
+ """Rebalancing status for shard groups"""
883
+
884
+ is_rebalancing: bool = betterproto.bool_field(1)
885
+ """Is currently rebalancing"""
886
+
887
+ old_shard_count: int = betterproto.uint32_field(2)
888
+ """Old shard count (before rebalancing)"""
889
+
890
+ new_shard_count: int = betterproto.uint32_field(3)
891
+ """New shard count (after rebalancing)"""
892
+
893
+ progress_percent: float = betterproto.double_field(4)
894
+ """Progress (0.0 to 100.0)"""
895
+
896
+ started_at: datetime = betterproto.message_field(5)
897
+ """When rebalancing started"""
898
+
899
+ estimated_completion: datetime = betterproto.message_field(6)
900
+ """Estimated completion time"""
901
+
902
+
903
+ @dataclass(eq=False, repr=False)
904
+ class NodePlacement(betterproto.Message):
905
+ """
906
+ Unified node placement: strategy, affinity, and resource requirements.
907
+ Replaces PlacementHint, PlacementPreferences, and label/placement fields on ActorResourceRequirements.
908
+ Scheduler (crates/scheduler) matches nodes using required_labels and resource_requirements.
909
+ """
910
+
911
+ strategy: "NodePlacementStrategy" = betterproto.enum_field(1)
912
+ cluster: str = betterproto.string_field(2)
913
+ """
914
+ For FROM_REGISTRY: optional cluster name filter (empty = all connected nodes)
915
+ """
916
+
917
+ node_ids: List[str] = betterproto.string_field(3)
918
+ """For NODE_IDS: explicit node IDs to place shards on (round-robin)"""
919
+
920
+ required_labels: Dict[str, str] = betterproto.map_field(
921
+ 4, betterproto.TYPE_STRING, betterproto.TYPE_STRING
922
+ )
923
+ """
924
+ Node must match all labels (Kubernetes-inspired). Used by scheduler for node selection.
925
+ """
926
+
927
+ avoid_node_ids: List[str] = betterproto.string_field(5)
928
+ """Avoid node IDs (anti-affinity; scheduler excludes these)"""
929
+
930
+ resource_requirements: "__common_v1__.ResourceSpec" = betterproto.message_field(6)
931
+ """
932
+ CPU, memory, disk, GPU requirements. Scheduler filters by NodeCapacity.available.
933
+ """
934
+
935
+ affinity_labels: Dict[str, str] = betterproto.map_field(
936
+ 7, betterproto.TYPE_STRING, betterproto.TYPE_STRING
937
+ )
938
+ """
939
+ Affinity labels (co-location hint; scheduler may prefer nodes with matching labels)
940
+ """
941
+
942
+
943
+ @dataclass(eq=False, repr=False)
944
+ class CreateShardGroupRequest(betterproto.Message):
945
+ config: "DataParallelConfig" = betterproto.message_field(1)
946
+ """
947
+ Group strategy (group_id, shard_count, partition_strategy, rebalance_policy, placement)
948
+ Use config.placement.required_labels for node placement; scheduler matches nodes by placement.
949
+ """
950
+
951
+ actor_type: str = betterproto.string_field(2)
952
+ shard_config: "ActorConfig" = betterproto.message_field(3)
953
+ """
954
+ Per-shard ActorConfig (optional data_parallel_config here is ignored; use config above)
955
+ """
956
+
957
+ initial_state: bytes = betterproto.bytes_field(4)
958
+ metadata: Dict[str, str] = betterproto.map_field(
959
+ 5, betterproto.TYPE_STRING, betterproto.TYPE_STRING
960
+ )
961
+
962
+
963
+ @dataclass(eq=False, repr=False)
964
+ class CreateShardGroupResponse(betterproto.Message):
965
+ group: "ShardGroup" = betterproto.message_field(1)
966
+
967
+
968
+ @dataclass(eq=False, repr=False)
969
+ class DeleteShardGroupRequest(betterproto.Message):
970
+ group_id: str = betterproto.string_field(1)
971
+ force: bool = betterproto.bool_field(2)
972
+ shutdown_timeout: timedelta = betterproto.message_field(3)
973
+
974
+
975
+ @dataclass(eq=False, repr=False)
976
+ class GetShardGroupRequest(betterproto.Message):
977
+ group_id: str = betterproto.string_field(1)
978
+
979
+
980
+ @dataclass(eq=False, repr=False)
981
+ class GetShardGroupResponse(betterproto.Message):
982
+ group: "ShardGroup" = betterproto.message_field(1)
983
+
984
+
985
+ @dataclass(eq=False, repr=False)
986
+ class ListShardGroupsRequest(betterproto.Message):
987
+ actor_type: str = betterproto.string_field(1)
988
+ state: "ShardGroupState" = betterproto.enum_field(2)
989
+ page: "__common_v1__.PageRequest" = betterproto.message_field(3)
990
+
991
+
992
+ @dataclass(eq=False, repr=False)
993
+ class ListShardGroupsResponse(betterproto.Message):
994
+ groups: List["ShardGroup"] = betterproto.message_field(1)
995
+ page: "__common_v1__.PageResponse" = betterproto.message_field(2)
996
+
997
+
998
+ @dataclass(eq=False, repr=False)
999
+ class SendToShardRequest(betterproto.Message):
1000
+ group_id: str = betterproto.string_field(1)
1001
+ partition_key: bytes = betterproto.bytes_field(2)
1002
+ message: "__common_v1__.Message" = betterproto.message_field(3)
1003
+ wait_for_response: bool = betterproto.bool_field(4)
1004
+ timeout: timedelta = betterproto.message_field(5)
1005
+
1006
+
1007
+ @dataclass(eq=False, repr=False)
1008
+ class SendToShardResponse(betterproto.Message):
1009
+ shard_id: int = betterproto.uint32_field(1)
1010
+ shard_actor_id: str = betterproto.string_field(2)
1011
+ response: "__common_v1__.Message" = betterproto.message_field(3)
1012
+
1013
+
1014
+ @dataclass(eq=False, repr=False)
1015
+ class ScatterGatherRequest(betterproto.Message):
1016
+ group_id: str = betterproto.string_field(1)
1017
+ query: "__common_v1__.Message" = betterproto.message_field(2)
1018
+ timeout: timedelta = betterproto.message_field(3)
1019
+ aggregation: "ShardGroupAggregationStrategy" = betterproto.enum_field(4)
1020
+ min_responses: int = betterproto.uint32_field(5)
1021
+
1022
+
1023
+ @dataclass(eq=False, repr=False)
1024
+ class ShardQueryResponse(betterproto.Message):
1025
+ shard_id: int = betterproto.uint32_field(1)
1026
+ shard_actor_id: str = betterproto.string_field(2)
1027
+ response: "__common_v1__.Message" = betterproto.message_field(3)
1028
+ latency: timedelta = betterproto.message_field(4)
1029
+ success: bool = betterproto.bool_field(5)
1030
+ error: str = betterproto.string_field(6)
1031
+
1032
+
1033
+ @dataclass(eq=False, repr=False)
1034
+ class ScatterGatherStats(betterproto.Message):
1035
+ shards_queried: int = betterproto.uint32_field(1)
1036
+ shards_responded: int = betterproto.uint32_field(2)
1037
+ shards_failed: int = betterproto.uint32_field(3)
1038
+ max_latency: timedelta = betterproto.message_field(4)
1039
+
1040
+
1041
+ @dataclass(eq=False, repr=False)
1042
+ class ScatterGatherResponse(betterproto.Message):
1043
+ result: "__common_v1__.Message" = betterproto.message_field(1)
1044
+ shard_responses: List["ShardQueryResponse"] = betterproto.message_field(2)
1045
+ stats: "ScatterGatherStats" = betterproto.message_field(3)
1046
+
1047
+
1048
+ @dataclass(eq=False, repr=False)
1049
+ class BulkUpdateShardGroupRequest(betterproto.Message):
1050
+ """
1051
+ Bulk update: send update messages to multiple shards (DPA UpdateFunction)
1052
+ Inspired by NSDI'22 Data-Parallel Actors paper
1053
+ """
1054
+
1055
+ group_id: str = betterproto.string_field(1)
1056
+ """Group to update"""
1057
+
1058
+ updates: Dict[str, "__common_v1__.Message"] = betterproto.map_field(
1059
+ 2, betterproto.TYPE_STRING, betterproto.TYPE_MESSAGE
1060
+ )
1061
+ """
1062
+ Update messages: partition_key -> message
1063
+ Messages will be routed to appropriate shards based on partition_key
1064
+ """
1065
+
1066
+ consistency_level: "ConsistencyLevel" = betterproto.enum_field(3)
1067
+ """Consistency level for updates"""
1068
+
1069
+ timeout: timedelta = betterproto.message_field(4)
1070
+ """Timeout for updates"""
1071
+
1072
+ wait_for_responses: bool = betterproto.bool_field(5)
1073
+ """Wait for responses (true = wait for all, false = fire-and-forget)"""
1074
+
1075
+
1076
+ @dataclass(eq=False, repr=False)
1077
+ class BulkUpdateShardGroupResponse(betterproto.Message):
1078
+ updates_sent: int = betterproto.uint32_field(1)
1079
+ """Number of updates sent"""
1080
+
1081
+ updates_succeeded: int = betterproto.uint32_field(2)
1082
+ """Number of successful updates"""
1083
+
1084
+ updates_failed: int = betterproto.uint32_field(3)
1085
+ """Number of failed updates"""
1086
+
1087
+ shard_stats: List["ShardUpdateStats"] = betterproto.message_field(4)
1088
+ """Per-shard statistics"""
1089
+
1090
+ errors: List[str] = betterproto.string_field(5)
1091
+ """Errors (if any)"""
1092
+
1093
+
1094
+ @dataclass(eq=False, repr=False)
1095
+ class ShardUpdateStats(betterproto.Message):
1096
+ shard_id: int = betterproto.uint32_field(1)
1097
+ shard_actor_id: str = betterproto.string_field(2)
1098
+ updates_sent: int = betterproto.uint32_field(3)
1099
+ updates_succeeded: int = betterproto.uint32_field(4)
1100
+ updates_failed: int = betterproto.uint32_field(5)
1101
+
1102
+
1103
+ @dataclass(eq=False, repr=False)
1104
+ class MapShardGroupRequest(betterproto.Message):
1105
+ """
1106
+ Map: apply function to all shards in parallel (DPA Map operator)
1107
+ Inspired by NSDI'22 Data-Parallel Actors paper
1108
+ """
1109
+
1110
+ group_id: str = betterproto.string_field(1)
1111
+ """Group to map over"""
1112
+
1113
+ map_function: "__common_v1__.Message" = betterproto.message_field(2)
1114
+ """Map function message (sent to each shard)"""
1115
+
1116
+ timeout: timedelta = betterproto.message_field(3)
1117
+ """Timeout for map operation"""
1118
+
1119
+ min_responses: int = betterproto.uint32_field(4)
1120
+ """Minimum number of shards that must respond (0 = all required)"""
1121
+
1122
+
1123
+ @dataclass(eq=False, repr=False)
1124
+ class MapShardGroupResponse(betterproto.Message):
1125
+ shard_results: List["ShardQueryResponse"] = betterproto.message_field(1)
1126
+ """Mapped results from each shard"""
1127
+
1128
+ stats: "ScatterGatherStats" = betterproto.message_field(2)
1129
+ """Statistics"""
1130
+
1131
+
1132
+ @dataclass(eq=False, repr=False)
1133
+ class CollectiveTargetField(betterproto.Message):
1134
+ value_path: str = betterproto.string_field(1)
1135
+
1136
+
1137
+ @dataclass(eq=False, repr=False)
1138
+ class BroadcastShardGroupRequest(betterproto.Message):
1139
+ group_id: str = betterproto.string_field(1)
1140
+ message: "__common_v1__.Message" = betterproto.message_field(2)
1141
+ timeout: timedelta = betterproto.message_field(3)
1142
+ min_acks: int = betterproto.uint32_field(4)
1143
+
1144
+
1145
+ @dataclass(eq=False, repr=False)
1146
+ class BroadcastShardGroupResponse(betterproto.Message):
1147
+ shard_responses: List["ShardQueryResponse"] = betterproto.message_field(1)
1148
+ stats: "ScatterGatherStats" = betterproto.message_field(2)
1149
+
1150
+
1151
+ @dataclass(eq=False, repr=False)
1152
+ class ReduceShardGroupRequest(betterproto.Message):
1153
+ group_id: str = betterproto.string_field(1)
1154
+ map_function: "__common_v1__.Message" = betterproto.message_field(2)
1155
+ timeout: timedelta = betterproto.message_field(3)
1156
+ min_responses: int = betterproto.uint32_field(4)
1157
+ reduction: "CollectiveReduction" = betterproto.enum_field(5)
1158
+ target: "CollectiveTargetField" = betterproto.message_field(6)
1159
+
1160
+
1161
+ @dataclass(eq=False, repr=False)
1162
+ class ReduceShardGroupResponse(betterproto.Message):
1163
+ result: "__common_v1__.Message" = betterproto.message_field(1)
1164
+ shard_responses: List["ShardQueryResponse"] = betterproto.message_field(2)
1165
+ stats: "ScatterGatherStats" = betterproto.message_field(3)
1166
+
1167
+
1168
+ @dataclass(eq=False, repr=False)
1169
+ class AllReduceShardGroupRequest(betterproto.Message):
1170
+ group_id: str = betterproto.string_field(1)
1171
+ map_function: "__common_v1__.Message" = betterproto.message_field(2)
1172
+ timeout: timedelta = betterproto.message_field(3)
1173
+ min_responses: int = betterproto.uint32_field(4)
1174
+ reduction: "CollectiveReduction" = betterproto.enum_field(5)
1175
+ target: "CollectiveTargetField" = betterproto.message_field(6)
1176
+
1177
+
1178
+ @dataclass(eq=False, repr=False)
1179
+ class AllReduceShardGroupResponse(betterproto.Message):
1180
+ result: "__common_v1__.Message" = betterproto.message_field(1)
1181
+ shard_responses: List["ShardQueryResponse"] = betterproto.message_field(2)
1182
+ stats: "ScatterGatherStats" = betterproto.message_field(3)
1183
+
1184
+
1185
+ @dataclass(eq=False, repr=False)
1186
+ class BarrierShardGroupRequest(betterproto.Message):
1187
+ group_id: str = betterproto.string_field(1)
1188
+ barrier_id: str = betterproto.string_field(2)
1189
+ round: int = betterproto.uint64_field(3)
1190
+ timeout: timedelta = betterproto.message_field(4)
1191
+ min_acks: int = betterproto.uint32_field(5)
1192
+
1193
+
1194
+ @dataclass(eq=False, repr=False)
1195
+ class BarrierShardGroupResponse(betterproto.Message):
1196
+ shard_responses: List["ShardQueryResponse"] = betterproto.message_field(1)
1197
+ stats: "ScatterGatherStats" = betterproto.message_field(2)
1198
+
1199
+
1200
+ @dataclass(eq=False, repr=False)
1201
+ class ScaleShardGroupRequest(betterproto.Message):
1202
+ """Scale shard group (add/remove shards with rebalancing)"""
1203
+
1204
+ group_id: str = betterproto.string_field(1)
1205
+ """Group to scale"""
1206
+
1207
+ new_shard_count: int = betterproto.uint32_field(2)
1208
+ """New shard count"""
1209
+
1210
+ rebalance_policy: "RebalancePolicy" = betterproto.enum_field(3)
1211
+ """Rebalancing policy"""
1212
+
1213
+ new_shard_config: "ActorConfig" = betterproto.message_field(4)
1214
+ """Configuration for new shards (if scaling up)"""
1215
+
1216
+
1217
+ @dataclass(eq=False, repr=False)
1218
+ class ScaleShardGroupResponse(betterproto.Message):
1219
+ group: "ShardGroup" = betterproto.message_field(1)
1220
+ """Updated shard group"""
1221
+
1222
+ rebalance_status: "RebalanceStatus" = betterproto.message_field(2)
1223
+ """Rebalancing status"""
1224
+
1225
+
1226
+ @dataclass(eq=False, repr=False)
1227
+ class ActorMetrics(betterproto.Message):
1228
+ """Actor performance metrics"""
1229
+
1230
+ messages_processed: int = betterproto.uint64_field(1)
1231
+ messages_failed: int = betterproto.uint64_field(2)
1232
+ average_processing_time: timedelta = betterproto.message_field(3)
1233
+ restarts: int = betterproto.uint64_field(4)
1234
+ last_activity: datetime = betterproto.message_field(5)
1235
+ memory_usage_bytes: int = betterproto.uint64_field(6)
1236
+ cpu_usage_percent: float = betterproto.double_field(7)
1237
+
1238
+
1239
+ @dataclass(eq=False, repr=False)
1240
+ class SpawnActorRequest(betterproto.Message):
1241
+ spec: "ActorSpawnSpec" = betterproto.message_field(1)
1242
+ """
1243
+ Full spawn contract: identity, role, namespace, tenant_id, behavior_kind, args, facets, labels, config, visibility.
1244
+ """
1245
+
1246
+ namespace: str = betterproto.string_field(2)
1247
+ """
1248
+ Optional namespace override for this RPC only.
1249
+ If non-empty, the server merges this into spec.namespace for the spawn operation; if empty, spec.namespace is used.
1250
+ """
1251
+
1252
+ instances_count: int = betterproto.uint32_field(3)
1253
+ """
1254
+ Number of identical replicas to spawn (default: 1 when 0).
1255
+ When > 1, spawns N actors with auto-generated instance names derived from spec.identity.name (prefix pattern).
1256
+ """
1257
+
1258
+
1259
+ @dataclass(eq=False, repr=False)
1260
+ class SpawnActorResponse(betterproto.Message):
1261
+ """
1262
+ Response from SpawnActor
1263
+
1264
+ ## Purpose
1265
+ Returns reference to newly spawned actor on the node receiving the request.
1266
+
1267
+ ## Design Notes
1268
+ - actor_ref: String in format "actor_id@target_node_id"
1269
+ - Can be used immediately for tell/ask operations
1270
+ - Location transparent - same API as local actors
1271
+ - Example: "actor-abc123@node2"
1272
+ - actor: Full Actor details for inspection
1273
+ - Contains state, config, metrics, etc.
1274
+ - Useful for monitoring and debugging
1275
+ """
1276
+
1277
+ actor_ref: str = betterproto.string_field(1)
1278
+ """
1279
+ Reference to spawned actor (format: "actor_id@node_id")
1280
+ Example: "general-1@node2", "worker-abc@prod-7"
1281
+ Use this for messaging: actor_ref.tell(msg), actor_ref.ask(msg)
1282
+ """
1283
+
1284
+ actor: "Actor" = betterproto.message_field(2)
1285
+ """
1286
+ Full actor details (state, config, metrics)
1287
+ Useful for inspection and monitoring
1288
+ """
1289
+
1290
+
1291
+ @dataclass(eq=False, repr=False)
1292
+ class SpawnActorsRequest(betterproto.Message):
1293
+ requests: List["SpawnActorRequest"] = betterproto.message_field(1)
1294
+
1295
+
1296
+ @dataclass(eq=False, repr=False)
1297
+ class SpawnActorResult(betterproto.Message):
1298
+ success: bool = betterproto.bool_field(1)
1299
+ error: str = betterproto.string_field(2)
1300
+ response: "SpawnActorResponse" = betterproto.message_field(3)
1301
+
1302
+
1303
+ @dataclass(eq=False, repr=False)
1304
+ class SpawnActorsResponse(betterproto.Message):
1305
+ results: List["SpawnActorResult"] = betterproto.message_field(1)
1306
+
1307
+
1308
+ @dataclass(eq=False, repr=False)
1309
+ class GetActorRequest(betterproto.Message):
1310
+ """Request to get an actor"""
1311
+
1312
+ actor_id: str = betterproto.string_field(1)
1313
+ namespace: str = betterproto.string_field(2)
1314
+ """Namespace for tenant isolation (tenant_id from JWT)"""
1315
+
1316
+
1317
+ @dataclass(eq=False, repr=False)
1318
+ class GetActorResponse(betterproto.Message):
1319
+ actor: "Actor" = betterproto.message_field(1)
1320
+
1321
+
1322
+ @dataclass(eq=False, repr=False)
1323
+ class ListActorsRequest(betterproto.Message):
1324
+ """Request to list actors"""
1325
+
1326
+ page_request: "__common_v1__.PageRequest" = betterproto.message_field(1)
1327
+ actor_type: str = betterproto.string_field(2)
1328
+ state: "ActorState" = betterproto.enum_field(3)
1329
+ node_id: str = betterproto.string_field(4)
1330
+ namespace: str = betterproto.string_field(5)
1331
+ """
1332
+ Namespace for tenant isolation (tenant_id from JWT)
1333
+ Only actors in this namespace will be returned
1334
+ """
1335
+
1336
+
1337
+ @dataclass(eq=False, repr=False)
1338
+ class ListActorsResponse(betterproto.Message):
1339
+ actors: List["Actor"] = betterproto.message_field(1)
1340
+ page_response: "__common_v1__.PageResponse" = betterproto.message_field(2)
1341
+
1342
+
1343
+ @dataclass(eq=False, repr=False)
1344
+ class SendMessageRequest(betterproto.Message):
1345
+ """Request to send a message via tell semantics"""
1346
+
1347
+ namespace: str = betterproto.string_field(1)
1348
+ """
1349
+ Namespace (extracted from path: /api/v1/actors/{namespace}/{actor_type})
1350
+ """
1351
+
1352
+ actor_type: str = betterproto.string_field(2)
1353
+ """
1354
+ Actor type or actor id to target.
1355
+ If this value contains '@', it is treated as a direct actor id first.
1356
+ """
1357
+
1358
+ http_method: str = betterproto.string_field(3)
1359
+ """Optional HTTP method metadata for gateway-originated requests."""
1360
+
1361
+ payload: bytes = betterproto.bytes_field(4)
1362
+ """Request payload bytes."""
1363
+
1364
+ headers: Dict[str, str] = betterproto.map_field(
1365
+ 5, betterproto.TYPE_STRING, betterproto.TYPE_STRING
1366
+ )
1367
+ """Request headers or message metadata."""
1368
+
1369
+ query_params: Dict[str, str] = betterproto.map_field(
1370
+ 6, betterproto.TYPE_STRING, betterproto.TYPE_STRING
1371
+ )
1372
+ """Query parameters from HTTP requests."""
1373
+
1374
+ path: str = betterproto.string_field(7)
1375
+ """Full request path for gateway-originated requests."""
1376
+
1377
+ subpath: str = betterproto.string_field(8)
1378
+ """Remaining path segment after actor_type."""
1379
+
1380
+ sender_id: str = betterproto.string_field(9)
1381
+ """Optional sender actor id for remoting and tracing."""
1382
+
1383
+ message_type: str = betterproto.string_field(10)
1384
+ """Optional application or transport message type."""
1385
+
1386
+ correlation_id: str = betterproto.string_field(11)
1387
+ """Optional correlation id preserved for remoting."""
1388
+
1389
+ reply_to: str = betterproto.string_field(12)
1390
+ """Optional reply_to preserved for remoting."""
1391
+
1392
+ message_id: str = betterproto.string_field(13)
1393
+ """Optional client-provided message id."""
1394
+
1395
+ actor_name: str = betterproto.string_field(20)
1396
+ """
1397
+ Optional actor instance name. When set together with actor_type and namespace,
1398
+ the handler constructs the canonical actor ID directly:
1399
+ actor_name//actor_type::namespace@node_id
1400
+ This avoids ambiguous lookups and makes addressing explicit.
1401
+ If empty, falls back to registry lookup by actor_type within the namespace.
1402
+ """
1403
+
1404
+
1405
+ @dataclass(eq=False, repr=False)
1406
+ class SendMessageResponse(betterproto.Message):
1407
+ success: bool = betterproto.bool_field(1)
1408
+ message_id: str = betterproto.string_field(2)
1409
+ actor_id: str = betterproto.string_field(3)
1410
+ error_message: str = betterproto.string_field(4)
1411
+
1412
+
1413
+ @dataclass(eq=False, repr=False)
1414
+ class StreamMessageRequest(betterproto.Message):
1415
+ """Request for streaming messages (high-throughput)"""
1416
+
1417
+ message: "__common_v1__.Message" = betterproto.message_field(1)
1418
+ sequence: int = betterproto.uint64_field(2)
1419
+ """Sequence number for ordering (client-generated)"""
1420
+
1421
+
1422
+ @dataclass(eq=False, repr=False)
1423
+ class StreamMessageResponse(betterproto.Message):
1424
+ """Response for streaming messages"""
1425
+
1426
+ message_id: str = betterproto.string_field(1)
1427
+ sequence: int = betterproto.uint64_field(2)
1428
+ """Acknowledgement of sequence number"""
1429
+
1430
+ status: str = betterproto.string_field(3)
1431
+ """Status: "delivered", "failed", "queued"""
1432
+
1433
+ error: str = betterproto.string_field(4)
1434
+ """Optional error message"""
1435
+
1436
+
1437
+ @dataclass(eq=False, repr=False)
1438
+ class DeleteActorRequest(betterproto.Message):
1439
+ """Request to delete actor"""
1440
+
1441
+ actor_id: str = betterproto.string_field(1)
1442
+ force: bool = betterproto.bool_field(2)
1443
+ namespace: str = betterproto.string_field(3)
1444
+ """Namespace for tenant isolation (tenant_id from JWT)"""
1445
+
1446
+
1447
+ @dataclass(eq=False, repr=False)
1448
+ class ActorLifecycleEvent(betterproto.Message):
1449
+ """
1450
+ Actor lifecycle event
1451
+
1452
+ ## Purpose
1453
+ Represents a state transition in an actor's lifecycle. Used for monitoring,
1454
+ observability, and triggering supervision actions.
1455
+
1456
+ ## Architecture Context
1457
+ - Emitted by Actor implementation when state changes occur
1458
+ - Consumed by Node for monitoring callbacks
1459
+ - Consumed by observability systems (metrics, tracing, logging)
1460
+ - Enables location-transparent monitoring (Erlang-style)
1461
+
1462
+ ## Design Decisions
1463
+ - Proto-based for distributed type safety across nodes
1464
+ - event_type as oneof for type-safe event variants
1465
+ - Timestamp for event ordering and time-travel debugging
1466
+ - Extensible for future event types without breaking changes
1467
+
1468
+ ## Usage
1469
+ ```rust
1470
+ // Actor emits event
1471
+ let event = ActorLifecycleEvent {
1472
+ actor_id: "worker@node1".to_string(),
1473
+ timestamp: Some(Timestamp::now()),
1474
+ event_type: Some(actor_lifecycle_event::EventType::Terminated(
1475
+ ActorTerminated { reason: "normal".to_string() }
1476
+ )),
1477
+ };
1478
+ node.handle_lifecycle_event(event).await?;
1479
+ ```
1480
+ """
1481
+
1482
+ actor_id: str = betterproto.string_field(1)
1483
+ """Actor that emitted this event"""
1484
+
1485
+ timestamp: datetime = betterproto.message_field(2)
1486
+ """When event occurred"""
1487
+
1488
+ created: "ActorCreated" = betterproto.message_field(10, group="event_type")
1489
+ starting: "ActorStarting" = betterproto.message_field(11, group="event_type")
1490
+ activated: "ActorActivated" = betterproto.message_field(12, group="event_type")
1491
+ deactivating: "ActorDeactivating" = betterproto.message_field(
1492
+ 13, group="event_type"
1493
+ )
1494
+ deactivated: "ActorDeactivated" = betterproto.message_field(14, group="event_type")
1495
+ terminated: "ActorTerminated" = betterproto.message_field(15, group="event_type")
1496
+ failed: "ActorFailed" = betterproto.message_field(16, group="event_type")
1497
+ migrating: "ActorMigrating" = betterproto.message_field(17, group="event_type")
1498
+
1499
+
1500
+ @dataclass(eq=False, repr=False)
1501
+ class ActorCreated(betterproto.Message):
1502
+ """
1503
+ Actor created (construction complete, not yet started)
1504
+
1505
+ ## Purpose
1506
+ Emitted after Actor::new() completes successfully.
1507
+
1508
+ ## State Transition
1509
+ [none] -> Created
1510
+
1511
+ ## Supervisor Action
1512
+ None (waiting for activation)
1513
+ """
1514
+
1515
+ pass
1516
+
1517
+
1518
+ @dataclass(eq=False, repr=False)
1519
+ class ActorStarting(betterproto.Message):
1520
+ """
1521
+ Actor starting (spawning message processing task)
1522
+
1523
+ ## Purpose
1524
+ Emitted when Actor::start() is called, before tokio::spawn().
1525
+
1526
+ ## State Transition
1527
+ Created -> Starting
1528
+
1529
+ ## Supervisor Action
1530
+ None (normal startup)
1531
+ """
1532
+
1533
+ pass
1534
+
1535
+
1536
+ @dataclass(eq=False, repr=False)
1537
+ class ActorActivated(betterproto.Message):
1538
+ """
1539
+ Actor activated (ready to process messages)
1540
+
1541
+ ## Purpose
1542
+ Emitted after actor's message loop starts and on_activate() hook completes.
1543
+
1544
+ ## State Transition
1545
+ Starting -> Activated
1546
+
1547
+ ## Supervisor Action
1548
+ None (actor running normally)
1549
+ """
1550
+
1551
+ pass
1552
+
1553
+
1554
+ @dataclass(eq=False, repr=False)
1555
+ class ActorDeactivating(betterproto.Message):
1556
+ """
1557
+ Actor deactivating (graceful shutdown initiated)
1558
+
1559
+ ## Purpose
1560
+ Emitted when actor receives shutdown signal or supervisor requests stop.
1561
+
1562
+ ## State Transition
1563
+ Activated -> Deactivating
1564
+
1565
+ ## Supervisor Action
1566
+ None (expected shutdown)
1567
+ """
1568
+
1569
+ reason: str = betterproto.string_field(1)
1570
+ """
1571
+ Why deactivation was initiated
1572
+ Examples: "supervisor_shutdown", "manual_stop", "timeout_idle"
1573
+ """
1574
+
1575
+
1576
+ @dataclass(eq=False, repr=False)
1577
+ class ActorDeactivated(betterproto.Message):
1578
+ """
1579
+ Actor deactivated (shutdown complete, but not destroyed)
1580
+
1581
+ ## Purpose
1582
+ Emitted after on_deactivate() hook completes. Actor can be reactivated.
1583
+
1584
+ ## State Transition
1585
+ Deactivating -> Deactivated
1586
+
1587
+ ## Supervisor Action
1588
+ None (actor cleanly stopped, can be restarted if needed)
1589
+ """
1590
+
1591
+ reason: str = betterproto.string_field(1)
1592
+ """Why deactivation occurred"""
1593
+
1594
+
1595
+ @dataclass(eq=False, repr=False)
1596
+ class ActorTerminated(betterproto.Message):
1597
+ """
1598
+ Actor terminated (permanently stopped, not restartable)
1599
+
1600
+ ## Purpose
1601
+ Emitted when actor's task completes normally (loop exits without error).
1602
+ Triggers monitoring callbacks (NotifyActorDown).
1603
+
1604
+ ## State Transition
1605
+ Activated|Deactivating -> Terminated
1606
+
1607
+ ## Supervisor Action
1608
+ - If restart strategy allows, restart actor
1609
+ - Notify all monitors (local + remote via NotifyActorDown RPC)
1610
+
1611
+ ## Design Notes
1612
+ - reason="normal": Graceful shutdown completed
1613
+ - reason="shutdown": Supervisor-initiated shutdown
1614
+ - reason="killed": Forcefully terminated
1615
+ """
1616
+
1617
+ reason: str = betterproto.string_field(1)
1618
+ """
1619
+ Termination reason
1620
+ "normal": Graceful shutdown
1621
+ "shutdown": Supervisor-initiated
1622
+ "killed": Forcefully terminated
1623
+ """
1624
+
1625
+
1626
+ @dataclass(eq=False, repr=False)
1627
+ class ActorFailed(betterproto.Message):
1628
+ """
1629
+ Actor failed (crashed, needs supervision action)
1630
+
1631
+ ## Purpose
1632
+ Emitted when actor's task panics or returns error. Triggers supervision
1633
+ restart logic and monitoring callbacks.
1634
+
1635
+ ## State Transition
1636
+ Activated -> Failed
1637
+
1638
+ ## Supervisor Action
1639
+ - Apply restart strategy (OneForOne, OneForAll, RestForOne)
1640
+ - Notify all monitors with error details
1641
+ - Increment failure counter for escalation
1642
+
1643
+ ## Design Notes
1644
+ - error: Full panic/error message for debugging
1645
+ - Supervision tree decides whether to restart based on restart strategy
1646
+ """
1647
+
1648
+ error: str = betterproto.string_field(1)
1649
+ """
1650
+ Error/panic message
1651
+ Examples:
1652
+ - "panic: index out of bounds"
1653
+ - "error: timeout waiting for response"
1654
+ - "error: connection refused"
1655
+ """
1656
+
1657
+ stack_trace: str = betterproto.string_field(2)
1658
+ """Optional: Stack trace for debugging"""
1659
+
1660
+
1661
+ @dataclass(eq=False, repr=False)
1662
+ class ActorMigrating(betterproto.Message):
1663
+ """
1664
+ Actor migrating (moving to different node)
1665
+
1666
+ ## Purpose
1667
+ Emitted when mobile agent starts migration to another node.
1668
+
1669
+ ## State Transition
1670
+ Activated -> Migrating
1671
+
1672
+ ## Supervisor Action
1673
+ - Update actor location in registry
1674
+ - If migration fails, restart on original node
1675
+
1676
+ ## Design Notes
1677
+ - target_node: Destination node address (e.g., "node2")
1678
+ - Future: Add migration_id for tracking migration progress
1679
+ """
1680
+
1681
+ target_node: str = betterproto.string_field(1)
1682
+ """Target node where actor is migrating"""
1683
+
1684
+
1685
+ @dataclass(eq=False, repr=False)
1686
+ class MonitorActorRequest(betterproto.Message):
1687
+ """
1688
+ Request to monitor an actor (Erlang-style)
1689
+
1690
+ ## Purpose
1691
+ Establishes a monitor from `supervisor_id` to `actor_id` on the **node that hosts
1692
+ `actor_id`**. When that actor terminates, that node runs termination handling and
1693
+ delivers a `__DOWN__` **mailbox message** to `supervisor_id` (same Erlang semantics
1694
+ as `{'DOWN', Ref, process, Pid, Reason}`), routing remotely via `ActorService` when
1695
+ the supervisor lives on another node.
1696
+
1697
+ ## Erlang Philosophy
1698
+ Equivalent to: Ref = erlang:monitor(process, Pid)
1699
+ Works the same for local and remote processes (location transparent).
1700
+
1701
+ ## Design Notes
1702
+ - actor_id: Canonical actor ID for the actor to monitor
1703
+ - supervisor_id: Canonical actor ID of the process that receives `__DOWN__` in its mailbox
1704
+ - supervisor_callback: **Reserved / wire compatibility.** The Rust server implementation
1705
+ does not use this field today; DOWN is sent with `ActorRegistry::tell` to `supervisor_id`.
1706
+ Clients should still populate it per validation (e.g. supervisor node's ActorService URL)
1707
+ for forward compatibility with a possible push-style callback path.
1708
+ """
1709
+
1710
+ actor_id: str = betterproto.string_field(1)
1711
+ supervisor_id: str = betterproto.string_field(2)
1712
+ supervisor_callback: str = betterproto.string_field(3)
1713
+
1714
+
1715
+ @dataclass(eq=False, repr=False)
1716
+ class MonitorActorResponse(betterproto.Message):
1717
+ """
1718
+ Response to MonitorActor request
1719
+
1720
+ ## Purpose
1721
+ Returns a monitor reference that can be used to demonitor in future.
1722
+
1723
+ ## Erlang Philosophy
1724
+ Equivalent to the Ref returned by erlang:monitor(process, Pid).
1725
+
1726
+ ## Design Notes
1727
+ - monitor_ref: Unique ID for this monitoring link (ULID)
1728
+ - Can be used for future demonitor() operation
1729
+ """
1730
+
1731
+ monitor_ref: str = betterproto.string_field(1)
1732
+
1733
+
1734
+ @dataclass(eq=False, repr=False)
1735
+ class DemonitorActorRequest(betterproto.Message):
1736
+ """
1737
+ Request to remove a monitor (Erlang demonitor/1 equivalent)
1738
+
1739
+ ## Purpose
1740
+ Cancels a monitor previously established via MonitorActor on the node that hosts
1741
+ the **monitored** actor (`actor_id`). The caller must supply the same `monitor_ref`
1742
+ returned by MonitorActor.
1743
+ """
1744
+
1745
+ actor_id: str = betterproto.string_field(1)
1746
+ supervisor_id: str = betterproto.string_field(2)
1747
+ monitor_ref: str = betterproto.string_field(3)
1748
+
1749
+
1750
+ @dataclass(eq=False, repr=False)
1751
+ class ActorDownNotification(betterproto.Message):
1752
+ """
1753
+ Notification that a monitored actor has terminated
1754
+
1755
+ ## Purpose
1756
+ Sent by the node hosting the actor to the supervisor when actor terminates.
1757
+ This is an internal message, not typically sent by user code.
1758
+
1759
+ ## Erlang Philosophy
1760
+ Equivalent to receiving: {'DOWN', Ref, process, Pid, Reason}
1761
+ Supervisor receives this asynchronously when monitored actor exits.
1762
+
1763
+ ## Design Notes
1764
+ - actor_id: The actor that terminated
1765
+ - supervisor_id: The supervisor that was monitoring (for routing)
1766
+ - reason: Why actor terminated:
1767
+ - "normal": Graceful shutdown
1768
+ - "shutdown": Supervisor-initiated shutdown
1769
+ - "killed": Forcefully terminated
1770
+ - Error message: Crash reason (e.g., "panic: index out of bounds")
1771
+ """
1772
+
1773
+ actor_id: str = betterproto.string_field(1)
1774
+ supervisor_id: str = betterproto.string_field(2)
1775
+ reason: str = betterproto.string_field(3)
1776
+ monitor_ref: str = betterproto.string_field(4)
1777
+ """
1778
+ Monitor reference ULID correlating with the original monitor() call.
1779
+ Allows the monitoring actor to identify which monitor fired when monitoring multiple actors.
1780
+ """
1781
+
1782
+ is_link_signal: bool = betterproto.bool_field(5)
1783
+ """
1784
+ When true, this notification is a Link EXIT signal (bidirectional death propagation).
1785
+ The receiving node should kill the supervisor_id actor with a Linked exit reason.
1786
+ When false (default), this is a Monitor DOWN notification delivered to the mailbox.
1787
+ """
1788
+
1789
+
1790
+ @dataclass(eq=False, repr=False)
1791
+ class GetActorStatesRequest(betterproto.Message):
1792
+ """
1793
+ Request to batch-check actor states — used by stale-monitor GC task
1794
+
1795
+ ## Purpose
1796
+ Efficiently checks the lifecycle state of multiple actors in a single RPC call.
1797
+ Used by the background monitor GC task to detect stale monitor entries for actors
1798
+ that no longer exist on their hosting node.
1799
+ """
1800
+
1801
+ actor_ids: List[str] = betterproto.string_field(1)
1802
+ """List of canonical actor IDs to check"""
1803
+
1804
+
1805
+ @dataclass(eq=False, repr=False)
1806
+ class GetActorStatesResponse(betterproto.Message):
1807
+ """Response for batch actor state check — uses existing ActorState enum"""
1808
+
1809
+ states: Dict[str, "ActorState"] = betterproto.map_field(
1810
+ 1, betterproto.TYPE_STRING, betterproto.TYPE_ENUM
1811
+ )
1812
+ """
1813
+ Map of canonical actor_id -> ActorState (uses existing ActorState enum)
1814
+ ACTOR_STATE_UNSPECIFIED / ACTOR_STATE_TERMINATED / not present = actor not found on this node
1815
+ """
1816
+
1817
+
1818
+ @dataclass(eq=False, repr=False)
1819
+ class ActorLink(betterproto.Message):
1820
+ """
1821
+ / Actor link for two-way death propagation
1822
+ /
1823
+ / ## Purpose
1824
+ / Represents a link between two actors. When one actor dies, the linked actor
1825
+ / automatically dies (cascading failure). This is the foundation for supervision trees.
1826
+ /
1827
+ / ## Erlang Philosophy
1828
+ / Equivalent to Erlang's `link/1` - creates a bidirectional link between processes.
1829
+ / If either process dies abnormally, the other dies too.
1830
+ /
1831
+ / ## Design Notes
1832
+ / - Links are bidirectional (if A links to B, B is linked to A)
1833
+ / - Links propagate death (if A dies, B dies; if B dies, A dies)
1834
+ / - Links are used internally by supervision (parent-child relationships)
1835
+ / - Links can also be created explicitly via API
1836
+ /
1837
+ / ## Example
1838
+ / ```rust
1839
+ / // Link two actors
1840
+ / node.link("actor-1", "actor-2").await?;
1841
+ /
1842
+ / // If actor-1 dies abnormally, actor-2 automatically dies
1843
+ / // If actor-2 dies abnormally, actor-1 automatically dies
1844
+ / ```
1845
+ """
1846
+
1847
+ actor_id: str = betterproto.string_field(1)
1848
+ """/ First actor in the link"""
1849
+
1850
+ linked_actor_id: str = betterproto.string_field(2)
1851
+ """/ Second actor in the link (bidirectional)"""
1852
+
1853
+ created_at: datetime = betterproto.message_field(3)
1854
+ """/ Link creation timestamp (for observability)"""
1855
+
1856
+ metadata: Dict[str, str] = betterproto.map_field(
1857
+ 4, betterproto.TYPE_STRING, betterproto.TYPE_STRING
1858
+ )
1859
+ """/ Metadata (optional, for debugging)"""
1860
+
1861
+
1862
+ @dataclass(eq=False, repr=False)
1863
+ class LinkActorRequest(betterproto.Message):
1864
+ """
1865
+ / Link two actors (Erlang link/1 equivalent)
1866
+ /
1867
+ / ## Purpose
1868
+ / Creates a bidirectional link between two actors. When one actor dies abnormally,
1869
+ / the linked actor automatically dies (cascading failure).
1870
+ /
1871
+ / ## Erlang Philosophy
1872
+ / Equivalent to Erlang's `link(Pid)` - creates bidirectional link.
1873
+ / If either process dies abnormally, the other dies too.
1874
+ /
1875
+ / ## Design Notes
1876
+ / - Links are bidirectional (if A links to B, B is linked to A)
1877
+ / - Links only propagate abnormal deaths (not "normal" shutdowns)
1878
+ / - Links are used internally by supervision (parent-child relationships)
1879
+ / - Links can be created explicitly via this API
1880
+ /
1881
+ / ## Example
1882
+ / ```rust
1883
+ / // Link two actors
1884
+ / node.link("actor-1", "actor-2").await?;
1885
+ /
1886
+ / // If actor-1 dies abnormally, actor-2 automatically dies
1887
+ / // If actor-2 dies abnormally, actor-1 automatically dies
1888
+ / ```
1889
+ """
1890
+
1891
+ actor_id: str = betterproto.string_field(1)
1892
+ linked_actor_id: str = betterproto.string_field(2)
1893
+
1894
+
1895
+ @dataclass(eq=False, repr=False)
1896
+ class LinkActorResponse(betterproto.Message):
1897
+ """/ Response to LinkActor request"""
1898
+
1899
+ success: bool = betterproto.bool_field(1)
1900
+
1901
+
1902
+ @dataclass(eq=False, repr=False)
1903
+ class UnlinkActorRequest(betterproto.Message):
1904
+ """
1905
+ / Unlink two actors (Erlang unlink/1 equivalent)
1906
+ /
1907
+ / ## Purpose
1908
+ / Removes the bidirectional link between two actors. After unlinking,
1909
+ / actors can die independently without cascading failures.
1910
+ /
1911
+ / ## Erlang Philosophy
1912
+ / Equivalent to Erlang's `unlink(Pid)` - removes bidirectional link.
1913
+ """
1914
+
1915
+ actor_id: str = betterproto.string_field(1)
1916
+ linked_actor_id: str = betterproto.string_field(2)
1917
+
1918
+
1919
+ @dataclass(eq=False, repr=False)
1920
+ class UnlinkActorResponse(betterproto.Message):
1921
+ """/ Response to UnlinkActor request"""
1922
+
1923
+ success: bool = betterproto.bool_field(1)
1924
+
1925
+
1926
+ @dataclass(eq=False, repr=False)
1927
+ class CheckActorExistsRequest(betterproto.Message):
1928
+ """Request to check if actor exists"""
1929
+
1930
+ actor_id: str = betterproto.string_field(1)
1931
+ """Actor ID to check"""
1932
+
1933
+
1934
+ @dataclass(eq=False, repr=False)
1935
+ class CheckActorExistsResponse(betterproto.Message):
1936
+ """Response to check actor exists request"""
1937
+
1938
+ exists: bool = betterproto.bool_field(1)
1939
+ """Actor exists (virtual or active)"""
1940
+
1941
+ is_active: bool = betterproto.bool_field(2)
1942
+ """Actor is currently active (in memory)"""
1943
+
1944
+ is_virtual: bool = betterproto.bool_field(3)
1945
+ """Actor has VirtualActorFacet (is virtual)"""
1946
+
1947
+
1948
+ @dataclass(eq=False, repr=False)
1949
+ class AskReplyRequest(betterproto.Message):
1950
+ """
1951
+ Request to ask an actor via HTTP-like interface (FaaS-style)
1952
+
1953
+ ## Purpose
1954
+ Enables ask-style requests to actors via HTTP GET/POST/PUT routes.
1955
+ The tenant_id, namespace, and actor_type are extracted from the HTTP path.
1956
+
1957
+ ## HTTP Method Handling
1958
+ - GET: Query parameters are converted to JSON and stored in payload as string
1959
+ - POST: Request body becomes payload, HTTP headers become headers map
1960
+ """
1961
+
1962
+ namespace: str = betterproto.string_field(1)
1963
+ """
1964
+ Namespace (extracted from path: /api/v1/actors/{namespace}/{actor_type})
1965
+ Can be empty - defaults to empty string if not provided
1966
+ Tenant ID comes from gRPC auth (JWT middleware) or default config, not from request
1967
+ """
1968
+
1969
+ actor_type: str = betterproto.string_field(2)
1970
+ """
1971
+ Actor type (extracted from path: /api/v1/actors/{namespace}/{actor_type})
1972
+ Used to lookup actors via ActorRegistry discover_actors_by_type
1973
+ """
1974
+
1975
+ http_method: str = betterproto.string_field(3)
1976
+ """HTTP method metadata (GET, POST, or PUT)"""
1977
+
1978
+ payload: bytes = betterproto.bytes_field(4)
1979
+ """
1980
+ Request payload
1981
+ For GET: JSON string of query parameters
1982
+ For POST/PUT: Request body bytes
1983
+ """
1984
+
1985
+ headers: Dict[str, str] = betterproto.map_field(
1986
+ 5, betterproto.TYPE_STRING, betterproto.TYPE_STRING
1987
+ )
1988
+ """
1989
+ HTTP headers
1990
+ Converted from HTTP request headers
1991
+ """
1992
+
1993
+ query_params: Dict[str, str] = betterproto.map_field(
1994
+ 6, betterproto.TYPE_STRING, betterproto.TYPE_STRING
1995
+ )
1996
+ """
1997
+ Query parameters (for GET requests)
1998
+ Converted to JSON and stored in payload
1999
+ """
2000
+
2001
+ path: str = betterproto.string_field(7)
2002
+ """
2003
+ Full HTTP path for the request (optional)
2004
+ Example: "/api/v1/actors/default/counter/custom/path"
2005
+ Allows actors to perform custom routing based on the complete URL
2006
+ """
2007
+
2008
+ subpath: str = betterproto.string_field(8)
2009
+ """
2010
+ Subpath after the actor_type segment (optional)
2011
+ Example: for "/api/v1/actors/default/counter/metrics/latest"
2012
+ subpath = "metrics/latest"
2013
+ This will be used in future for advanced per-actor routing capabilities.
2014
+ """
2015
+
2016
+ sender_id: str = betterproto.string_field(9)
2017
+ """Optional sender actor id for remoting and tracing."""
2018
+
2019
+ message_type: str = betterproto.string_field(10)
2020
+ """Optional transport message type."""
2021
+
2022
+ correlation_id: str = betterproto.string_field(11)
2023
+ """Optional correlation id preserved for remoting."""
2024
+
2025
+ reply_to: str = betterproto.string_field(12)
2026
+ """Optional reply_to preserved for remoting."""
2027
+
2028
+ message_id: str = betterproto.string_field(13)
2029
+ """Optional client-provided message id."""
2030
+
2031
+ timeout: timedelta = betterproto.message_field(14)
2032
+ """
2033
+ Optional timeout for request-reply (ask) operations.
2034
+ Defaults to 5 seconds if not specified. Use for long-running operations like training.
2035
+ HTTP gateway extracts from ?timeout=30 query parameter (in seconds).
2036
+ """
2037
+
2038
+ actor_name: str = betterproto.string_field(20)
2039
+ """
2040
+ Optional actor instance name. When set together with actor_type and namespace,
2041
+ the handler constructs the canonical actor ID directly:
2042
+ actor_name//actor_type::namespace@node_id
2043
+ If empty, falls back to registry lookup by actor_type within the namespace.
2044
+ """
2045
+
2046
+
2047
+ @dataclass(eq=False, repr=False)
2048
+ class AskReplyResponse(betterproto.Message):
2049
+ """
2050
+ Response from asking an actor
2051
+
2052
+ ## Purpose
2053
+ Returns the result of an actor ask request.
2054
+ """
2055
+
2056
+ success: bool = betterproto.bool_field(1)
2057
+ """Success status"""
2058
+
2059
+ payload: bytes = betterproto.bytes_field(2)
2060
+ """
2061
+ Response payload (for GET/ask requests)
2062
+ Contains the reply message from actor
2063
+ """
2064
+
2065
+ headers: Dict[str, str] = betterproto.map_field(
2066
+ 3, betterproto.TYPE_STRING, betterproto.TYPE_STRING
2067
+ )
2068
+ """Response headers (optional metadata)"""
2069
+
2070
+ actor_id: str = betterproto.string_field(4)
2071
+ """Actor ID that was invoked (format: "actor_id@node_id")"""
2072
+
2073
+ error_message: str = betterproto.string_field(5)
2074
+ """Error message (if success is false)"""
2075
+
2076
+
2077
+ @dataclass(eq=False, repr=False)
2078
+ class LifecycleEventFilter(betterproto.Message):
2079
+ """
2080
+ Lifecycle event filter for subscribers
2081
+
2082
+ ## Purpose
2083
+ Defines what events a subscriber wants to receive. Supports filtering by:
2084
+ - Event types (Created, Terminated, Failed, etc.)
2085
+ - Actor ID patterns (regex)
2086
+ - Node ID patterns (regex)
2087
+ - Custom tags
2088
+
2089
+ ## Examples
2090
+ ```
2091
+ // Prometheus exporter - only care about actor spawn/terminate for counts
2092
+ EventFilter {
2093
+ event_types: [ACTOR_CREATED, ACTOR_TERMINATED, ACTOR_FAILED]
2094
+ }
2095
+
2096
+ // Tracing system - want all events for specific actor group
2097
+ EventFilter {
2098
+ actor_id_pattern: "worker-.*@node1"
2099
+ event_types: [ACTOR_ACTIVATED, ACTOR_DEACTIVATED]
2100
+ }
2101
+
2102
+ // Monitoring dashboard - all critical events across cluster
2103
+ EventFilter {
2104
+ event_types: [ACTOR_FAILED, ACTOR_MIGRATING]
2105
+ }
2106
+ ```
2107
+ """
2108
+
2109
+ subscription_id: str = betterproto.string_field(1)
2110
+ """
2111
+ Subscription ID (ULID, generated by subscriber)
2112
+ Used for unsubscribe and tracking
2113
+ """
2114
+
2115
+ event_types: List["LifecycleEventType"] = betterproto.enum_field(2)
2116
+ """
2117
+ Event types to receive (empty = all types)
2118
+ Maps to ActorLifecycleEvent.event_type oneof
2119
+ """
2120
+
2121
+ actor_id_pattern: str = betterproto.string_field(3)
2122
+ """
2123
+ Actor ID pattern (regex, empty = all actors)
2124
+ Example: "worker-.*@node1" matches all workers on node1
2125
+ """
2126
+
2127
+ node_id_pattern: str = betterproto.string_field(4)
2128
+ """
2129
+ Node ID pattern (regex, empty = all nodes)
2130
+ Example: "prod-.*" matches all production nodes
2131
+ """
2132
+
2133
+ required_tags: Dict[str, str] = betterproto.map_field(
2134
+ 5, betterproto.TYPE_STRING, betterproto.TYPE_STRING
2135
+ )
2136
+ """
2137
+ Custom tags filter (AND logic - event must have all tags)
2138
+ Example: {"env": "production", "team": "platform"}
2139
+ """
2140
+
2141
+ buffer_size: int = betterproto.uint32_field(6)
2142
+ """
2143
+ Buffer size for slow subscriber (default: 1000 events)
2144
+ When buffer full, drop_policy determines behavior
2145
+ """
2146
+
2147
+ drop_policy: "DropPolicy" = betterproto.enum_field(7)
2148
+ """Drop policy when buffer full"""
2149
+
2150
+
2151
+ @dataclass(eq=False, repr=False)
2152
+ class VirtualActorLifecycle(betterproto.Message):
2153
+ """
2154
+ Virtual Actor Lifecycle (Orleans-inspired)
2155
+
2156
+ ## Purpose
2157
+ Tracks activation/deactivation state for virtual actors (actors that exist virtually,
2158
+ activated on-demand). Virtual actors are always addressable but not always in memory.
2159
+
2160
+ ## When Used
2161
+ Only present when actor has VirtualActorFacet attached (opt-in pattern).
2162
+ Regular actors (explicit creation) don't have this lifecycle tracking.
2163
+
2164
+ ## Design Decision
2165
+ Virtual actor lifecycle is tracked separately from core ActorState to maintain
2166
+ simplicity: core actors are explicit, virtual actors are opt-in via facet.
2167
+
2168
+ ## Example
2169
+ ```protobuf
2170
+ Actor {
2171
+ actor_id: "user-123"
2172
+ state: ACTOR_STATE_INACTIVE // Virtual actor, not in memory
2173
+ facets: [
2174
+ Facet {
2175
+ type: "virtual_actor"
2176
+ config: {
2177
+ "idle_timeout": "5m",
2178
+ "activation_strategy": "lazy"
2179
+ }
2180
+ }
2181
+ ]
2182
+ }
2183
+ ```
2184
+ Virtual Actor Lifecycle Metadata
2185
+
2186
+ ## Purpose
2187
+ Provides metadata about virtual actor activation/deactivation (timestamps, counts, etc.).
2188
+ This is METADATA only - the actual lifecycle state is tracked in Actor.state (ActorState enum).
2189
+
2190
+ ## Design Decision
2191
+ All actors (virtual or not) use the same ActorState enum for state consistency.
2192
+ VirtualActorLifecycle provides additional metadata useful for virtual actors:
2193
+ - Activation timestamps (for monitoring/debugging)
2194
+ - Activation counts (for metrics)
2195
+ - Idle timeout configuration
2196
+ - Pending message counts
2197
+
2198
+ ## Usage
2199
+ - Actor.state: Source of truth for lifecycle state (CREATING, ACTIVE, INACTIVE, etc.)
2200
+ - VirtualActorLifecycle: Optional metadata for virtual actors only
2201
+ """
2202
+
2203
+ last_activated: datetime = betterproto.message_field(1)
2204
+ """Last time actor was activated (loaded into memory)"""
2205
+
2206
+ last_accessed: datetime = betterproto.message_field(2)
2207
+ """Last time actor received a message or performed an operation"""
2208
+
2209
+ idle_timeout: timedelta = betterproto.message_field(3)
2210
+ """Idle timeout before deactivation (from facet config)"""
2211
+
2212
+ activation_count: int = betterproto.uint32_field(4)
2213
+ """Number of times this actor has been activated"""
2214
+
2215
+ is_activating: bool = betterproto.bool_field(5)
2216
+ """Is currently activating (prevents duplicate activations)"""
2217
+
2218
+ pending_message_count: int = betterproto.uint32_field(6)
2219
+ """
2220
+ Messages queued during activation (processed after activation completes)
2221
+ Note: This is a simplified representation - actual queue is in Node
2222
+ """
2223
+
2224
+
2225
+ @dataclass(eq=False, repr=False)
2226
+ class VirtualActorConfig(betterproto.Message):
2227
+ """
2228
+ Virtual Actor Configuration (for VirtualActorFacet)
2229
+
2230
+ ## Purpose
2231
+ Configuration for virtual actor behavior (activation strategy, idle timeout, etc.)
2232
+
2233
+ ## Usage
2234
+ This config is stored in the VirtualActorFacet's config map:
2235
+ ```protobuf
2236
+ Facet {
2237
+ type: "virtual_actor"
2238
+ config: {
2239
+ "idle_timeout": "5m",
2240
+ "activation_strategy": "lazy"
2241
+ }
2242
+ }
2243
+ ```
2244
+
2245
+ ## Design Decision
2246
+ Config is stored as string map in Facet (simplicity, flexibility).
2247
+ This message is for documentation and type safety in Rust code.
2248
+ """
2249
+
2250
+ activation_strategy: "__common_v1__.ActivationStrategy" = betterproto.enum_field(1)
2251
+ """Activation strategy (from common.proto)"""
2252
+
2253
+ idle_timeout: timedelta = betterproto.message_field(2)
2254
+ """Idle timeout before deactivation"""
2255
+
2256
+ persist_on_deactivation: bool = betterproto.bool_field(3)
2257
+ """Should actor persist state on deactivation?"""
2258
+
2259
+
2260
+ @dataclass(eq=False, repr=False)
2261
+ class ActorRefError(betterproto.Message):
2262
+ """ActorRef error message"""
2263
+
2264
+ code: "ActorRefErrorCode" = betterproto.enum_field(1)
2265
+ message: str = betterproto.string_field(2)
2266
+ actor_id: str = betterproto.string_field(3)
2267
+
2268
+
2269
+ @dataclass(eq=False, repr=False)
2270
+ class ResourceContract(betterproto.Message):
2271
+ """
2272
+ Resource contract for an actor
2273
+ Declares resource requirements and limits upfront
2274
+ """
2275
+
2276
+ max_cpu_percent: float = betterproto.float_field(1)
2277
+ """Maximum CPU usage as percentage (0.0 - 100.0)"""
2278
+
2279
+ max_memory_bytes: int = betterproto.uint64_field(2)
2280
+ """Maximum memory usage in bytes"""
2281
+
2282
+ max_io_ops_per_sec: Optional[int] = betterproto.uint32_field(3, optional=True)
2283
+ """Maximum I/O operations per second"""
2284
+
2285
+ guaranteed_bandwidth_mbps: Optional[int] = betterproto.uint32_field(
2286
+ 4, optional=True
2287
+ )
2288
+ """Guaranteed network bandwidth in Mbps"""
2289
+
2290
+ max_execution_time: Optional[timedelta] = betterproto.message_field(
2291
+ 5, optional=True
2292
+ )
2293
+ """Maximum execution time per message"""
2294
+
2295
+
2296
+ @dataclass(eq=False, repr=False)
2297
+ class ResourceUsage(betterproto.Message):
2298
+ """Current resource usage of an actor"""
2299
+
2300
+ cpu_percent: float = betterproto.float_field(1)
2301
+ """Current CPU usage percentage"""
2302
+
2303
+ memory_bytes: int = betterproto.uint64_field(2)
2304
+ """Current memory usage in bytes"""
2305
+
2306
+ io_ops_per_sec: int = betterproto.uint32_field(3)
2307
+ """Current I/O operations per second"""
2308
+
2309
+ network_mbps: int = betterproto.uint32_field(4)
2310
+ """Current network bandwidth in Mbps"""
2311
+
2312
+
2313
+ @dataclass(eq=False, repr=False)
2314
+ class ResourceViolation(betterproto.Message):
2315
+ """Resource violation error message"""
2316
+
2317
+ code: "ResourceViolationCode" = betterproto.enum_field(1)
2318
+ message: str = betterproto.string_field(2)
2319
+ allowed_value: Optional[float] = betterproto.float_field(3, optional=True)
2320
+ """Allowed value (e.g., max_cpu_percent)"""
2321
+
2322
+ actual_value: Optional[float] = betterproto.float_field(4, optional=True)
2323
+ """Actual value (e.g., current cpu_percent)"""
2324
+
2325
+
2326
+ @dataclass(eq=False, repr=False)
2327
+ class ActorHealth(betterproto.Message):
2328
+ """Actor health message"""
2329
+
2330
+ status: "ActorHealthStatus" = betterproto.enum_field(1)
2331
+ stuck_since: Optional[timedelta] = betterproto.message_field(2, optional=True)
2332
+ """For STUCK status: how long actor has been stuck"""
2333
+
2334
+ failure_reason: Optional[str] = betterproto.string_field(3, optional=True)
2335
+ """For FAILED status: reason for failure"""
2336
+
2337
+
2338
+ @dataclass(eq=False, repr=False)
2339
+ class ActorSpawnSpec(betterproto.Message):
2340
+ """
2341
+ Complete specification for spawning or reactivating any actor.
2342
+
2343
+ ## Purpose
2344
+ Single source of truth for all information needed to spawn an actor, whether
2345
+ from TOML app-config, SDK annotations, gRPC, or virtual actor reactivation.
2346
+ Replaces the fragmented triple of (init_config_template, initial_state, labels)
2347
+ and the VirtualActorDefinitionRegistration intermediary.
2348
+
2349
+ ## Design
2350
+ - node_id is NOT included: ActorFactory always resolves local_node_id at spawn time.
2351
+ - tenant_id is overridden from JWT at request time if available.
2352
+ - args map is the canonical user-supplied init payload: becomes "args" key in WASM init().
2353
+ - facets carry the full facet declaration (type, config, priority) verbatim from ChildSpec.
2354
+
2355
+ ## Usage
2356
+ - TOML ChildSpec → ActorSpawnSpec via actor_spawn_spec_from_child_spec()
2357
+ - SDK annotations → ActorSpawnSpec built from behavior + declared facets
2358
+ - VirtualActorMetadata stores ActorSpawnSpec as its spec field
2359
+ - ActorBuilder.from_spec() accepts ActorSpawnSpec
2360
+ - ActorFactory.spawn_actor() accepts ActorSpawnSpec
2361
+ """
2362
+
2363
+ identity: "__common_v1__.ActorIdentity" = betterproto.message_field(1)
2364
+ """
2365
+ Instance name + behavior class (namespace and tenant added in fields below).
2366
+ """
2367
+
2368
+ role: str = betterproto.string_field(2)
2369
+ """
2370
+ Role of the actor within its application (e.g. "worker", "leader").
2371
+ Maps 1:1 to ChildSpec.role (TOML `role` field in [[supervisor.children]]).
2372
+ Used by BehaviorRegistry to dispatch the correct spec when multiple children
2373
+ share the same actor_type (behavior class).
2374
+ """
2375
+
2376
+ namespace: str = betterproto.string_field(3)
2377
+ """Namespace for actor isolation (required at spawn time)."""
2378
+
2379
+ tenant_id: str = betterproto.string_field(4)
2380
+ """
2381
+ Tenant ID for multi-tenancy isolation (empty if auth disabled).
2382
+ Overridden from JWT claims at request time when auth is enabled.
2383
+ """
2384
+
2385
+ visibility: "ActorVisibility" = betterproto.enum_field(5)
2386
+ """
2387
+ Tell/ask isolation for cross-tenant/cross-namespace callers (see ActorVisibility).
2388
+ UNSPECIFIED is treated as PUBLIC.
2389
+ """
2390
+
2391
+ behavior_kind: str = betterproto.string_field(6)
2392
+ """
2393
+ OTP-style behavior kind for logging and observability.
2394
+ Examples: "GenServer", "GenEvent", "GenStateMachine", "Workflow".
2395
+ """
2396
+
2397
+ args: Dict[str, str] = betterproto.map_field(
2398
+ 7, betterproto.TYPE_STRING, betterproto.TYPE_STRING
2399
+ )
2400
+ """
2401
+ User-supplied initialization arguments.
2402
+ These become the "args" field in the WASM init() payload so TypeScript/Python/Go
2403
+ actors can read them via host.config("initial_count"), etc.
2404
+ Also used by Rust embedded actors as configuration.
2405
+ """
2406
+
2407
+ facets: List["__common_v1__.Facet"] = betterproto.message_field(8)
2408
+ """
2409
+ Facet declarations attached to this actor.
2410
+ Carries virtual_actor, durability, timer, etc. facets verbatim from ChildSpec.
2411
+ ActorFactory instantiates these at spawn time via create_facets_from_config().
2412
+ """
2413
+
2414
+ labels: Dict[str, str] = betterproto.map_field(
2415
+ 9, betterproto.TYPE_STRING, betterproto.TYPE_STRING
2416
+ )
2417
+ """Observability labels propagated to metrics and traces."""
2418
+
2419
+ config: "ActorConfig" = betterproto.message_field(10)
2420
+ """
2421
+ Actor runtime configuration (mailbox, restart policy, etc.).
2422
+ Optional — defaults apply when absent.
2423
+ """
2424
+
2425
+ register_in_object_registry: bool = betterproto.bool_field(11)
2426
+ """
2427
+ If true, auto-register this actor in ObjectRegistry on spawn and unregister on stop.
2428
+ Auto-enabled when facets include both "virtual_actor" and "durability".
2429
+ """
2430
+
2431
+ enforce_unique_placement: bool = betterproto.bool_field(12)
2432
+ """
2433
+ If true, enforce unique placement via alias (requires register_in_object_registry=true or auto-enabled).
2434
+ Spawn fails with ACTOR_ERROR_PLACEMENT_CONFLICT if another active instance with same identity exists.
2435
+ """
2436
+
2437
+ placement_strategy: "PlacementStrategy" = betterproto.enum_field(13)
2438
+ """
2439
+ Placement strategy for distributed actor placement (default: PREFER_LOCAL).
2440
+ """
2441
+
2442
+
2443
+ class ActorServiceStub(betterproto.ServiceStub):
2444
+ async def spawn_actor(
2445
+ self,
2446
+ spawn_actor_request: "SpawnActorRequest",
2447
+ *,
2448
+ timeout: Optional[float] = None,
2449
+ deadline: Optional["Deadline"] = None,
2450
+ metadata: Optional["MetadataLike"] = None
2451
+ ) -> "SpawnActorResponse":
2452
+ return await self._unary_unary(
2453
+ "/plexspaces.actor.v1.ActorService/SpawnActor",
2454
+ spawn_actor_request,
2455
+ SpawnActorResponse,
2456
+ timeout=timeout,
2457
+ deadline=deadline,
2458
+ metadata=metadata,
2459
+ )
2460
+
2461
+ async def spawn_actors(
2462
+ self,
2463
+ spawn_actors_request: "SpawnActorsRequest",
2464
+ *,
2465
+ timeout: Optional[float] = None,
2466
+ deadline: Optional["Deadline"] = None,
2467
+ metadata: Optional["MetadataLike"] = None
2468
+ ) -> "SpawnActorsResponse":
2469
+ return await self._unary_unary(
2470
+ "/plexspaces.actor.v1.ActorService/SpawnActors",
2471
+ spawn_actors_request,
2472
+ SpawnActorsResponse,
2473
+ timeout=timeout,
2474
+ deadline=deadline,
2475
+ metadata=metadata,
2476
+ )
2477
+
2478
+ async def get_actor(
2479
+ self,
2480
+ get_actor_request: "GetActorRequest",
2481
+ *,
2482
+ timeout: Optional[float] = None,
2483
+ deadline: Optional["Deadline"] = None,
2484
+ metadata: Optional["MetadataLike"] = None
2485
+ ) -> "GetActorResponse":
2486
+ return await self._unary_unary(
2487
+ "/plexspaces.actor.v1.ActorService/GetActor",
2488
+ get_actor_request,
2489
+ GetActorResponse,
2490
+ timeout=timeout,
2491
+ deadline=deadline,
2492
+ metadata=metadata,
2493
+ )
2494
+
2495
+ async def list_actors(
2496
+ self,
2497
+ list_actors_request: "ListActorsRequest",
2498
+ *,
2499
+ timeout: Optional[float] = None,
2500
+ deadline: Optional["Deadline"] = None,
2501
+ metadata: Optional["MetadataLike"] = None
2502
+ ) -> "ListActorsResponse":
2503
+ return await self._unary_unary(
2504
+ "/plexspaces.actor.v1.ActorService/ListActors",
2505
+ list_actors_request,
2506
+ ListActorsResponse,
2507
+ timeout=timeout,
2508
+ deadline=deadline,
2509
+ metadata=metadata,
2510
+ )
2511
+
2512
+ async def send_message(
2513
+ self,
2514
+ send_message_request: "SendMessageRequest",
2515
+ *,
2516
+ timeout: Optional[float] = None,
2517
+ deadline: Optional["Deadline"] = None,
2518
+ metadata: Optional["MetadataLike"] = None
2519
+ ) -> "SendMessageResponse":
2520
+ return await self._unary_unary(
2521
+ "/plexspaces.actor.v1.ActorService/SendMessage",
2522
+ send_message_request,
2523
+ SendMessageResponse,
2524
+ timeout=timeout,
2525
+ deadline=deadline,
2526
+ metadata=metadata,
2527
+ )
2528
+
2529
+ async def stream_messages(
2530
+ self,
2531
+ stream_message_request_iterator: Union[
2532
+ AsyncIterable[StreamMessageRequest], Iterable[StreamMessageRequest]
2533
+ ],
2534
+ *,
2535
+ timeout: Optional[float] = None,
2536
+ deadline: Optional["Deadline"] = None,
2537
+ metadata: Optional["MetadataLike"] = None
2538
+ ) -> AsyncIterator[StreamMessageResponse]:
2539
+ async for response in self._stream_stream(
2540
+ "/plexspaces.actor.v1.ActorService/StreamMessages",
2541
+ stream_message_request_iterator,
2542
+ StreamMessageRequest,
2543
+ StreamMessageResponse,
2544
+ timeout=timeout,
2545
+ deadline=deadline,
2546
+ metadata=metadata,
2547
+ ):
2548
+ yield response
2549
+
2550
+ async def delete_actor(
2551
+ self,
2552
+ delete_actor_request: "DeleteActorRequest",
2553
+ *,
2554
+ timeout: Optional[float] = None,
2555
+ deadline: Optional["Deadline"] = None,
2556
+ metadata: Optional["MetadataLike"] = None
2557
+ ) -> "__common_v1__.Empty":
2558
+ return await self._unary_unary(
2559
+ "/plexspaces.actor.v1.ActorService/DeleteActor",
2560
+ delete_actor_request,
2561
+ __common_v1__.Empty,
2562
+ timeout=timeout,
2563
+ deadline=deadline,
2564
+ metadata=metadata,
2565
+ )
2566
+
2567
+ async def monitor_actor(
2568
+ self,
2569
+ monitor_actor_request: "MonitorActorRequest",
2570
+ *,
2571
+ timeout: Optional[float] = None,
2572
+ deadline: Optional["Deadline"] = None,
2573
+ metadata: Optional["MetadataLike"] = None
2574
+ ) -> "MonitorActorResponse":
2575
+ return await self._unary_unary(
2576
+ "/plexspaces.actor.v1.ActorService/MonitorActor",
2577
+ monitor_actor_request,
2578
+ MonitorActorResponse,
2579
+ timeout=timeout,
2580
+ deadline=deadline,
2581
+ metadata=metadata,
2582
+ )
2583
+
2584
+ async def demonitor_actor(
2585
+ self,
2586
+ demonitor_actor_request: "DemonitorActorRequest",
2587
+ *,
2588
+ timeout: Optional[float] = None,
2589
+ deadline: Optional["Deadline"] = None,
2590
+ metadata: Optional["MetadataLike"] = None
2591
+ ) -> "__common_v1__.Empty":
2592
+ return await self._unary_unary(
2593
+ "/plexspaces.actor.v1.ActorService/DemonitorActor",
2594
+ demonitor_actor_request,
2595
+ __common_v1__.Empty,
2596
+ timeout=timeout,
2597
+ deadline=deadline,
2598
+ metadata=metadata,
2599
+ )
2600
+
2601
+ async def link_actor(
2602
+ self,
2603
+ link_actor_request: "LinkActorRequest",
2604
+ *,
2605
+ timeout: Optional[float] = None,
2606
+ deadline: Optional["Deadline"] = None,
2607
+ metadata: Optional["MetadataLike"] = None
2608
+ ) -> "LinkActorResponse":
2609
+ return await self._unary_unary(
2610
+ "/plexspaces.actor.v1.ActorService/LinkActor",
2611
+ link_actor_request,
2612
+ LinkActorResponse,
2613
+ timeout=timeout,
2614
+ deadline=deadline,
2615
+ metadata=metadata,
2616
+ )
2617
+
2618
+ async def unlink_actor(
2619
+ self,
2620
+ unlink_actor_request: "UnlinkActorRequest",
2621
+ *,
2622
+ timeout: Optional[float] = None,
2623
+ deadline: Optional["Deadline"] = None,
2624
+ metadata: Optional["MetadataLike"] = None
2625
+ ) -> "UnlinkActorResponse":
2626
+ return await self._unary_unary(
2627
+ "/plexspaces.actor.v1.ActorService/UnlinkActor",
2628
+ unlink_actor_request,
2629
+ UnlinkActorResponse,
2630
+ timeout=timeout,
2631
+ deadline=deadline,
2632
+ metadata=metadata,
2633
+ )
2634
+
2635
+ async def notify_actor_down(
2636
+ self,
2637
+ actor_down_notification: "ActorDownNotification",
2638
+ *,
2639
+ timeout: Optional[float] = None,
2640
+ deadline: Optional["Deadline"] = None,
2641
+ metadata: Optional["MetadataLike"] = None
2642
+ ) -> "__common_v1__.Empty":
2643
+ return await self._unary_unary(
2644
+ "/plexspaces.actor.v1.ActorService/NotifyActorDown",
2645
+ actor_down_notification,
2646
+ __common_v1__.Empty,
2647
+ timeout=timeout,
2648
+ deadline=deadline,
2649
+ metadata=metadata,
2650
+ )
2651
+
2652
+ async def check_actor_exists(
2653
+ self,
2654
+ check_actor_exists_request: "CheckActorExistsRequest",
2655
+ *,
2656
+ timeout: Optional[float] = None,
2657
+ deadline: Optional["Deadline"] = None,
2658
+ metadata: Optional["MetadataLike"] = None
2659
+ ) -> "CheckActorExistsResponse":
2660
+ return await self._unary_unary(
2661
+ "/plexspaces.actor.v1.ActorService/CheckActorExists",
2662
+ check_actor_exists_request,
2663
+ CheckActorExistsResponse,
2664
+ timeout=timeout,
2665
+ deadline=deadline,
2666
+ metadata=metadata,
2667
+ )
2668
+
2669
+ async def get_actor_states(
2670
+ self,
2671
+ get_actor_states_request: "GetActorStatesRequest",
2672
+ *,
2673
+ timeout: Optional[float] = None,
2674
+ deadline: Optional["Deadline"] = None,
2675
+ metadata: Optional["MetadataLike"] = None
2676
+ ) -> "GetActorStatesResponse":
2677
+ return await self._unary_unary(
2678
+ "/plexspaces.actor.v1.ActorService/GetActorStates",
2679
+ get_actor_states_request,
2680
+ GetActorStatesResponse,
2681
+ timeout=timeout,
2682
+ deadline=deadline,
2683
+ metadata=metadata,
2684
+ )
2685
+
2686
+ async def ask_reply(
2687
+ self,
2688
+ ask_reply_request: "AskReplyRequest",
2689
+ *,
2690
+ timeout: Optional[float] = None,
2691
+ deadline: Optional["Deadline"] = None,
2692
+ metadata: Optional["MetadataLike"] = None
2693
+ ) -> "AskReplyResponse":
2694
+ return await self._unary_unary(
2695
+ "/plexspaces.actor.v1.ActorService/AskReply",
2696
+ ask_reply_request,
2697
+ AskReplyResponse,
2698
+ timeout=timeout,
2699
+ deadline=deadline,
2700
+ metadata=metadata,
2701
+ )
2702
+
2703
+ async def create_shard_group(
2704
+ self,
2705
+ create_shard_group_request: "CreateShardGroupRequest",
2706
+ *,
2707
+ timeout: Optional[float] = None,
2708
+ deadline: Optional["Deadline"] = None,
2709
+ metadata: Optional["MetadataLike"] = None
2710
+ ) -> "CreateShardGroupResponse":
2711
+ return await self._unary_unary(
2712
+ "/plexspaces.actor.v1.ActorService/CreateShardGroup",
2713
+ create_shard_group_request,
2714
+ CreateShardGroupResponse,
2715
+ timeout=timeout,
2716
+ deadline=deadline,
2717
+ metadata=metadata,
2718
+ )
2719
+
2720
+ async def delete_shard_group(
2721
+ self,
2722
+ delete_shard_group_request: "DeleteShardGroupRequest",
2723
+ *,
2724
+ timeout: Optional[float] = None,
2725
+ deadline: Optional["Deadline"] = None,
2726
+ metadata: Optional["MetadataLike"] = None
2727
+ ) -> "__common_v1__.Empty":
2728
+ return await self._unary_unary(
2729
+ "/plexspaces.actor.v1.ActorService/DeleteShardGroup",
2730
+ delete_shard_group_request,
2731
+ __common_v1__.Empty,
2732
+ timeout=timeout,
2733
+ deadline=deadline,
2734
+ metadata=metadata,
2735
+ )
2736
+
2737
+ async def get_shard_group(
2738
+ self,
2739
+ get_shard_group_request: "GetShardGroupRequest",
2740
+ *,
2741
+ timeout: Optional[float] = None,
2742
+ deadline: Optional["Deadline"] = None,
2743
+ metadata: Optional["MetadataLike"] = None
2744
+ ) -> "GetShardGroupResponse":
2745
+ return await self._unary_unary(
2746
+ "/plexspaces.actor.v1.ActorService/GetShardGroup",
2747
+ get_shard_group_request,
2748
+ GetShardGroupResponse,
2749
+ timeout=timeout,
2750
+ deadline=deadline,
2751
+ metadata=metadata,
2752
+ )
2753
+
2754
+ async def list_shard_groups(
2755
+ self,
2756
+ list_shard_groups_request: "ListShardGroupsRequest",
2757
+ *,
2758
+ timeout: Optional[float] = None,
2759
+ deadline: Optional["Deadline"] = None,
2760
+ metadata: Optional["MetadataLike"] = None
2761
+ ) -> "ListShardGroupsResponse":
2762
+ return await self._unary_unary(
2763
+ "/plexspaces.actor.v1.ActorService/ListShardGroups",
2764
+ list_shard_groups_request,
2765
+ ListShardGroupsResponse,
2766
+ timeout=timeout,
2767
+ deadline=deadline,
2768
+ metadata=metadata,
2769
+ )
2770
+
2771
+ async def scale_shard_group(
2772
+ self,
2773
+ scale_shard_group_request: "ScaleShardGroupRequest",
2774
+ *,
2775
+ timeout: Optional[float] = None,
2776
+ deadline: Optional["Deadline"] = None,
2777
+ metadata: Optional["MetadataLike"] = None
2778
+ ) -> "ScaleShardGroupResponse":
2779
+ return await self._unary_unary(
2780
+ "/plexspaces.actor.v1.ActorService/ScaleShardGroup",
2781
+ scale_shard_group_request,
2782
+ ScaleShardGroupResponse,
2783
+ timeout=timeout,
2784
+ deadline=deadline,
2785
+ metadata=metadata,
2786
+ )
2787
+
2788
+ async def send_to_shard(
2789
+ self,
2790
+ send_to_shard_request: "SendToShardRequest",
2791
+ *,
2792
+ timeout: Optional[float] = None,
2793
+ deadline: Optional["Deadline"] = None,
2794
+ metadata: Optional["MetadataLike"] = None
2795
+ ) -> "SendToShardResponse":
2796
+ return await self._unary_unary(
2797
+ "/plexspaces.actor.v1.ActorService/SendToShard",
2798
+ send_to_shard_request,
2799
+ SendToShardResponse,
2800
+ timeout=timeout,
2801
+ deadline=deadline,
2802
+ metadata=metadata,
2803
+ )
2804
+
2805
+ async def broadcast_shard_group(
2806
+ self,
2807
+ broadcast_shard_group_request: "BroadcastShardGroupRequest",
2808
+ *,
2809
+ timeout: Optional[float] = None,
2810
+ deadline: Optional["Deadline"] = None,
2811
+ metadata: Optional["MetadataLike"] = None
2812
+ ) -> "BroadcastShardGroupResponse":
2813
+ return await self._unary_unary(
2814
+ "/plexspaces.actor.v1.ActorService/BroadcastShardGroup",
2815
+ broadcast_shard_group_request,
2816
+ BroadcastShardGroupResponse,
2817
+ timeout=timeout,
2818
+ deadline=deadline,
2819
+ metadata=metadata,
2820
+ )
2821
+
2822
+ async def reduce_shard_group(
2823
+ self,
2824
+ reduce_shard_group_request: "ReduceShardGroupRequest",
2825
+ *,
2826
+ timeout: Optional[float] = None,
2827
+ deadline: Optional["Deadline"] = None,
2828
+ metadata: Optional["MetadataLike"] = None
2829
+ ) -> "ReduceShardGroupResponse":
2830
+ return await self._unary_unary(
2831
+ "/plexspaces.actor.v1.ActorService/ReduceShardGroup",
2832
+ reduce_shard_group_request,
2833
+ ReduceShardGroupResponse,
2834
+ timeout=timeout,
2835
+ deadline=deadline,
2836
+ metadata=metadata,
2837
+ )
2838
+
2839
+ async def all_reduce_shard_group(
2840
+ self,
2841
+ all_reduce_shard_group_request: "AllReduceShardGroupRequest",
2842
+ *,
2843
+ timeout: Optional[float] = None,
2844
+ deadline: Optional["Deadline"] = None,
2845
+ metadata: Optional["MetadataLike"] = None
2846
+ ) -> "AllReduceShardGroupResponse":
2847
+ return await self._unary_unary(
2848
+ "/plexspaces.actor.v1.ActorService/AllReduceShardGroup",
2849
+ all_reduce_shard_group_request,
2850
+ AllReduceShardGroupResponse,
2851
+ timeout=timeout,
2852
+ deadline=deadline,
2853
+ metadata=metadata,
2854
+ )
2855
+
2856
+ async def barrier_shard_group(
2857
+ self,
2858
+ barrier_shard_group_request: "BarrierShardGroupRequest",
2859
+ *,
2860
+ timeout: Optional[float] = None,
2861
+ deadline: Optional["Deadline"] = None,
2862
+ metadata: Optional["MetadataLike"] = None
2863
+ ) -> "BarrierShardGroupResponse":
2864
+ return await self._unary_unary(
2865
+ "/plexspaces.actor.v1.ActorService/BarrierShardGroup",
2866
+ barrier_shard_group_request,
2867
+ BarrierShardGroupResponse,
2868
+ timeout=timeout,
2869
+ deadline=deadline,
2870
+ metadata=metadata,
2871
+ )
2872
+
2873
+ async def scatter_gather(
2874
+ self,
2875
+ scatter_gather_request: "ScatterGatherRequest",
2876
+ *,
2877
+ timeout: Optional[float] = None,
2878
+ deadline: Optional["Deadline"] = None,
2879
+ metadata: Optional["MetadataLike"] = None
2880
+ ) -> "ScatterGatherResponse":
2881
+ return await self._unary_unary(
2882
+ "/plexspaces.actor.v1.ActorService/ScatterGather",
2883
+ scatter_gather_request,
2884
+ ScatterGatherResponse,
2885
+ timeout=timeout,
2886
+ deadline=deadline,
2887
+ metadata=metadata,
2888
+ )
2889
+
2890
+ async def bulk_update_shard_group(
2891
+ self,
2892
+ bulk_update_shard_group_request: "BulkUpdateShardGroupRequest",
2893
+ *,
2894
+ timeout: Optional[float] = None,
2895
+ deadline: Optional["Deadline"] = None,
2896
+ metadata: Optional["MetadataLike"] = None
2897
+ ) -> "BulkUpdateShardGroupResponse":
2898
+ return await self._unary_unary(
2899
+ "/plexspaces.actor.v1.ActorService/BulkUpdateShardGroup",
2900
+ bulk_update_shard_group_request,
2901
+ BulkUpdateShardGroupResponse,
2902
+ timeout=timeout,
2903
+ deadline=deadline,
2904
+ metadata=metadata,
2905
+ )
2906
+
2907
+ async def map_shard_group(
2908
+ self,
2909
+ map_shard_group_request: "MapShardGroupRequest",
2910
+ *,
2911
+ timeout: Optional[float] = None,
2912
+ deadline: Optional["Deadline"] = None,
2913
+ metadata: Optional["MetadataLike"] = None
2914
+ ) -> "MapShardGroupResponse":
2915
+ return await self._unary_unary(
2916
+ "/plexspaces.actor.v1.ActorService/MapShardGroup",
2917
+ map_shard_group_request,
2918
+ MapShardGroupResponse,
2919
+ timeout=timeout,
2920
+ deadline=deadline,
2921
+ metadata=metadata,
2922
+ )
2923
+
2924
+
2925
+ class LifecycleEventChannelStub(betterproto.ServiceStub):
2926
+ async def subscribe_lifecycle_events(
2927
+ self,
2928
+ lifecycle_event_filter: "LifecycleEventFilter",
2929
+ *,
2930
+ timeout: Optional[float] = None,
2931
+ deadline: Optional["Deadline"] = None,
2932
+ metadata: Optional["MetadataLike"] = None
2933
+ ) -> AsyncIterator[ActorLifecycleEvent]:
2934
+ async for response in self._unary_stream(
2935
+ "/plexspaces.actor.v1.LifecycleEventChannel/SubscribeLifecycleEvents",
2936
+ lifecycle_event_filter,
2937
+ ActorLifecycleEvent,
2938
+ timeout=timeout,
2939
+ deadline=deadline,
2940
+ metadata=metadata,
2941
+ ):
2942
+ yield response
2943
+
2944
+ async def publish_lifecycle_event(
2945
+ self,
2946
+ actor_lifecycle_event: "ActorLifecycleEvent",
2947
+ *,
2948
+ timeout: Optional[float] = None,
2949
+ deadline: Optional["Deadline"] = None,
2950
+ metadata: Optional["MetadataLike"] = None
2951
+ ) -> "__common_v1__.Empty":
2952
+ return await self._unary_unary(
2953
+ "/plexspaces.actor.v1.LifecycleEventChannel/PublishLifecycleEvent",
2954
+ actor_lifecycle_event,
2955
+ __common_v1__.Empty,
2956
+ timeout=timeout,
2957
+ deadline=deadline,
2958
+ metadata=metadata,
2959
+ )
2960
+
2961
+
2962
+ class ActorServiceBase(ServiceBase):
2963
+
2964
+ async def spawn_actor(
2965
+ self, spawn_actor_request: "SpawnActorRequest"
2966
+ ) -> "SpawnActorResponse":
2967
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
2968
+
2969
+ async def spawn_actors(
2970
+ self, spawn_actors_request: "SpawnActorsRequest"
2971
+ ) -> "SpawnActorsResponse":
2972
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
2973
+
2974
+ async def get_actor(
2975
+ self, get_actor_request: "GetActorRequest"
2976
+ ) -> "GetActorResponse":
2977
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
2978
+
2979
+ async def list_actors(
2980
+ self, list_actors_request: "ListActorsRequest"
2981
+ ) -> "ListActorsResponse":
2982
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
2983
+
2984
+ async def send_message(
2985
+ self, send_message_request: "SendMessageRequest"
2986
+ ) -> "SendMessageResponse":
2987
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
2988
+
2989
+ async def stream_messages(
2990
+ self, stream_message_request_iterator: AsyncIterator[StreamMessageRequest]
2991
+ ) -> AsyncIterator[StreamMessageResponse]:
2992
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
2993
+ yield StreamMessageResponse()
2994
+
2995
+ async def delete_actor(
2996
+ self, delete_actor_request: "DeleteActorRequest"
2997
+ ) -> "__common_v1__.Empty":
2998
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
2999
+
3000
+ async def monitor_actor(
3001
+ self, monitor_actor_request: "MonitorActorRequest"
3002
+ ) -> "MonitorActorResponse":
3003
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
3004
+
3005
+ async def demonitor_actor(
3006
+ self, demonitor_actor_request: "DemonitorActorRequest"
3007
+ ) -> "__common_v1__.Empty":
3008
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
3009
+
3010
+ async def link_actor(
3011
+ self, link_actor_request: "LinkActorRequest"
3012
+ ) -> "LinkActorResponse":
3013
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
3014
+
3015
+ async def unlink_actor(
3016
+ self, unlink_actor_request: "UnlinkActorRequest"
3017
+ ) -> "UnlinkActorResponse":
3018
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
3019
+
3020
+ async def notify_actor_down(
3021
+ self, actor_down_notification: "ActorDownNotification"
3022
+ ) -> "__common_v1__.Empty":
3023
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
3024
+
3025
+ async def check_actor_exists(
3026
+ self, check_actor_exists_request: "CheckActorExistsRequest"
3027
+ ) -> "CheckActorExistsResponse":
3028
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
3029
+
3030
+ async def get_actor_states(
3031
+ self, get_actor_states_request: "GetActorStatesRequest"
3032
+ ) -> "GetActorStatesResponse":
3033
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
3034
+
3035
+ async def ask_reply(
3036
+ self, ask_reply_request: "AskReplyRequest"
3037
+ ) -> "AskReplyResponse":
3038
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
3039
+
3040
+ async def create_shard_group(
3041
+ self, create_shard_group_request: "CreateShardGroupRequest"
3042
+ ) -> "CreateShardGroupResponse":
3043
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
3044
+
3045
+ async def delete_shard_group(
3046
+ self, delete_shard_group_request: "DeleteShardGroupRequest"
3047
+ ) -> "__common_v1__.Empty":
3048
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
3049
+
3050
+ async def get_shard_group(
3051
+ self, get_shard_group_request: "GetShardGroupRequest"
3052
+ ) -> "GetShardGroupResponse":
3053
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
3054
+
3055
+ async def list_shard_groups(
3056
+ self, list_shard_groups_request: "ListShardGroupsRequest"
3057
+ ) -> "ListShardGroupsResponse":
3058
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
3059
+
3060
+ async def scale_shard_group(
3061
+ self, scale_shard_group_request: "ScaleShardGroupRequest"
3062
+ ) -> "ScaleShardGroupResponse":
3063
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
3064
+
3065
+ async def send_to_shard(
3066
+ self, send_to_shard_request: "SendToShardRequest"
3067
+ ) -> "SendToShardResponse":
3068
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
3069
+
3070
+ async def broadcast_shard_group(
3071
+ self, broadcast_shard_group_request: "BroadcastShardGroupRequest"
3072
+ ) -> "BroadcastShardGroupResponse":
3073
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
3074
+
3075
+ async def reduce_shard_group(
3076
+ self, reduce_shard_group_request: "ReduceShardGroupRequest"
3077
+ ) -> "ReduceShardGroupResponse":
3078
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
3079
+
3080
+ async def all_reduce_shard_group(
3081
+ self, all_reduce_shard_group_request: "AllReduceShardGroupRequest"
3082
+ ) -> "AllReduceShardGroupResponse":
3083
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
3084
+
3085
+ async def barrier_shard_group(
3086
+ self, barrier_shard_group_request: "BarrierShardGroupRequest"
3087
+ ) -> "BarrierShardGroupResponse":
3088
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
3089
+
3090
+ async def scatter_gather(
3091
+ self, scatter_gather_request: "ScatterGatherRequest"
3092
+ ) -> "ScatterGatherResponse":
3093
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
3094
+
3095
+ async def bulk_update_shard_group(
3096
+ self, bulk_update_shard_group_request: "BulkUpdateShardGroupRequest"
3097
+ ) -> "BulkUpdateShardGroupResponse":
3098
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
3099
+
3100
+ async def map_shard_group(
3101
+ self, map_shard_group_request: "MapShardGroupRequest"
3102
+ ) -> "MapShardGroupResponse":
3103
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
3104
+
3105
+ async def __rpc_spawn_actor(
3106
+ self, stream: "grpclib.server.Stream[SpawnActorRequest, SpawnActorResponse]"
3107
+ ) -> None:
3108
+ request = await stream.recv_message()
3109
+ response = await self.spawn_actor(request)
3110
+ await stream.send_message(response)
3111
+
3112
+ async def __rpc_spawn_actors(
3113
+ self, stream: "grpclib.server.Stream[SpawnActorsRequest, SpawnActorsResponse]"
3114
+ ) -> None:
3115
+ request = await stream.recv_message()
3116
+ response = await self.spawn_actors(request)
3117
+ await stream.send_message(response)
3118
+
3119
+ async def __rpc_get_actor(
3120
+ self, stream: "grpclib.server.Stream[GetActorRequest, GetActorResponse]"
3121
+ ) -> None:
3122
+ request = await stream.recv_message()
3123
+ response = await self.get_actor(request)
3124
+ await stream.send_message(response)
3125
+
3126
+ async def __rpc_list_actors(
3127
+ self, stream: "grpclib.server.Stream[ListActorsRequest, ListActorsResponse]"
3128
+ ) -> None:
3129
+ request = await stream.recv_message()
3130
+ response = await self.list_actors(request)
3131
+ await stream.send_message(response)
3132
+
3133
+ async def __rpc_send_message(
3134
+ self, stream: "grpclib.server.Stream[SendMessageRequest, SendMessageResponse]"
3135
+ ) -> None:
3136
+ request = await stream.recv_message()
3137
+ response = await self.send_message(request)
3138
+ await stream.send_message(response)
3139
+
3140
+ async def __rpc_stream_messages(
3141
+ self,
3142
+ stream: "grpclib.server.Stream[StreamMessageRequest, StreamMessageResponse]",
3143
+ ) -> None:
3144
+ request = stream.__aiter__()
3145
+ await self._call_rpc_handler_server_stream(
3146
+ self.stream_messages,
3147
+ stream,
3148
+ request,
3149
+ )
3150
+
3151
+ async def __rpc_delete_actor(
3152
+ self, stream: "grpclib.server.Stream[DeleteActorRequest, __common_v1__.Empty]"
3153
+ ) -> None:
3154
+ request = await stream.recv_message()
3155
+ response = await self.delete_actor(request)
3156
+ await stream.send_message(response)
3157
+
3158
+ async def __rpc_monitor_actor(
3159
+ self, stream: "grpclib.server.Stream[MonitorActorRequest, MonitorActorResponse]"
3160
+ ) -> None:
3161
+ request = await stream.recv_message()
3162
+ response = await self.monitor_actor(request)
3163
+ await stream.send_message(response)
3164
+
3165
+ async def __rpc_demonitor_actor(
3166
+ self,
3167
+ stream: "grpclib.server.Stream[DemonitorActorRequest, __common_v1__.Empty]",
3168
+ ) -> None:
3169
+ request = await stream.recv_message()
3170
+ response = await self.demonitor_actor(request)
3171
+ await stream.send_message(response)
3172
+
3173
+ async def __rpc_link_actor(
3174
+ self, stream: "grpclib.server.Stream[LinkActorRequest, LinkActorResponse]"
3175
+ ) -> None:
3176
+ request = await stream.recv_message()
3177
+ response = await self.link_actor(request)
3178
+ await stream.send_message(response)
3179
+
3180
+ async def __rpc_unlink_actor(
3181
+ self, stream: "grpclib.server.Stream[UnlinkActorRequest, UnlinkActorResponse]"
3182
+ ) -> None:
3183
+ request = await stream.recv_message()
3184
+ response = await self.unlink_actor(request)
3185
+ await stream.send_message(response)
3186
+
3187
+ async def __rpc_notify_actor_down(
3188
+ self,
3189
+ stream: "grpclib.server.Stream[ActorDownNotification, __common_v1__.Empty]",
3190
+ ) -> None:
3191
+ request = await stream.recv_message()
3192
+ response = await self.notify_actor_down(request)
3193
+ await stream.send_message(response)
3194
+
3195
+ async def __rpc_check_actor_exists(
3196
+ self,
3197
+ stream: "grpclib.server.Stream[CheckActorExistsRequest, CheckActorExistsResponse]",
3198
+ ) -> None:
3199
+ request = await stream.recv_message()
3200
+ response = await self.check_actor_exists(request)
3201
+ await stream.send_message(response)
3202
+
3203
+ async def __rpc_get_actor_states(
3204
+ self,
3205
+ stream: "grpclib.server.Stream[GetActorStatesRequest, GetActorStatesResponse]",
3206
+ ) -> None:
3207
+ request = await stream.recv_message()
3208
+ response = await self.get_actor_states(request)
3209
+ await stream.send_message(response)
3210
+
3211
+ async def __rpc_ask_reply(
3212
+ self, stream: "grpclib.server.Stream[AskReplyRequest, AskReplyResponse]"
3213
+ ) -> None:
3214
+ request = await stream.recv_message()
3215
+ response = await self.ask_reply(request)
3216
+ await stream.send_message(response)
3217
+
3218
+ async def __rpc_create_shard_group(
3219
+ self,
3220
+ stream: "grpclib.server.Stream[CreateShardGroupRequest, CreateShardGroupResponse]",
3221
+ ) -> None:
3222
+ request = await stream.recv_message()
3223
+ response = await self.create_shard_group(request)
3224
+ await stream.send_message(response)
3225
+
3226
+ async def __rpc_delete_shard_group(
3227
+ self,
3228
+ stream: "grpclib.server.Stream[DeleteShardGroupRequest, __common_v1__.Empty]",
3229
+ ) -> None:
3230
+ request = await stream.recv_message()
3231
+ response = await self.delete_shard_group(request)
3232
+ await stream.send_message(response)
3233
+
3234
+ async def __rpc_get_shard_group(
3235
+ self,
3236
+ stream: "grpclib.server.Stream[GetShardGroupRequest, GetShardGroupResponse]",
3237
+ ) -> None:
3238
+ request = await stream.recv_message()
3239
+ response = await self.get_shard_group(request)
3240
+ await stream.send_message(response)
3241
+
3242
+ async def __rpc_list_shard_groups(
3243
+ self,
3244
+ stream: "grpclib.server.Stream[ListShardGroupsRequest, ListShardGroupsResponse]",
3245
+ ) -> None:
3246
+ request = await stream.recv_message()
3247
+ response = await self.list_shard_groups(request)
3248
+ await stream.send_message(response)
3249
+
3250
+ async def __rpc_scale_shard_group(
3251
+ self,
3252
+ stream: "grpclib.server.Stream[ScaleShardGroupRequest, ScaleShardGroupResponse]",
3253
+ ) -> None:
3254
+ request = await stream.recv_message()
3255
+ response = await self.scale_shard_group(request)
3256
+ await stream.send_message(response)
3257
+
3258
+ async def __rpc_send_to_shard(
3259
+ self, stream: "grpclib.server.Stream[SendToShardRequest, SendToShardResponse]"
3260
+ ) -> None:
3261
+ request = await stream.recv_message()
3262
+ response = await self.send_to_shard(request)
3263
+ await stream.send_message(response)
3264
+
3265
+ async def __rpc_broadcast_shard_group(
3266
+ self,
3267
+ stream: "grpclib.server.Stream[BroadcastShardGroupRequest, BroadcastShardGroupResponse]",
3268
+ ) -> None:
3269
+ request = await stream.recv_message()
3270
+ response = await self.broadcast_shard_group(request)
3271
+ await stream.send_message(response)
3272
+
3273
+ async def __rpc_reduce_shard_group(
3274
+ self,
3275
+ stream: "grpclib.server.Stream[ReduceShardGroupRequest, ReduceShardGroupResponse]",
3276
+ ) -> None:
3277
+ request = await stream.recv_message()
3278
+ response = await self.reduce_shard_group(request)
3279
+ await stream.send_message(response)
3280
+
3281
+ async def __rpc_all_reduce_shard_group(
3282
+ self,
3283
+ stream: "grpclib.server.Stream[AllReduceShardGroupRequest, AllReduceShardGroupResponse]",
3284
+ ) -> None:
3285
+ request = await stream.recv_message()
3286
+ response = await self.all_reduce_shard_group(request)
3287
+ await stream.send_message(response)
3288
+
3289
+ async def __rpc_barrier_shard_group(
3290
+ self,
3291
+ stream: "grpclib.server.Stream[BarrierShardGroupRequest, BarrierShardGroupResponse]",
3292
+ ) -> None:
3293
+ request = await stream.recv_message()
3294
+ response = await self.barrier_shard_group(request)
3295
+ await stream.send_message(response)
3296
+
3297
+ async def __rpc_scatter_gather(
3298
+ self,
3299
+ stream: "grpclib.server.Stream[ScatterGatherRequest, ScatterGatherResponse]",
3300
+ ) -> None:
3301
+ request = await stream.recv_message()
3302
+ response = await self.scatter_gather(request)
3303
+ await stream.send_message(response)
3304
+
3305
+ async def __rpc_bulk_update_shard_group(
3306
+ self,
3307
+ stream: "grpclib.server.Stream[BulkUpdateShardGroupRequest, BulkUpdateShardGroupResponse]",
3308
+ ) -> None:
3309
+ request = await stream.recv_message()
3310
+ response = await self.bulk_update_shard_group(request)
3311
+ await stream.send_message(response)
3312
+
3313
+ async def __rpc_map_shard_group(
3314
+ self,
3315
+ stream: "grpclib.server.Stream[MapShardGroupRequest, MapShardGroupResponse]",
3316
+ ) -> None:
3317
+ request = await stream.recv_message()
3318
+ response = await self.map_shard_group(request)
3319
+ await stream.send_message(response)
3320
+
3321
+ def __mapping__(self) -> Dict[str, grpclib.const.Handler]:
3322
+ return {
3323
+ "/plexspaces.actor.v1.ActorService/SpawnActor": grpclib.const.Handler(
3324
+ self.__rpc_spawn_actor,
3325
+ grpclib.const.Cardinality.UNARY_UNARY,
3326
+ SpawnActorRequest,
3327
+ SpawnActorResponse,
3328
+ ),
3329
+ "/plexspaces.actor.v1.ActorService/SpawnActors": grpclib.const.Handler(
3330
+ self.__rpc_spawn_actors,
3331
+ grpclib.const.Cardinality.UNARY_UNARY,
3332
+ SpawnActorsRequest,
3333
+ SpawnActorsResponse,
3334
+ ),
3335
+ "/plexspaces.actor.v1.ActorService/GetActor": grpclib.const.Handler(
3336
+ self.__rpc_get_actor,
3337
+ grpclib.const.Cardinality.UNARY_UNARY,
3338
+ GetActorRequest,
3339
+ GetActorResponse,
3340
+ ),
3341
+ "/plexspaces.actor.v1.ActorService/ListActors": grpclib.const.Handler(
3342
+ self.__rpc_list_actors,
3343
+ grpclib.const.Cardinality.UNARY_UNARY,
3344
+ ListActorsRequest,
3345
+ ListActorsResponse,
3346
+ ),
3347
+ "/plexspaces.actor.v1.ActorService/SendMessage": grpclib.const.Handler(
3348
+ self.__rpc_send_message,
3349
+ grpclib.const.Cardinality.UNARY_UNARY,
3350
+ SendMessageRequest,
3351
+ SendMessageResponse,
3352
+ ),
3353
+ "/plexspaces.actor.v1.ActorService/StreamMessages": grpclib.const.Handler(
3354
+ self.__rpc_stream_messages,
3355
+ grpclib.const.Cardinality.STREAM_STREAM,
3356
+ StreamMessageRequest,
3357
+ StreamMessageResponse,
3358
+ ),
3359
+ "/plexspaces.actor.v1.ActorService/DeleteActor": grpclib.const.Handler(
3360
+ self.__rpc_delete_actor,
3361
+ grpclib.const.Cardinality.UNARY_UNARY,
3362
+ DeleteActorRequest,
3363
+ __common_v1__.Empty,
3364
+ ),
3365
+ "/plexspaces.actor.v1.ActorService/MonitorActor": grpclib.const.Handler(
3366
+ self.__rpc_monitor_actor,
3367
+ grpclib.const.Cardinality.UNARY_UNARY,
3368
+ MonitorActorRequest,
3369
+ MonitorActorResponse,
3370
+ ),
3371
+ "/plexspaces.actor.v1.ActorService/DemonitorActor": grpclib.const.Handler(
3372
+ self.__rpc_demonitor_actor,
3373
+ grpclib.const.Cardinality.UNARY_UNARY,
3374
+ DemonitorActorRequest,
3375
+ __common_v1__.Empty,
3376
+ ),
3377
+ "/plexspaces.actor.v1.ActorService/LinkActor": grpclib.const.Handler(
3378
+ self.__rpc_link_actor,
3379
+ grpclib.const.Cardinality.UNARY_UNARY,
3380
+ LinkActorRequest,
3381
+ LinkActorResponse,
3382
+ ),
3383
+ "/plexspaces.actor.v1.ActorService/UnlinkActor": grpclib.const.Handler(
3384
+ self.__rpc_unlink_actor,
3385
+ grpclib.const.Cardinality.UNARY_UNARY,
3386
+ UnlinkActorRequest,
3387
+ UnlinkActorResponse,
3388
+ ),
3389
+ "/plexspaces.actor.v1.ActorService/NotifyActorDown": grpclib.const.Handler(
3390
+ self.__rpc_notify_actor_down,
3391
+ grpclib.const.Cardinality.UNARY_UNARY,
3392
+ ActorDownNotification,
3393
+ __common_v1__.Empty,
3394
+ ),
3395
+ "/plexspaces.actor.v1.ActorService/CheckActorExists": grpclib.const.Handler(
3396
+ self.__rpc_check_actor_exists,
3397
+ grpclib.const.Cardinality.UNARY_UNARY,
3398
+ CheckActorExistsRequest,
3399
+ CheckActorExistsResponse,
3400
+ ),
3401
+ "/plexspaces.actor.v1.ActorService/GetActorStates": grpclib.const.Handler(
3402
+ self.__rpc_get_actor_states,
3403
+ grpclib.const.Cardinality.UNARY_UNARY,
3404
+ GetActorStatesRequest,
3405
+ GetActorStatesResponse,
3406
+ ),
3407
+ "/plexspaces.actor.v1.ActorService/AskReply": grpclib.const.Handler(
3408
+ self.__rpc_ask_reply,
3409
+ grpclib.const.Cardinality.UNARY_UNARY,
3410
+ AskReplyRequest,
3411
+ AskReplyResponse,
3412
+ ),
3413
+ "/plexspaces.actor.v1.ActorService/CreateShardGroup": grpclib.const.Handler(
3414
+ self.__rpc_create_shard_group,
3415
+ grpclib.const.Cardinality.UNARY_UNARY,
3416
+ CreateShardGroupRequest,
3417
+ CreateShardGroupResponse,
3418
+ ),
3419
+ "/plexspaces.actor.v1.ActorService/DeleteShardGroup": grpclib.const.Handler(
3420
+ self.__rpc_delete_shard_group,
3421
+ grpclib.const.Cardinality.UNARY_UNARY,
3422
+ DeleteShardGroupRequest,
3423
+ __common_v1__.Empty,
3424
+ ),
3425
+ "/plexspaces.actor.v1.ActorService/GetShardGroup": grpclib.const.Handler(
3426
+ self.__rpc_get_shard_group,
3427
+ grpclib.const.Cardinality.UNARY_UNARY,
3428
+ GetShardGroupRequest,
3429
+ GetShardGroupResponse,
3430
+ ),
3431
+ "/plexspaces.actor.v1.ActorService/ListShardGroups": grpclib.const.Handler(
3432
+ self.__rpc_list_shard_groups,
3433
+ grpclib.const.Cardinality.UNARY_UNARY,
3434
+ ListShardGroupsRequest,
3435
+ ListShardGroupsResponse,
3436
+ ),
3437
+ "/plexspaces.actor.v1.ActorService/ScaleShardGroup": grpclib.const.Handler(
3438
+ self.__rpc_scale_shard_group,
3439
+ grpclib.const.Cardinality.UNARY_UNARY,
3440
+ ScaleShardGroupRequest,
3441
+ ScaleShardGroupResponse,
3442
+ ),
3443
+ "/plexspaces.actor.v1.ActorService/SendToShard": grpclib.const.Handler(
3444
+ self.__rpc_send_to_shard,
3445
+ grpclib.const.Cardinality.UNARY_UNARY,
3446
+ SendToShardRequest,
3447
+ SendToShardResponse,
3448
+ ),
3449
+ "/plexspaces.actor.v1.ActorService/BroadcastShardGroup": grpclib.const.Handler(
3450
+ self.__rpc_broadcast_shard_group,
3451
+ grpclib.const.Cardinality.UNARY_UNARY,
3452
+ BroadcastShardGroupRequest,
3453
+ BroadcastShardGroupResponse,
3454
+ ),
3455
+ "/plexspaces.actor.v1.ActorService/ReduceShardGroup": grpclib.const.Handler(
3456
+ self.__rpc_reduce_shard_group,
3457
+ grpclib.const.Cardinality.UNARY_UNARY,
3458
+ ReduceShardGroupRequest,
3459
+ ReduceShardGroupResponse,
3460
+ ),
3461
+ "/plexspaces.actor.v1.ActorService/AllReduceShardGroup": grpclib.const.Handler(
3462
+ self.__rpc_all_reduce_shard_group,
3463
+ grpclib.const.Cardinality.UNARY_UNARY,
3464
+ AllReduceShardGroupRequest,
3465
+ AllReduceShardGroupResponse,
3466
+ ),
3467
+ "/plexspaces.actor.v1.ActorService/BarrierShardGroup": grpclib.const.Handler(
3468
+ self.__rpc_barrier_shard_group,
3469
+ grpclib.const.Cardinality.UNARY_UNARY,
3470
+ BarrierShardGroupRequest,
3471
+ BarrierShardGroupResponse,
3472
+ ),
3473
+ "/plexspaces.actor.v1.ActorService/ScatterGather": grpclib.const.Handler(
3474
+ self.__rpc_scatter_gather,
3475
+ grpclib.const.Cardinality.UNARY_UNARY,
3476
+ ScatterGatherRequest,
3477
+ ScatterGatherResponse,
3478
+ ),
3479
+ "/plexspaces.actor.v1.ActorService/BulkUpdateShardGroup": grpclib.const.Handler(
3480
+ self.__rpc_bulk_update_shard_group,
3481
+ grpclib.const.Cardinality.UNARY_UNARY,
3482
+ BulkUpdateShardGroupRequest,
3483
+ BulkUpdateShardGroupResponse,
3484
+ ),
3485
+ "/plexspaces.actor.v1.ActorService/MapShardGroup": grpclib.const.Handler(
3486
+ self.__rpc_map_shard_group,
3487
+ grpclib.const.Cardinality.UNARY_UNARY,
3488
+ MapShardGroupRequest,
3489
+ MapShardGroupResponse,
3490
+ ),
3491
+ }
3492
+
3493
+
3494
+ class LifecycleEventChannelBase(ServiceBase):
3495
+
3496
+ async def subscribe_lifecycle_events(
3497
+ self, lifecycle_event_filter: "LifecycleEventFilter"
3498
+ ) -> AsyncIterator[ActorLifecycleEvent]:
3499
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
3500
+ yield ActorLifecycleEvent()
3501
+
3502
+ async def publish_lifecycle_event(
3503
+ self, actor_lifecycle_event: "ActorLifecycleEvent"
3504
+ ) -> "__common_v1__.Empty":
3505
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
3506
+
3507
+ async def __rpc_subscribe_lifecycle_events(
3508
+ self, stream: "grpclib.server.Stream[LifecycleEventFilter, ActorLifecycleEvent]"
3509
+ ) -> None:
3510
+ request = await stream.recv_message()
3511
+ await self._call_rpc_handler_server_stream(
3512
+ self.subscribe_lifecycle_events,
3513
+ stream,
3514
+ request,
3515
+ )
3516
+
3517
+ async def __rpc_publish_lifecycle_event(
3518
+ self, stream: "grpclib.server.Stream[ActorLifecycleEvent, __common_v1__.Empty]"
3519
+ ) -> None:
3520
+ request = await stream.recv_message()
3521
+ response = await self.publish_lifecycle_event(request)
3522
+ await stream.send_message(response)
3523
+
3524
+ def __mapping__(self) -> Dict[str, grpclib.const.Handler]:
3525
+ return {
3526
+ "/plexspaces.actor.v1.LifecycleEventChannel/SubscribeLifecycleEvents": grpclib.const.Handler(
3527
+ self.__rpc_subscribe_lifecycle_events,
3528
+ grpclib.const.Cardinality.UNARY_STREAM,
3529
+ LifecycleEventFilter,
3530
+ ActorLifecycleEvent,
3531
+ ),
3532
+ "/plexspaces.actor.v1.LifecycleEventChannel/PublishLifecycleEvent": grpclib.const.Handler(
3533
+ self.__rpc_publish_lifecycle_event,
3534
+ grpclib.const.Cardinality.UNARY_UNARY,
3535
+ ActorLifecycleEvent,
3536
+ __common_v1__.Empty,
3537
+ ),
3538
+ }