econagents 0.0.2__py3-none-any.whl → 0.0.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.
- econagents/__init__.py +1 -1
- econagents/core/agent_role.py +13 -4
- econagents/core/game_runner.py +11 -2
- econagents/core/manager/base.py +64 -3
- econagents/core/manager/phase.py +2 -2
- econagents/core/state/game.py +23 -1
- {econagents-0.0.2.dist-info → econagents-0.0.3.dist-info}/METADATA +10 -26
- {econagents-0.0.2.dist-info → econagents-0.0.3.dist-info}/RECORD +10 -10
- {econagents-0.0.2.dist-info → econagents-0.0.3.dist-info}/LICENSE +0 -0
- {econagents-0.0.2.dist-info → econagents-0.0.3.dist-info}/WHEEL +0 -0
econagents/__init__.py
CHANGED
@@ -12,7 +12,7 @@ from econagents.core.state.game import GameState, MetaInformation, PrivateInform
|
|
12
12
|
from econagents.llm.openai import ChatOpenAI
|
13
13
|
|
14
14
|
# Don't manually change, let poetry-dynamic-versioning handle it.
|
15
|
-
__version__ = "0.0.
|
15
|
+
__version__ = "0.0.3"
|
16
16
|
|
17
17
|
__all__: list[str] = [
|
18
18
|
"AgentRole",
|
econagents/core/agent_role.py
CHANGED
@@ -3,6 +3,7 @@ import logging
|
|
3
3
|
import re
|
4
4
|
from abc import ABC
|
5
5
|
from pathlib import Path
|
6
|
+
from jinja2 import FileSystemLoader
|
6
7
|
from typing import Any, Callable, ClassVar, Dict, Generic, Literal, Optional, Pattern, Protocol, TypeVar
|
7
8
|
|
8
9
|
from jinja2.sandbox import SandboxedEnvironment
|
@@ -129,12 +130,20 @@ class AgentRole(ABC, Generic[StateT_contra], LoggerMixin):
|
|
129
130
|
Raises:
|
130
131
|
FileNotFoundError: If no matching prompt template is found
|
131
132
|
"""
|
133
|
+
# Initialize Jinja environment with a file system loader
|
134
|
+
env = SandboxedEnvironment(loader=FileSystemLoader(prompts_path))
|
135
|
+
|
132
136
|
# Try role-specific prompt first, then fall back to 'all'
|
133
137
|
for role in [self.name, "all"]:
|
134
|
-
if
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
+
if prompt_file_path := self._resolve_prompt_file(prompt_type, phase, role, prompts_path):
|
139
|
+
# Get filename relative to the prompts_path for the loader
|
140
|
+
template_filename = str(prompt_file_path.relative_to(prompts_path))
|
141
|
+
try:
|
142
|
+
template = env.get_template(template_filename)
|
143
|
+
return template.render(**context)
|
144
|
+
except Exception as e: # Catch potential Jinja errors during loading/rendering
|
145
|
+
self.logger.error(f"Error loading/rendering template {template_filename}: {e}")
|
146
|
+
raise # Re-raise after logging
|
138
147
|
|
139
148
|
raise FileNotFoundError(
|
140
149
|
f"No prompt template found for type={prompt_type}, phase={phase}, "
|
econagents/core/game_runner.py
CHANGED
@@ -63,6 +63,10 @@ class GameRunnerConfig(BaseModel):
|
|
63
63
|
observability_provider: Optional[Literal["langsmith", "langfuse"]] = None
|
64
64
|
"""Name of the observability provider to use. Options: 'langsmith' or 'langfuse'"""
|
65
65
|
|
66
|
+
# Agent stop configuration
|
67
|
+
end_game_event: str = "game-over"
|
68
|
+
"""Event type that signals the end of the game and should stop the agent."""
|
69
|
+
|
66
70
|
|
67
71
|
class TurnBasedGameRunnerConfig(GameRunnerConfig):
|
68
72
|
"""Configuration class for TurnBasedGameRunner."""
|
@@ -303,6 +307,10 @@ class GameRunner:
|
|
303
307
|
agent_manager.auth_mechanism = self.config.auth_mechanism
|
304
308
|
agent_manager.logger.debug(f"Injected default auth mechanism: {agent_manager.auth_mechanism}")
|
305
309
|
|
310
|
+
if agent_manager.end_game_event_type != self.config.end_game_event:
|
311
|
+
agent_manager.end_game_event_type = self.config.end_game_event
|
312
|
+
agent_manager.logger.debug(f"Injected default end game event: {agent_manager.end_game_event_type}")
|
313
|
+
|
306
314
|
if agent_manager.llm_provider and self.config.observability_provider:
|
307
315
|
try:
|
308
316
|
provider = get_observability_provider(self.config.observability_provider)
|
@@ -349,7 +357,7 @@ class GameRunner:
|
|
349
357
|
agent_manager.logger.info(f"Connecting to WebSocket URL: {agent_manager.url}")
|
350
358
|
await agent_manager.start()
|
351
359
|
except Exception:
|
352
|
-
agent_manager.logger.exception(f"Error in
|
360
|
+
agent_manager.logger.exception(f"Error in game for Agent {agent_id}")
|
353
361
|
raise
|
354
362
|
|
355
363
|
async def run_game(self) -> None:
|
@@ -360,7 +368,7 @@ class GameRunner:
|
|
360
368
|
|
361
369
|
try:
|
362
370
|
tasks = []
|
363
|
-
game_logger.info("Starting
|
371
|
+
game_logger.info("Starting game")
|
364
372
|
|
365
373
|
for i, agent_manager in enumerate(self.agents, start=1):
|
366
374
|
tasks.append(self.spawn_agent(agent_manager, i))
|
@@ -369,4 +377,5 @@ class GameRunner:
|
|
369
377
|
game_logger.exception(f"Failed to run game: {e}")
|
370
378
|
raise
|
371
379
|
finally:
|
380
|
+
game_logger.info("Game over")
|
372
381
|
self.cleanup_logging()
|
econagents/core/manager/base.py
CHANGED
@@ -68,6 +68,10 @@ class AgentManager(LoggerMixin):
|
|
68
68
|
self._global_pre_event_hooks: list[Callable[[Message], Any]] = []
|
69
69
|
self._global_post_event_hooks: list[Callable[[Message], Any]] = []
|
70
70
|
|
71
|
+
# Default event type to trigger stopping the agent
|
72
|
+
self._end_game_event_type: str = "game-over"
|
73
|
+
self.register_event_handler(self._end_game_event_type, self._handle_end_game)
|
74
|
+
|
71
75
|
# Initialize transport if URL is provided
|
72
76
|
if url:
|
73
77
|
self._initialize_transport()
|
@@ -156,6 +160,30 @@ class AgentManager(LoggerMixin):
|
|
156
160
|
return None
|
157
161
|
return Message(message_type=message_type, event_type=event_type, data=data)
|
158
162
|
|
163
|
+
@property
|
164
|
+
def end_game_event_type(self) -> str:
|
165
|
+
"""Get the event type that triggers the agent to stop."""
|
166
|
+
return self._end_game_event_type
|
167
|
+
|
168
|
+
@end_game_event_type.setter
|
169
|
+
def end_game_event_type(self, value: str):
|
170
|
+
"""
|
171
|
+
Set the event type that triggers the agent to stop.
|
172
|
+
|
173
|
+
Unregisters the stop handler from the old event type and registers it
|
174
|
+
for the new event type.
|
175
|
+
|
176
|
+
Args:
|
177
|
+
value (str): The new event type to listen for.
|
178
|
+
"""
|
179
|
+
if self._end_game_event_type != value:
|
180
|
+
# Unregister from the old event type
|
181
|
+
self.unregister_event_handler(self._end_game_event_type, self._handle_end_game)
|
182
|
+
# Register for the new event type
|
183
|
+
self._end_game_event_type = value
|
184
|
+
self.register_event_handler(self._end_game_event_type, self._handle_end_game)
|
185
|
+
self.logger.info(f"End game event type set to: {value}")
|
186
|
+
|
159
187
|
async def on_message(self, message: Message):
|
160
188
|
"""
|
161
189
|
Default implementation to handle incoming messages from the server.
|
@@ -202,6 +230,7 @@ class AgentManager(LoggerMixin):
|
|
202
230
|
self.running = False
|
203
231
|
if self.transport:
|
204
232
|
await self.transport.stop()
|
233
|
+
self.logger.info("Agent manager stopped and connection closed.")
|
205
234
|
|
206
235
|
async def on_event(self, message: Message):
|
207
236
|
"""
|
@@ -270,6 +299,16 @@ class AgentManager(LoggerMixin):
|
|
270
299
|
if hasattr(result, "__await__"):
|
271
300
|
await result
|
272
301
|
|
302
|
+
async def _handle_end_game(self, message: Message):
|
303
|
+
"""
|
304
|
+
Default handler for the 'end_game_event_type'. Stops the agent manager.
|
305
|
+
|
306
|
+
Args:
|
307
|
+
message (Message): The event message triggering the end game.
|
308
|
+
"""
|
309
|
+
self.logger.info(f"Received end game event ({message.event_type}). Stopping agent manager...")
|
310
|
+
await self.stop()
|
311
|
+
|
273
312
|
# Event handler registration
|
274
313
|
def register_event_handler(self, event_type: str, handler: Callable[[Message], Any]):
|
275
314
|
"""
|
@@ -355,7 +394,17 @@ class AgentManager(LoggerMixin):
|
|
355
394
|
if handler is None:
|
356
395
|
self._event_handlers.pop(event_type)
|
357
396
|
else:
|
358
|
-
|
397
|
+
# Use list comprehension to avoid modifying list while iterating
|
398
|
+
handlers_to_keep = [h for h in self._event_handlers[event_type] if h != handler]
|
399
|
+
if not handlers_to_keep:
|
400
|
+
# Remove the key if the list becomes empty
|
401
|
+
self._event_handlers.pop(event_type, None)
|
402
|
+
else:
|
403
|
+
self._event_handlers[event_type] = handlers_to_keep
|
404
|
+
# Explicitly handle removal of the default end game handler
|
405
|
+
if event_type == self._end_game_event_type and handler == self._handle_end_game:
|
406
|
+
self.logger.warning(f"Default end game handler for '{event_type}' unregistered.")
|
407
|
+
|
359
408
|
return self
|
360
409
|
|
361
410
|
def unregister_global_event_handler(self, handler: Optional[Callable] = None):
|
@@ -384,7 +433,13 @@ class AgentManager(LoggerMixin):
|
|
384
433
|
if hook is None:
|
385
434
|
self._pre_event_hooks.pop(event_type)
|
386
435
|
else:
|
387
|
-
|
436
|
+
# Use list comprehension to avoid modifying list while iterating
|
437
|
+
hooks_to_keep = [h for h in self._pre_event_hooks[event_type] if h != hook]
|
438
|
+
if not hooks_to_keep:
|
439
|
+
# Remove the key if the list becomes empty
|
440
|
+
self._pre_event_hooks.pop(event_type, None)
|
441
|
+
else:
|
442
|
+
self._pre_event_hooks[event_type] = hooks_to_keep
|
388
443
|
return self
|
389
444
|
|
390
445
|
def unregister_global_pre_event_hook(self, hook: Optional[Callable] = None):
|
@@ -413,7 +468,13 @@ class AgentManager(LoggerMixin):
|
|
413
468
|
if hook is None:
|
414
469
|
self._post_event_hooks.pop(event_type)
|
415
470
|
else:
|
416
|
-
|
471
|
+
# Use list comprehension to avoid modifying list while iterating
|
472
|
+
hooks_to_keep = [h for h in self._post_event_hooks[event_type] if h != hook]
|
473
|
+
if not hooks_to_keep:
|
474
|
+
# Remove the key if the list becomes empty
|
475
|
+
self._post_event_hooks.pop(event_type, None)
|
476
|
+
else:
|
477
|
+
self._post_event_hooks[event_type] = hooks_to_keep
|
417
478
|
return self
|
418
479
|
|
419
480
|
def unregister_global_post_event_hook(self, hook: Optional[Callable] = None):
|
econagents/core/manager/phase.py
CHANGED
@@ -3,14 +3,14 @@ import json
|
|
3
3
|
import logging
|
4
4
|
import random
|
5
5
|
from abc import ABC, abstractmethod
|
6
|
-
from typing import Any, Callable, Optional
|
7
6
|
from pathlib import Path
|
7
|
+
from typing import Any, Callable, Optional
|
8
8
|
|
9
9
|
from econagents.core.agent_role import AgentRole
|
10
10
|
from econagents.core.events import Message
|
11
11
|
from econagents.core.manager.base import AgentManager
|
12
12
|
from econagents.core.state.game import GameState
|
13
|
-
from econagents.core.transport import AuthenticationMechanism
|
13
|
+
from econagents.core.transport import AuthenticationMechanism
|
14
14
|
|
15
15
|
|
16
16
|
class PhaseManager(AgentManager, ABC):
|
econagents/core/state/game.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
from typing import Any, Callable, Optional, Protocol, Type, TypeVar
|
1
|
+
from typing import Any, Callable, Optional, Protocol, Type, TypeVar, cast
|
2
2
|
|
3
3
|
from pydantic import BaseModel, ConfigDict
|
4
4
|
|
@@ -220,3 +220,25 @@ class GameState(BaseModel):
|
|
220
220
|
dict[str, EventHandler]: A mapping of event types to handler functions.
|
221
221
|
"""
|
222
222
|
return {}
|
223
|
+
|
224
|
+
def reset(self) -> None:
|
225
|
+
"""
|
226
|
+
Resets meta, private_information, and public_information
|
227
|
+
to their initial state by re-initializing them using their default factories.
|
228
|
+
This effectively removes any dynamically added attributes.
|
229
|
+
"""
|
230
|
+
# Re-initialize components using their default factories from the GameState model definition
|
231
|
+
meta_field = self.__class__.model_fields["meta"]
|
232
|
+
if meta_field.default_factory:
|
233
|
+
fac = cast("Callable[[], Any]", meta_field.default_factory)
|
234
|
+
self.meta = fac()
|
235
|
+
|
236
|
+
private_field = self.__class__.model_fields["private_information"]
|
237
|
+
if private_field.default_factory:
|
238
|
+
fac = cast("Callable[[], Any]", private_field.default_factory)
|
239
|
+
self.private_information = fac()
|
240
|
+
|
241
|
+
public_field = self.__class__.model_fields["public_information"]
|
242
|
+
if public_field.default_factory:
|
243
|
+
fac = cast("Callable[[], Any]", public_field.default_factory)
|
244
|
+
self.public_information = fac()
|
@@ -1,41 +1,25 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: econagents
|
3
|
-
Version: 0.0.
|
4
|
-
Summary:
|
5
|
-
License: MIT
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
of this software and associated documentation files (the "Software"), to deal
|
11
|
-
in the Software without restriction, including without limitation the rights
|
12
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
13
|
-
copies of the Software, and to permit persons to whom the Software is
|
14
|
-
furnished to do so, subject to the following conditions:
|
15
|
-
|
16
|
-
The above copyright notice and this permission notice shall be included in all
|
17
|
-
copies or substantial portions of the Software.
|
18
|
-
|
19
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
20
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
21
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
22
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
23
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
24
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
25
|
-
SOFTWARE.
|
26
|
-
Requires-Python: >=3.10,<3.13
|
27
|
-
Classifier: License :: Other/Proprietary License
|
3
|
+
Version: 0.0.3
|
4
|
+
Summary:
|
5
|
+
License: MIT
|
6
|
+
Author: Dylan Castillo
|
7
|
+
Author-email: dylan@iwanalabs.com
|
8
|
+
Requires-Python: >=3.10,<4
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
28
10
|
Classifier: Programming Language :: Python :: 3
|
29
11
|
Classifier: Programming Language :: Python :: 3.10
|
30
12
|
Classifier: Programming Language :: Python :: 3.11
|
31
13
|
Classifier: Programming Language :: Python :: 3.12
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
32
15
|
Provides-Extra: all
|
33
16
|
Provides-Extra: default
|
34
17
|
Provides-Extra: langfuse
|
35
18
|
Provides-Extra: langsmith
|
36
19
|
Provides-Extra: ollama
|
37
20
|
Provides-Extra: openai
|
38
|
-
Requires-Dist:
|
21
|
+
Requires-Dist: jinja2 (>=3.1.6,<4.0.0)
|
22
|
+
Requires-Dist: pydantic (>=2.11.3,<3.0.0)
|
39
23
|
Requires-Dist: requests (>=2.32.3,<3.0.0)
|
40
24
|
Requires-Dist: websockets (>=15.0,<16.0)
|
41
25
|
Description-Content-Type: text/markdown
|
@@ -1,16 +1,16 @@
|
|
1
|
-
econagents/__init__.py,sha256=
|
1
|
+
econagents/__init__.py,sha256=gFWs83sRb_j9pq30PhclH3DNXifyHiPMz0IKN1O4xJk,1111
|
2
2
|
econagents/_c_extension.pyi,sha256=evVvDNUCGqyMPrNViPF7QXfGUNNIMbUdY5HemRNQ1_o,113
|
3
3
|
econagents/core/__init__.py,sha256=QZoOp6n5CX1j-Ob6PZgyCNY78vi2kWmd_LVLrJUj1TU,393
|
4
|
-
econagents/core/agent_role.py,sha256=
|
4
|
+
econagents/core/agent_role.py,sha256=MA0aaTEWiI5NLSKN2YmWHb8gBYljtDVlLOSpohkv45M,15895
|
5
5
|
econagents/core/events.py,sha256=hx-Ru_NoSISuN--7ZFC3CIql5hry3AATSnHZJJv3Kds,294
|
6
|
-
econagents/core/game_runner.py,sha256=
|
6
|
+
econagents/core/game_runner.py,sha256=lrujwoeL3lXwCInA9msu6YvovCJuBUTcV0DrnyryQDE,14099
|
7
7
|
econagents/core/logging_mixin.py,sha256=tYsRc5ngW-hzfElrb838KO-9-BGOPyUv2v5LLuJToBE,1421
|
8
8
|
econagents/core/manager/__init__.py,sha256=bDpCQlFcw_E-js575X3Xl6iwZ1uILC18An1vt6oE7S4,284
|
9
|
-
econagents/core/manager/base.py,sha256=
|
10
|
-
econagents/core/manager/phase.py,sha256=
|
9
|
+
econagents/core/manager/base.py,sha256=qJyqBaUz0dMz3XCny4YydJ8iV6mqEy5osKgL2lNozkc,19032
|
10
|
+
econagents/core/manager/phase.py,sha256=gx1ku_hPVmT-BgKTdc55BSZ56Qd3fN1smUgjPbYYtCs,19268
|
11
11
|
econagents/core/state/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
12
|
econagents/core/state/fields.py,sha256=YxVOqdriaHRHoyeXsIB8ZDHygneMJD1OikOyeILK_oA,1854
|
13
|
-
econagents/core/state/game.py,sha256=
|
13
|
+
econagents/core/state/game.py,sha256=6J3xV_SViShwBNgcWc_sK1dQPNO8Abb7qONFF0d4qVs,9729
|
14
14
|
econagents/core/state/market.py,sha256=Jg-X9mYH6B3cYOwxzjFDV5PDbCIYxipx2UN4ecfyyDE,3909
|
15
15
|
econagents/core/transport.py,sha256=7eq31nb2KY67RuL5i2kxJrcGtwfcVm5qy0eVj4_xWQw,5063
|
16
16
|
econagents/llm/__init__.py,sha256=J1PqpG3wL41oBAfzp5QaaP2ekwxCM_tiRq4H0OeSv4w,482
|
@@ -19,7 +19,7 @@ econagents/llm/observability.py,sha256=WSkJ7lZZl2FVpDWpFivudoeOENR_lyLZscUisveAi
|
|
19
19
|
econagents/llm/ollama.py,sha256=0ElnQhyCCAOu6u-TIC2JeNzh8OV6_VZjchFpi1F3HxU,2242
|
20
20
|
econagents/llm/openai.py,sha256=x1L9GS2DXNvPTECXP6DVMwr9fuhcHMZh_2k8ds30i-Y,2395
|
21
21
|
econagents/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
22
|
-
econagents-0.0.
|
23
|
-
econagents-0.0.
|
24
|
-
econagents-0.0.
|
25
|
-
econagents-0.0.
|
22
|
+
econagents-0.0.3.dist-info/LICENSE,sha256=Bd4MAEaMayyDO5BncOl3q0T2b6sWcjABigXLRFxgKIU,1082
|
23
|
+
econagents-0.0.3.dist-info/METADATA,sha256=Ks0NdoiMiHBGGWZmJpY3fPurimnzd38RnWX3lzN5KSE,3557
|
24
|
+
econagents-0.0.3.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
|
25
|
+
econagents-0.0.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|