akgentic-core 1.3.3__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.
- akgentic/__init__.py +2 -0
- akgentic/core/__init__.py +59 -0
- akgentic/core/actor_address.py +150 -0
- akgentic/core/actor_address_impl.py +347 -0
- akgentic/core/actor_system_impl.py +457 -0
- akgentic/core/agent.py +802 -0
- akgentic/core/agent_card.py +339 -0
- akgentic/core/agent_config.py +78 -0
- akgentic/core/agent_state.py +113 -0
- akgentic/core/messages/__init__.py +39 -0
- akgentic/core/messages/message.py +121 -0
- akgentic/core/messages/orchestrator.py +125 -0
- akgentic/core/orchestrator.py +738 -0
- akgentic/core/py.typed +0 -0
- akgentic/core/user_proxy.py +96 -0
- akgentic/core/utils/__init__.py +37 -0
- akgentic/core/utils/deserializer.py +202 -0
- akgentic/core/utils/serializer.py +304 -0
- akgentic_core-1.3.3.dist-info/METADATA +783 -0
- akgentic_core-1.3.3.dist-info/RECORD +22 -0
- akgentic_core-1.3.3.dist-info/WHEEL +4 -0
- akgentic_core-1.3.3.dist-info/licenses/LICENSE +661 -0
akgentic/__init__.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""Akgentic v2: Actor framework for agent-based systems.
|
|
2
|
+
|
|
3
|
+
Phase 1 provides core actor primitives with minimal dependencies (pydantic for serialization).
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
# Extend __path__ to allow sub-packages from other workspace distributions
|
|
7
|
+
# (e.g. akgentic-llm's akgentic.llm) to be discovered as part of this namespace.
|
|
8
|
+
from pkgutil import extend_path
|
|
9
|
+
|
|
10
|
+
__path__ = extend_path(__path__, __name__)
|
|
11
|
+
|
|
12
|
+
from akgentic.core.actor_address import ActorAddress
|
|
13
|
+
from akgentic.core.actor_address_impl import (
|
|
14
|
+
ActorAddressImpl,
|
|
15
|
+
ActorAddressProxy,
|
|
16
|
+
ActorAddressStopped,
|
|
17
|
+
)
|
|
18
|
+
from akgentic.core.actor_system_impl import (
|
|
19
|
+
ActorSystem,
|
|
20
|
+
ExecutionContext,
|
|
21
|
+
Statistics,
|
|
22
|
+
)
|
|
23
|
+
from akgentic.core.agent import Akgent, AkgentDeserializeContext
|
|
24
|
+
from akgentic.core.agent_card import AgentCard
|
|
25
|
+
from akgentic.core.agent_config import (
|
|
26
|
+
AgentConfig,
|
|
27
|
+
BaseConfig,
|
|
28
|
+
)
|
|
29
|
+
from akgentic.core.agent_state import AkgentStateObserver, BaseState
|
|
30
|
+
from akgentic.core.orchestrator import EventSubscriber, Orchestrator
|
|
31
|
+
from akgentic.core.user_proxy import UserProxy
|
|
32
|
+
|
|
33
|
+
__all__ = [
|
|
34
|
+
# Agent base class and proxies
|
|
35
|
+
"Akgent",
|
|
36
|
+
"AkgentDeserializeContext",
|
|
37
|
+
# Actor system
|
|
38
|
+
"ActorSystem",
|
|
39
|
+
"ExecutionContext",
|
|
40
|
+
"Statistics",
|
|
41
|
+
# Actor addressing
|
|
42
|
+
"ActorAddress",
|
|
43
|
+
"ActorAddressImpl",
|
|
44
|
+
"ActorAddressProxy",
|
|
45
|
+
"ActorAddressStopped",
|
|
46
|
+
# Agent configuration
|
|
47
|
+
"AgentConfig",
|
|
48
|
+
"BaseConfig",
|
|
49
|
+
# Agent card (profiles)
|
|
50
|
+
"AgentCard",
|
|
51
|
+
# Agent state
|
|
52
|
+
"AkgentStateObserver",
|
|
53
|
+
"BaseState",
|
|
54
|
+
# Orchestrator
|
|
55
|
+
"Orchestrator",
|
|
56
|
+
"EventSubscriber",
|
|
57
|
+
# UserProxy
|
|
58
|
+
"UserProxy",
|
|
59
|
+
]
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"""ActorAddress abstraction for agent references and communication.
|
|
2
|
+
|
|
3
|
+
Provides an abstract interface for agent addressing that decouples agent identity
|
|
4
|
+
from the underlying actor implementation (Pykka). Supports serialization for
|
|
5
|
+
Phase 3 remote communication and rich metadata access.
|
|
6
|
+
|
|
7
|
+
Phase 3 Extensibility:
|
|
8
|
+
This module defines the ActorAddress ABC that can be extended for remote
|
|
9
|
+
communication. Future implementations may include:
|
|
10
|
+
- ActorAddressRemote: HTTP-based remote actor communication
|
|
11
|
+
- ActorAddressRedis: Redis pub/sub based communication
|
|
12
|
+
|
|
13
|
+
Example:
|
|
14
|
+
>>> # Using ActorAddress in an Agent (future Agent class)
|
|
15
|
+
>>> class Agent(pykka.ThreadingActor):
|
|
16
|
+
... @property
|
|
17
|
+
... def myAddress(self) -> ActorAddress:
|
|
18
|
+
... return ActorAddressImpl(self.actor_ref)
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import uuid
|
|
24
|
+
from abc import ABC, abstractmethod
|
|
25
|
+
from typing import TYPE_CHECKING, Any
|
|
26
|
+
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
from akgentic.core.utils.deserializer import ActorAddressDict
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ActorAddress(ABC):
|
|
32
|
+
"""Abstract base class for actor address/reference.
|
|
33
|
+
|
|
34
|
+
Provides a unified interface for agent identification and communication
|
|
35
|
+
that is independent of the underlying actor system (Pykka). All agent
|
|
36
|
+
references should use this abstraction rather than raw ActorRef.
|
|
37
|
+
|
|
38
|
+
The address provides rich metadata (name, role, team_id, squad_id) without
|
|
39
|
+
exposing internal implementation details. Implementations handle different
|
|
40
|
+
scenarios: live local actors, deserialized proxy addresses, and stopped actors.
|
|
41
|
+
|
|
42
|
+
Attributes:
|
|
43
|
+
agent_id: Unique identifier for the agent.
|
|
44
|
+
name: Agent name from configuration.
|
|
45
|
+
role: Agent role from configuration.
|
|
46
|
+
team_id: Team identifier from configuration.
|
|
47
|
+
squad_id: Squad identifier from configuration.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
@abstractmethod
|
|
52
|
+
def agent_id(self) -> uuid.UUID:
|
|
53
|
+
"""Unique identifier for this agent.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
UUID uniquely identifying this agent instance.
|
|
57
|
+
"""
|
|
58
|
+
...
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
@abstractmethod
|
|
62
|
+
def name(self) -> str:
|
|
63
|
+
"""Agent name from configuration.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
Human-readable name for this agent.
|
|
67
|
+
"""
|
|
68
|
+
...
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
@abstractmethod
|
|
72
|
+
def role(self) -> str:
|
|
73
|
+
"""Agent role from configuration.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
Role string describing agent's function.
|
|
77
|
+
"""
|
|
78
|
+
...
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
@abstractmethod
|
|
82
|
+
def team_id(self) -> uuid.UUID:
|
|
83
|
+
"""Team identifier from configuration.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
UUID of the team this agent belongs to.
|
|
87
|
+
"""
|
|
88
|
+
...
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
@abstractmethod
|
|
92
|
+
def squad_id(self) -> uuid.UUID | None:
|
|
93
|
+
"""Squad identifier from configuration.
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
UUID of the squad this agent belongs to, or None if not set.
|
|
97
|
+
"""
|
|
98
|
+
...
|
|
99
|
+
|
|
100
|
+
@abstractmethod
|
|
101
|
+
def send(self, recipient: ActorAddress, message: Any) -> None:
|
|
102
|
+
"""Send a message from this agent to the recipient.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
recipient: The target actor to receive the message, or None if unspecified.
|
|
106
|
+
message: The message to send (typically a Message subclass).
|
|
107
|
+
|
|
108
|
+
Raises:
|
|
109
|
+
RuntimeError: If this address cannot send messages (e.g., proxy address).
|
|
110
|
+
"""
|
|
111
|
+
...
|
|
112
|
+
|
|
113
|
+
@abstractmethod
|
|
114
|
+
def is_alive(self) -> bool:
|
|
115
|
+
"""Check if this actor is still running.
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
True if the actor is alive and can receive messages, False otherwise.
|
|
119
|
+
"""
|
|
120
|
+
...
|
|
121
|
+
|
|
122
|
+
@abstractmethod
|
|
123
|
+
def handle_user_message(self) -> bool:
|
|
124
|
+
"""Check if this agent accepts user messages.
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
True if the agent can process UserMessage instances.
|
|
128
|
+
"""
|
|
129
|
+
...
|
|
130
|
+
|
|
131
|
+
@abstractmethod
|
|
132
|
+
def serialize(self) -> ActorAddressDict:
|
|
133
|
+
"""Serialize this address to a dictionary for transport.
|
|
134
|
+
|
|
135
|
+
Produces an ActorAddressDict suitable for JSON serialization
|
|
136
|
+
and Phase 3 remote communication.
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
Dictionary representation of this address.
|
|
140
|
+
"""
|
|
141
|
+
...
|
|
142
|
+
|
|
143
|
+
@abstractmethod
|
|
144
|
+
def __repr__(self) -> str:
|
|
145
|
+
"""String representation for debugging.
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
String showing class name, agent name, and ID.
|
|
149
|
+
"""
|
|
150
|
+
...
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
"""Concrete ActorAddress implementations.
|
|
2
|
+
|
|
3
|
+
Provides three ActorAddress implementations for different use cases:
|
|
4
|
+
- ActorAddressImpl: Wraps live Pykka ActorRef for local actors
|
|
5
|
+
- ActorAddressProxy: Represents deserialized/mock addresses
|
|
6
|
+
- ActorAddressStopped: Represents actors that have been stopped
|
|
7
|
+
|
|
8
|
+
Phase 3 Extensibility:
|
|
9
|
+
Additional implementations can be added for remote communication:
|
|
10
|
+
- ActorAddressRemote: HTTP-based remote actor addressing
|
|
11
|
+
- ActorAddressRedis: Redis pub/sub based addressing
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import uuid
|
|
17
|
+
from typing import TYPE_CHECKING, Any
|
|
18
|
+
|
|
19
|
+
from akgentic.core.actor_address import ActorAddress
|
|
20
|
+
from akgentic.core.utils.deserializer import ActorAddressDict
|
|
21
|
+
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
from pykka import ActorRef
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ActorAddressImpl(ActorAddress):
|
|
27
|
+
"""ActorAddress wrapping a live Pykka ActorRef.
|
|
28
|
+
|
|
29
|
+
Provides access to agent metadata by reading from the underlying actor
|
|
30
|
+
instance. Used for local in-memory actor communication.
|
|
31
|
+
|
|
32
|
+
Note:
|
|
33
|
+
This implementation accesses the Pykka 4.4.2+ weakref API
|
|
34
|
+
(_actor_ref._actor_weakref) to dereference the underlying actor.
|
|
35
|
+
Direct property access on a GC'd actor raises RuntimeError.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
actor_ref: The Pykka ActorRef to wrap.
|
|
39
|
+
|
|
40
|
+
Example:
|
|
41
|
+
>>> actor_ref = MyAgent.start(config=agent_config)
|
|
42
|
+
>>> address = ActorAddressImpl(actor_ref)
|
|
43
|
+
>>> print(address.name)
|
|
44
|
+
'my-agent'
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
def __init__(self, actor_ref: ActorRef[Any]) -> None:
|
|
48
|
+
"""Initialize with a Pykka ActorRef.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
actor_ref: The Pykka ActorRef to wrap.
|
|
52
|
+
"""
|
|
53
|
+
self._actor_ref = actor_ref
|
|
54
|
+
|
|
55
|
+
def _resolve_actor(self) -> Any:
|
|
56
|
+
"""Dereference the weak reference to the underlying actor.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
The live actor instance.
|
|
60
|
+
|
|
61
|
+
Raises:
|
|
62
|
+
RuntimeError: If the actor has been garbage collected.
|
|
63
|
+
"""
|
|
64
|
+
actor = self._actor_ref._actor_weakref()
|
|
65
|
+
if actor is None:
|
|
66
|
+
raise RuntimeError(f"Actor {self._actor_ref.actor_urn} has been garbage collected")
|
|
67
|
+
return actor
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def agent_id(self) -> uuid.UUID:
|
|
71
|
+
"""Unique identifier from the underlying actor.
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
UUID from the actor's agent_id attribute.
|
|
75
|
+
"""
|
|
76
|
+
return self._resolve_actor().agent_id # type: ignore[no-any-return]
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def name(self) -> str:
|
|
80
|
+
"""Agent name from the underlying actor's config.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
Name string from config, or string representation of actor_ref as fallback.
|
|
84
|
+
"""
|
|
85
|
+
actor = self._resolve_actor()
|
|
86
|
+
return actor.config.name # type: ignore[no-any-return]
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
def role(self) -> str:
|
|
90
|
+
"""Agent role from the underlying actor's config.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
Role string from config, or class name as fallback.
|
|
94
|
+
"""
|
|
95
|
+
actor = self._resolve_actor()
|
|
96
|
+
return actor.config.role # type: ignore[no-any-return]
|
|
97
|
+
|
|
98
|
+
@property
|
|
99
|
+
def team_id(self) -> uuid.UUID:
|
|
100
|
+
"""Team identifier from the underlying actor.
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
UUID from team_id.
|
|
104
|
+
"""
|
|
105
|
+
actor = self._resolve_actor()
|
|
106
|
+
return actor.team_id # type: ignore[no-any-return]
|
|
107
|
+
|
|
108
|
+
@property
|
|
109
|
+
def squad_id(self) -> uuid.UUID | None:
|
|
110
|
+
"""Squad identifier from the underlying actor's config.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
UUID from config.squad_id, or None if not available.
|
|
114
|
+
"""
|
|
115
|
+
actor = self._resolve_actor()
|
|
116
|
+
return actor.config.squad_id # type: ignore[no-any-return]
|
|
117
|
+
|
|
118
|
+
def send(self, recipient: ActorAddress, message: Any) -> None:
|
|
119
|
+
"""Send a message via Pykka proxy.
|
|
120
|
+
|
|
121
|
+
Uses the actor's send method via proxy to deliver the message.
|
|
122
|
+
Blocks until the message is delivered.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
recipient: The intended recipient of the message.
|
|
126
|
+
message: The message to send.
|
|
127
|
+
"""
|
|
128
|
+
self._actor_ref.proxy().send(recipient, message).get()
|
|
129
|
+
|
|
130
|
+
def is_alive(self) -> bool:
|
|
131
|
+
"""Check if the underlying actor is still running.
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
True if the Pykka actor is alive.
|
|
135
|
+
"""
|
|
136
|
+
return self._actor_ref.is_alive()
|
|
137
|
+
|
|
138
|
+
def handle_user_message(self) -> bool:
|
|
139
|
+
"""Check if the agent accepts user messages.
|
|
140
|
+
|
|
141
|
+
Checks for the existence of a receiveMsg_UserMessage method on the actor.
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
True if the actor has a receiveMsg_UserMessage method.
|
|
145
|
+
"""
|
|
146
|
+
actor = self._resolve_actor()
|
|
147
|
+
accept_method = getattr(actor, "receiveMsg_UserMessage", None)
|
|
148
|
+
return callable(accept_method)
|
|
149
|
+
|
|
150
|
+
def serialize(self) -> ActorAddressDict:
|
|
151
|
+
"""Serialize to dictionary for transport.
|
|
152
|
+
|
|
153
|
+
Captures all metadata from the live actor for reconstruction.
|
|
154
|
+
The __actor_type__ is set to the actual agent class, not the address wrapper.
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
ActorAddressDict with all actor metadata.
|
|
158
|
+
"""
|
|
159
|
+
agent_type = self._resolve_actor().__class__
|
|
160
|
+
return {
|
|
161
|
+
"__actor_address__": True,
|
|
162
|
+
"__actor_type__": f"{agent_type.__module__}.{agent_type.__name__}",
|
|
163
|
+
"agent_id": str(self.agent_id),
|
|
164
|
+
"name": self.name,
|
|
165
|
+
"role": self.role,
|
|
166
|
+
"team_id": str(self.team_id),
|
|
167
|
+
"squad_id": str(self.squad_id) if self.squad_id is not None else "",
|
|
168
|
+
"user_message": self.handle_user_message(),
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
def __repr__(self) -> str:
|
|
172
|
+
"""String representation for debugging."""
|
|
173
|
+
return f"<ActorAddress {self.role} {self.name} ({self.agent_id})>"
|
|
174
|
+
|
|
175
|
+
def __eq__(self, other: object) -> bool:
|
|
176
|
+
"""Compare addresses by agent_id (same class only)."""
|
|
177
|
+
if not isinstance(other, ActorAddressImpl):
|
|
178
|
+
return False
|
|
179
|
+
return self.agent_id == other.agent_id
|
|
180
|
+
|
|
181
|
+
def __hash__(self) -> int:
|
|
182
|
+
"""Hash based on agent_id."""
|
|
183
|
+
return hash(self.agent_id)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
class ActorAddressProxy(ActorAddress):
|
|
187
|
+
"""ActorAddress representing a deserialized or mock address.
|
|
188
|
+
|
|
189
|
+
Constructed from an ActorAddressDict, this class provides read-only
|
|
190
|
+
access to address metadata without an underlying live actor. Used for:
|
|
191
|
+
- Deserialized addresses from network messages
|
|
192
|
+
- Mock addresses in tests
|
|
193
|
+
- Phase 3 remote agent references
|
|
194
|
+
|
|
195
|
+
Note:
|
|
196
|
+
Cannot send messages - will raise RuntimeError if attempted.
|
|
197
|
+
Assumes actor is alive (is_alive returns True).
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
address_dict: The ActorAddressDict containing address metadata.
|
|
201
|
+
|
|
202
|
+
Example:
|
|
203
|
+
>>> data = {"__actor_address__": True, "agent_id": "...", ...}
|
|
204
|
+
>>> proxy = ActorAddressProxy(data)
|
|
205
|
+
>>> print(proxy.name)
|
|
206
|
+
'remote-agent'
|
|
207
|
+
"""
|
|
208
|
+
|
|
209
|
+
def __init__(self, address_dict: ActorAddressDict) -> None:
|
|
210
|
+
"""Initialize from ActorAddressDict.
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
address_dict: Dictionary containing all address metadata.
|
|
214
|
+
"""
|
|
215
|
+
self.actor_address_dict = address_dict
|
|
216
|
+
|
|
217
|
+
@property
|
|
218
|
+
def agent_id(self) -> uuid.UUID:
|
|
219
|
+
"""Unique identifier from the stored dictionary.
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
UUID parsed from the agent_id string.
|
|
223
|
+
"""
|
|
224
|
+
return uuid.UUID(self.actor_address_dict["agent_id"])
|
|
225
|
+
|
|
226
|
+
@property
|
|
227
|
+
def name(self) -> str:
|
|
228
|
+
"""Agent name from the stored dictionary.
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
Name string from the dictionary.
|
|
232
|
+
"""
|
|
233
|
+
return self.actor_address_dict["name"]
|
|
234
|
+
|
|
235
|
+
@property
|
|
236
|
+
def role(self) -> str:
|
|
237
|
+
"""Agent role from the stored dictionary.
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
Role string from the dictionary.
|
|
241
|
+
"""
|
|
242
|
+
return self.actor_address_dict["role"]
|
|
243
|
+
|
|
244
|
+
@property
|
|
245
|
+
def team_id(self) -> uuid.UUID:
|
|
246
|
+
"""Team identifier from the stored dictionary.
|
|
247
|
+
|
|
248
|
+
Returns:
|
|
249
|
+
UUID parsed from the team_id string.
|
|
250
|
+
"""
|
|
251
|
+
return uuid.UUID(self.actor_address_dict["team_id"])
|
|
252
|
+
|
|
253
|
+
@property
|
|
254
|
+
def squad_id(self) -> uuid.UUID | None:
|
|
255
|
+
"""Squad identifier from the stored dictionary.
|
|
256
|
+
|
|
257
|
+
Returns:
|
|
258
|
+
UUID parsed from the squad_id string, or None if not present.
|
|
259
|
+
"""
|
|
260
|
+
squad_id_str = self.actor_address_dict.get("squad_id")
|
|
261
|
+
return uuid.UUID(squad_id_str) if squad_id_str else None
|
|
262
|
+
|
|
263
|
+
def send(self, recipient: ActorAddress, message: Any) -> None:
|
|
264
|
+
"""Send a message via Pykka proxy.
|
|
265
|
+
|
|
266
|
+
Args:
|
|
267
|
+
recipient: Not used.
|
|
268
|
+
message: Not used.
|
|
269
|
+
|
|
270
|
+
Raises:
|
|
271
|
+
RuntimeError: Always, as proxy addresses cannot send.
|
|
272
|
+
"""
|
|
273
|
+
raise RuntimeError(
|
|
274
|
+
f"Cannot send message from mock actor address {self.name} - actor not alive"
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
def is_alive(self) -> bool:
|
|
278
|
+
"""Assume the remote/proxied actor is alive.
|
|
279
|
+
|
|
280
|
+
Returns:
|
|
281
|
+
True (assumed alive for proxy addresses).
|
|
282
|
+
"""
|
|
283
|
+
return True
|
|
284
|
+
|
|
285
|
+
def handle_user_message(self) -> bool:
|
|
286
|
+
"""Check if this agent accepts user messages.
|
|
287
|
+
|
|
288
|
+
Returns:
|
|
289
|
+
Boolean from the stored dictionary, defaults to False if not present.
|
|
290
|
+
"""
|
|
291
|
+
return self.actor_address_dict.get("user_message", False)
|
|
292
|
+
|
|
293
|
+
def serialize(self) -> ActorAddressDict:
|
|
294
|
+
"""Return the stored dictionary.
|
|
295
|
+
|
|
296
|
+
Returns:
|
|
297
|
+
The original ActorAddressDict.
|
|
298
|
+
"""
|
|
299
|
+
return self.actor_address_dict
|
|
300
|
+
|
|
301
|
+
def __repr__(self) -> str:
|
|
302
|
+
"""String representation for debugging."""
|
|
303
|
+
return f"<ActorAddressProxy {self.role} {self.name} ({self.agent_id})>"
|
|
304
|
+
|
|
305
|
+
def __eq__(self, other: object) -> bool:
|
|
306
|
+
"""Compare addresses by agent_id (same class only)."""
|
|
307
|
+
if not isinstance(other, ActorAddressProxy):
|
|
308
|
+
return False
|
|
309
|
+
return self.agent_id == other.agent_id
|
|
310
|
+
|
|
311
|
+
def __hash__(self) -> int:
|
|
312
|
+
"""Hash based on agent_id."""
|
|
313
|
+
return hash(self.agent_id)
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
class ActorAddressStopped(ActorAddressProxy):
|
|
317
|
+
"""ActorAddress representing a stopped actor.
|
|
318
|
+
|
|
319
|
+
Inherits from ActorAddressProxy but overrides is_alive to return False.
|
|
320
|
+
Used for tracking stopped actors in orchestrator history.
|
|
321
|
+
|
|
322
|
+
Args:
|
|
323
|
+
address_dict: The ActorAddressDict containing address metadata.
|
|
324
|
+
|
|
325
|
+
Example:
|
|
326
|
+
>>> stopped = ActorAddressStopped(original_address.serialize())
|
|
327
|
+
>>> stopped.is_alive()
|
|
328
|
+
False
|
|
329
|
+
"""
|
|
330
|
+
|
|
331
|
+
def is_alive(self) -> bool:
|
|
332
|
+
"""Stopped actors are not alive.
|
|
333
|
+
|
|
334
|
+
Returns:
|
|
335
|
+
False (actor has been stopped).
|
|
336
|
+
"""
|
|
337
|
+
return False
|
|
338
|
+
|
|
339
|
+
def __repr__(self) -> str:
|
|
340
|
+
"""String representation for debugging."""
|
|
341
|
+
return f"<ActorAddressStopped {self.role} {self.name} ({self.agent_id})>"
|
|
342
|
+
|
|
343
|
+
def __eq__(self, other: object) -> bool:
|
|
344
|
+
"""Compare addresses by agent_id (same class only)."""
|
|
345
|
+
if not isinstance(other, ActorAddressStopped):
|
|
346
|
+
return False
|
|
347
|
+
return self.agent_id == other.agent_id
|