From 585f7b6917c7e11fb0ed829ce8cffd21c1998273 Mon Sep 17 00:00:00 2001 From: Edward FitzGerald Date: Wed, 28 Aug 2024 17:13:52 +0100 Subject: [PATCH 1/9] wip: consistent session ids --- python/src/uagents/agent.py | 62 ++++++++++++++++---------- python/src/uagents/context.py | 11 ++--- python/src/uagents/wallet_messaging.py | 3 +- 3 files changed, 44 insertions(+), 32 deletions(-) diff --git a/python/src/uagents/agent.py b/python/src/uagents/agent.py index e2637a6f..3e2fd1c8 100644 --- a/python/src/uagents/agent.py +++ b/python/src/uagents/agent.py @@ -71,24 +71,27 @@ from uagents.utils import get_logger -async def _run_interval(func: IntervalCallback, ctx: Context, period: float): +async def _run_interval(func: IntervalCallback, agent: 'Agent', period: float): """ Run the provided interval callback function at a specified period. Args: func (IntervalCallback): The interval callback function to run. - ctx (Context): The context for the agent. + agent (Agent): The agent that is running the interval callback. period (float): The time period at which to run the callback function. """ + logger = agent._logger + while True: try: + ctx = agent._build_context() await func(ctx) except OSError as ex: - ctx.logger.exception(f"OS Error in interval handler: {ex}") + logger.exception(f"OS Error in interval handler: {ex}") except RuntimeError as ex: - ctx.logger.exception(f"Runtime Error in interval handler: {ex}") + logger.exception(f"Runtime Error in interval handler: {ex}") except Exception as ex: - ctx.logger.exception(f"Exception in interval handler: {ex}") + logger.exception(f"Exception in interval handler: {ex}") await asyncio.sleep(period) @@ -377,21 +380,6 @@ def __init__( # keep track of supported protocols self.protocols: Dict[str, Protocol] = {} - self._ctx = InternalContext( - agent=AgentRepresentation( - address=self.address, - name=self._name, - signing_callback=self._identity.sign_digest, - ), - storage=self._storage, - ledger=self._ledger, - resolver=self._resolver, - dispenser=self._dispenser, - interval_messages=self._interval_messages, - wallet_messaging_client=self._wallet_messaging_client, - logger=self._logger, - ) - # register with the dispatcher self._dispatcher.register(self.address, self) @@ -426,6 +414,27 @@ async def _handle_get_messages(_ctx: Context): self._init_done = True + def _build_context(self) -> InternalContext: + """ + An internal method to build the context for the agent + + @return: + """ + return InternalContext( + agent=AgentRepresentation( + address=self.address, + name=self._name, + signing_callback=self._identity.sign_digest, + ), + storage=self._storage, + ledger=self._ledger, + resolver=self._resolver, + dispenser=self._dispenser, + interval_messages=self._interval_messages, + wallet_messaging_client=self._wallet_messaging_client, + logger=self._logger, + ) + def _initialize_wallet_and_identity(self, seed, name, wallet_key_derivation_index): """ Initialize the wallet and identity for the agent. @@ -1015,7 +1024,8 @@ async def _startup(self): ) for handler in self._on_startup: try: - await handler(self._ctx) + ctx = self._build_context() + await handler(ctx) except OSError as ex: self._logger.exception(f"OS Error in startup handler: {ex}") except RuntimeError as ex: @@ -1030,7 +1040,8 @@ async def _shutdown(self): """ for handler in self._on_shutdown: try: - await handler(self._ctx) + ctx = self._build_context() + await handler(ctx) except OSError as ex: self._logger.exception(f"OS Error in shutdown handler: {ex}") except RuntimeError as ex: @@ -1061,7 +1072,7 @@ def start_interval_tasks(self): """ for func, period in self._interval_handlers: - self._loop.create_task(_run_interval(func, self._ctx, period)) + self._loop.create_task(_run_interval(func, self, period)) def start_message_receivers(self): """ @@ -1163,7 +1174,7 @@ async def _process_message_queue(self): ) context = ExternalContext( - agent=self._ctx.agent, + agent=self, storage=self._storage, ledger=self._ledger, resolver=self._resolver, @@ -1179,6 +1190,9 @@ async def _process_message_queue(self): protocol=protocol_info, ) + # sanity check + assert context.session == session, "Context object should always have message session" + # parse the received message try: recovered = model_class.parse_raw(message) diff --git a/python/src/uagents/context.py b/python/src/uagents/context.py index a6139170..8586b4e8 100644 --- a/python/src/uagents/context.py +++ b/python/src/uagents/context.py @@ -116,7 +116,7 @@ def logger(self) -> logging.Logger: @property @abstractmethod - def session(self) -> Union[uuid.UUID, None]: + def session(self) -> uuid.UUID: """ Get the session UUID associated with the context. @@ -256,6 +256,7 @@ def __init__( ledger: LedgerClient, resolver: Resolver, dispenser: Dispenser, + session: Optional[uuid.UUID] = None, interval_messages: Optional[Set[str]] = None, wallet_messaging_client: Optional[Any] = None, logger: Optional[logging.Logger] = None, @@ -266,7 +267,7 @@ def __init__( self._resolver = resolver self._dispenser = dispenser self._logger = logger - self._session: Optional[uuid.UUID] = None + self._session = session or uuid.uuid4() self._interval_messages = interval_messages self._wallet_messaging_client = wallet_messaging_client self._outbound_messages: Dict[str, Tuple[JsonStr, str]] = {} @@ -288,7 +289,7 @@ def logger(self) -> Union[logging.Logger, None]: return self._logger @property - def session(self) -> Union[uuid.UUID, None]: + def session(self) -> uuid.UUID: """ Get the session UUID associated with the context. @@ -408,7 +409,6 @@ async def send( we don't have access properties that are only necessary in re-active contexts, like 'replies', 'message_received', or 'protocol'. """ - self._session = None schema_digest = Model.build_schema_digest(message) message_body = message.model_dump_json() @@ -440,7 +440,6 @@ async def send_raw( protocol_digest: Optional[str] = None, queries: Optional[Dict[str, asyncio.Future]] = None, ) -> MsgStatus: - self._session = self._session or uuid.uuid4() # Extract address from destination agent identifier if present _, parsed_name, parsed_address = parse_identifier(destination) @@ -575,7 +574,6 @@ class ExternalContext(InternalContext): def __init__( self, message_received: MsgDigest, - session: Optional[uuid.UUID] = None, queries: Optional[Dict[str, asyncio.Future]] = None, replies: Optional[Dict[str, Dict[str, Type[Model]]]] = None, protocol: Optional[Tuple[str, Protocol]] = None, @@ -594,7 +592,6 @@ def __init__( protocol (Optional[Tuple[str, Protocol]]): The optional Tuple of protocols. """ super().__init__(**kwargs) - self._session = session or None self._queries = queries or {} self._replies = replies self._message_received = message_received diff --git a/python/src/uagents/wallet_messaging.py b/python/src/uagents/wallet_messaging.py index 2c86355a..9d3a4ab0 100644 --- a/python/src/uagents/wallet_messaging.py +++ b/python/src/uagents/wallet_messaging.py @@ -79,8 +79,9 @@ async def poll_server(self): ) await asyncio.sleep(self._poll_interval) - async def process_message_queue(self, ctx: Context): + async def process_message_queue(self, agent: 'Agent'): while True: msg: WalletMessage = await self._message_queue.get() for handler in self._message_handlers: + ctx = agent._build_context() await handler(ctx, msg) From e7f00e62f794e930a5ec63f13fe728cb60fca654 Mon Sep 17 00:00:00 2001 From: Edward FitzGerald Date: Mon, 16 Sep 2024 17:59:56 +0100 Subject: [PATCH 2/9] tests and fixes --- python/src/uagents/agent.py | 13 +++-- python/src/uagents/context.py | 1 - python/src/uagents/wallet_messaging.py | 3 +- python/tests/test_agent.py | 4 +- python/tests/test_context.py | 73 +++++++++++++++----------- 5 files changed, 54 insertions(+), 40 deletions(-) diff --git a/python/src/uagents/agent.py b/python/src/uagents/agent.py index 3e2fd1c8..c5d45344 100644 --- a/python/src/uagents/agent.py +++ b/python/src/uagents/agent.py @@ -71,7 +71,7 @@ from uagents.utils import get_logger -async def _run_interval(func: IntervalCallback, agent: 'Agent', period: float): +async def _run_interval(func: IntervalCallback, agent: "Agent", period: float): """ Run the provided interval callback function at a specified period. @@ -1006,7 +1006,10 @@ async def handle_rest( if not handler: return None - args = (self._ctx, message) if message else (self._ctx,) + context = self._build_context() + args = [context] + if message: + args.append(message) return await handler(*args) # type: ignore @@ -1086,7 +1089,7 @@ def start_message_receivers(self): if self._wallet_messaging_client is not None: for task in [ self._wallet_messaging_client.poll_server(), - self._wallet_messaging_client.process_message_queue(self._ctx), + self._wallet_messaging_client.process_message_queue(self), ]: self._loop.create_task(task) @@ -1191,7 +1194,9 @@ async def _process_message_queue(self): ) # sanity check - assert context.session == session, "Context object should always have message session" + assert ( + context.session == session + ), "Context object should always have message session" # parse the received message try: diff --git a/python/src/uagents/context.py b/python/src/uagents/context.py index 8586b4e8..d2929138 100644 --- a/python/src/uagents/context.py +++ b/python/src/uagents/context.py @@ -440,7 +440,6 @@ async def send_raw( protocol_digest: Optional[str] = None, queries: Optional[Dict[str, asyncio.Future]] = None, ) -> MsgStatus: - # Extract address from destination agent identifier if present _, parsed_name, parsed_address = parse_identifier(destination) diff --git a/python/src/uagents/wallet_messaging.py b/python/src/uagents/wallet_messaging.py index 9d3a4ab0..cee6de2f 100644 --- a/python/src/uagents/wallet_messaging.py +++ b/python/src/uagents/wallet_messaging.py @@ -11,7 +11,6 @@ from requests import HTTPError, JSONDecodeError from uagents.config import WALLET_MESSAGING_POLL_INTERVAL_SECONDS -from uagents.context import Context from uagents.crypto import Identity from uagents.types import WalletMessageCallback from uagents.utils import get_logger @@ -79,7 +78,7 @@ async def poll_server(self): ) await asyncio.sleep(self._poll_interval) - async def process_message_queue(self, agent: 'Agent'): + async def process_message_queue(self, agent: "Agent"): while True: msg: WalletMessage = await self._message_queue.get() for handler in self._message_handlers: diff --git a/python/tests/test_agent.py b/python/tests/test_agent.py index e8dcdde3..4d63bb2b 100644 --- a/python/tests/test_agent.py +++ b/python/tests/test_agent.py @@ -82,7 +82,7 @@ def _(ctx: Context): startup_handlers = self.agent._on_startup self.assertEqual(len(startup_handlers), 1) self.assertTrue(isinstance(startup_handlers[0], Callable)) - self.assertIsNone(self.agent._ctx.storage.get("startup")) + self.assertIsNone(self.agent._storage.get("startup")) def test_agent_on_shutdown_event(self): @self.agent.on_event("shutdown") @@ -92,7 +92,7 @@ def _(ctx: Context): shutdown_handlers = self.agent._on_shutdown self.assertEqual(len(shutdown_handlers), 1) self.assertTrue(isinstance(shutdown_handlers[0], Callable)) - self.assertIsNone(self.agent._ctx.storage.get("shutdown")) + self.assertIsNone(self.agent._storage.get("shutdown")) def test_agent_on_rest_get(self): @self.agent.on_rest_get("/get", Response) diff --git a/python/tests/test_context.py b/python/tests/test_context.py index c15db9f9..34dae936 100644 --- a/python/tests/test_context.py +++ b/python/tests/test_context.py @@ -52,10 +52,8 @@ def setUp(self): self.alice = Agent(name="alice", seed="alice recovery phrase", resolve=resolver) self.bob = Agent(name="bob", seed="bob recovery phrase") - self.agent = self.alice - self.context = self.agent._ctx self.loop = asyncio.get_event_loop() - self.loop.create_task(self.context._dispenser.run()) + self.loop.create_task(self.alice._dispenser.run()) def get_external_context( self, @@ -65,13 +63,13 @@ def get_external_context( queries: Optional[Dict[str, asyncio.Future]] = None, ): return ExternalContext( - agent=self.context.agent, - storage=self.agent._storage, - ledger=self.agent._ledger, - resolver=self.agent._resolver, - dispenser=self.agent._dispenser, - wallet_messaging_client=self.agent._wallet_messaging_client, - logger=self.agent._logger, + agent=self.alice, + storage=self.alice._storage, + ledger=self.alice._ledger, + resolver=self.alice._resolver, + dispenser=self.alice._dispenser, + wallet_messaging_client=self.alice._wallet_messaging_client, + logger=self.alice._logger, queries=queries, session=None, replies=replies, @@ -79,13 +77,14 @@ def get_external_context( ) async def test_send_local_dispatch(self): - result = await self.context.send(self.bob.address, msg) + context = self.alice._build_context() + result = await context.send(self.bob.address, msg) exp_msg_status = MsgStatus( status=DeliveryStatus.DELIVERED, detail="Message dispatched locally", destination=self.bob.address, endpoint="", - session=self.context.session, + session=context.session, ) self.assertEqual(result, exp_msg_status) @@ -121,28 +120,29 @@ async def test_send_local_dispatch_invalid_reply(self): self.assertEqual(result, exp_msg_status) async def test_send_local_dispatch_valid_interval_msg(self): - self.context._interval_messages = {msg_digest} - result = await self.context.send(self.bob.address, msg) + context = self.alice._build_context() + context._interval_messages = {msg_digest} + result = await context.send(self.bob.address, msg) exp_msg_status = MsgStatus( status=DeliveryStatus.DELIVERED, detail="Message dispatched locally", destination=self.bob.address, endpoint="", - session=self.context.session, + session=context.session, ) self.assertEqual(result, exp_msg_status) - self.context._interval_messages = set() async def test_send_local_dispatch_invalid_interval_msg(self): - self.context._interval_messages = {msg_digest} - result = await self.context.send(self.bob.address, incoming) + context = self.alice._build_context() + context._interval_messages = {msg_digest} + result = await context.send(self.bob.address, incoming) exp_msg_status = MsgStatus( status=DeliveryStatus.FAILED, detail="Invalid interval message", destination=self.bob.address, endpoint="", - session=self.context.session, + session=context.session, ) self.assertEqual(result, exp_msg_status) @@ -170,13 +170,14 @@ async def test_send_resolve_sync_query(self): async def test_send_external_dispatch_resolve_failure(self): destination = Identity.generate().address - result = await self.context.send(destination, msg) + context = self.alice._build_context() + result = await context.send(destination, msg) exp_msg_status = MsgStatus( status=DeliveryStatus.FAILED, detail="Unable to resolve destination endpoint", destination=destination, endpoint="", - session=self.context.session, + session=context.session, ) self.assertEqual(result, exp_msg_status) @@ -186,8 +187,10 @@ async def test_send_external_dispatch_success(self, mocked_responses): # Mock the HTTP POST request with a status code and response content mocked_responses.post(endpoints[0], status=200) + context = self.alice._build_context() + # Perform the actual operation - result = await self.context.send(self.clyde.address, msg) + result = await context.send(self.clyde.address, msg) # Define the expected message status exp_msg_status = MsgStatus( @@ -195,7 +198,7 @@ async def test_send_external_dispatch_success(self, mocked_responses): detail="Message successfully delivered via HTTP", destination=self.clyde.address, endpoint=endpoints[0], - session=self.context.session, + session=context.session, ) # Assertions @@ -206,8 +209,10 @@ async def test_send_external_dispatch_failure(self, mocked_responses): # Mock the HTTP POST request with a status code and response content mocked_responses.post(endpoints[0], status=404) + context = self.alice._build_context() + # Perform the actual operation - result = await self.context.send(self.clyde.address, msg) + result = await context.send(self.clyde.address, msg) # Define the expected message status exp_msg_status = MsgStatus( @@ -215,7 +220,7 @@ async def test_send_external_dispatch_failure(self, mocked_responses): detail="Message delivery failed", destination=self.clyde.address, endpoint="", - session=self.context.session, + session=context.session, ) # Assertions @@ -231,8 +236,10 @@ async def test_send_external_dispatch_multiple_endpoints_first_success( mocked_responses.post(endpoints[0], status=200) mocked_responses.post(endpoints[1], status=404) + context = self.alice._build_context() + # Perform the actual operation - result = await self.context.send(self.clyde.address, msg) + result = await context.send(self.clyde.address, msg) # Define the expected message status exp_msg_status = MsgStatus( @@ -240,7 +247,7 @@ async def test_send_external_dispatch_multiple_endpoints_first_success( detail="Message successfully delivered via HTTP", destination=self.clyde.address, endpoint=endpoints[0], - session=self.context.session, + session=context.session, ) # Assertions @@ -261,8 +268,10 @@ async def test_send_external_dispatch_multiple_endpoints_second_success( mocked_responses.post(endpoints[0], status=404) mocked_responses.post(endpoints[1], status=200) + context = self.alice._build_context() + # Perform the actual operation - result = await self.context.send(self.clyde.address, msg) + result = await context.send(self.clyde.address, msg) # Define the expected message status exp_msg_status = MsgStatus( @@ -270,7 +279,7 @@ async def test_send_external_dispatch_multiple_endpoints_second_success( detail="Message successfully delivered via HTTP", destination=self.clyde.address, endpoint=endpoints[1], - session=self.context.session, + session=context.session, ) # Assertions @@ -288,8 +297,10 @@ async def test_send_external_dispatch_multiple_endpoints_failure( mocked_responses.post(endpoints[0], status=404) mocked_responses.post(endpoints[1], status=404) + context = self.alice._build_context() + # Perform the actual operation - result = await self.context.send(self.clyde.address, msg) + result = await context.send(self.clyde.address, msg) # Define the expected message status exp_msg_status = MsgStatus( @@ -297,7 +308,7 @@ async def test_send_external_dispatch_multiple_endpoints_failure( detail="Message delivery failed", destination=self.clyde.address, endpoint="", - session=self.context.session, + session=context.session, ) # Assertions From 5ecf6edf0fbd496928afa749dae72cc1ffe180da Mon Sep 17 00:00:00 2001 From: Edward FitzGerald Date: Mon, 16 Sep 2024 18:01:57 +0100 Subject: [PATCH 3/9] disable warning --- python/src/uagents/wallet_messaging.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/src/uagents/wallet_messaging.py b/python/src/uagents/wallet_messaging.py index cee6de2f..be732a53 100644 --- a/python/src/uagents/wallet_messaging.py +++ b/python/src/uagents/wallet_messaging.py @@ -78,7 +78,7 @@ async def poll_server(self): ) await asyncio.sleep(self._poll_interval) - async def process_message_queue(self, agent: "Agent"): + async def process_message_queue(self, agent: "Agent"): # noqa: F821 while True: msg: WalletMessage = await self._message_queue.get() for handler in self._message_handlers: From efacbe8c05fe18021169ced1ec937f79e4b2fe92 Mon Sep 17 00:00:00 2001 From: Edward FitzGerald Date: Thu, 3 Oct 2024 12:38:26 +0100 Subject: [PATCH 4/9] wip: switch to builder pattern --- python/src/uagents/agent.py | 12 +++++------- python/src/uagents/context.py | 5 ++++- python/src/uagents/wallet_messaging.py | 7 +++++-- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/python/src/uagents/agent.py b/python/src/uagents/agent.py index c5d45344..cb650402 100644 --- a/python/src/uagents/agent.py +++ b/python/src/uagents/agent.py @@ -36,7 +36,7 @@ parse_agentverse_config, parse_endpoint_config, ) -from uagents.context import Context, ExternalContext, InternalContext +from uagents.context import Context, ExternalContext, InternalContext, ContextFactory from uagents.crypto import Identity, derive_key_from_seed, is_user_address from uagents.dispatch import Sink, dispatcher from uagents.envelope import EnvelopeHistory, EnvelopeHistoryEntry @@ -71,7 +71,7 @@ from uagents.utils import get_logger -async def _run_interval(func: IntervalCallback, agent: "Agent", period: float): +async def _run_interval(func: IntervalCallback, logger: logging.Logger, context_factory: ContextFactory, period: float): """ Run the provided interval callback function at a specified period. @@ -80,11 +80,9 @@ async def _run_interval(func: IntervalCallback, agent: "Agent", period: float): agent (Agent): The agent that is running the interval callback. period (float): The time period at which to run the callback function. """ - logger = agent._logger - while True: try: - ctx = agent._build_context() + ctx = context_factory() await func(ctx) except OSError as ex: logger.exception(f"OS Error in interval handler: {ex}") @@ -1075,7 +1073,7 @@ def start_interval_tasks(self): """ for func, period in self._interval_handlers: - self._loop.create_task(_run_interval(func, self, period)) + self._loop.create_task(_run_interval(func, self._logger, self._build_context, period)) def start_message_receivers(self): """ @@ -1089,7 +1087,7 @@ def start_message_receivers(self): if self._wallet_messaging_client is not None: for task in [ self._wallet_messaging_client.poll_server(), - self._wallet_messaging_client.process_message_queue(self), + self._wallet_messaging_client.process_message_queue(self._build_context), ]: self._loop.create_task(task) diff --git a/python/src/uagents/context.py b/python/src/uagents/context.py index d2929138..e1ae6b30 100644 --- a/python/src/uagents/context.py +++ b/python/src/uagents/context.py @@ -16,7 +16,7 @@ Set, Tuple, Type, - Union, + Union, Callable, ) import requests @@ -670,3 +670,6 @@ async def send( protocol_digest=self._protocol[0], queries=self._queries, ) + + +ContextFactory = Callable[[], Context] diff --git a/python/src/uagents/wallet_messaging.py b/python/src/uagents/wallet_messaging.py index be732a53..dfde52d4 100644 --- a/python/src/uagents/wallet_messaging.py +++ b/python/src/uagents/wallet_messaging.py @@ -14,6 +14,7 @@ from uagents.crypto import Identity from uagents.types import WalletMessageCallback from uagents.utils import get_logger +from uagents.context import ContextFactory class WalletMessagingClient: @@ -78,9 +79,11 @@ async def poll_server(self): ) await asyncio.sleep(self._poll_interval) - async def process_message_queue(self, agent: "Agent"): # noqa: F821 + async def process_message_queue( + self, context_factory: ContextFactory + ): # noqa: F821 while True: msg: WalletMessage = await self._message_queue.get() for handler in self._message_handlers: - ctx = agent._build_context() + ctx = context_factory() await handler(ctx, msg) From f28c30748bed921108edb7820caefb480a7be441 Mon Sep 17 00:00:00 2001 From: Edward FitzGerald Date: Thu, 3 Oct 2024 12:42:24 +0100 Subject: [PATCH 5/9] ruff formatting fixes --- python/src/uagents/agent.py | 2 +- python/src/uagents/context.py | 3 ++- python/src/uagents/wallet_messaging.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/python/src/uagents/agent.py b/python/src/uagents/agent.py index cb650402..41bc760e 100644 --- a/python/src/uagents/agent.py +++ b/python/src/uagents/agent.py @@ -36,7 +36,7 @@ parse_agentverse_config, parse_endpoint_config, ) -from uagents.context import Context, ExternalContext, InternalContext, ContextFactory +from uagents.context import Context, ContextFactory, ExternalContext, InternalContext from uagents.crypto import Identity, derive_key_from_seed, is_user_address from uagents.dispatch import Sink, dispatcher from uagents.envelope import EnvelopeHistory, EnvelopeHistoryEntry diff --git a/python/src/uagents/context.py b/python/src/uagents/context.py index e1ae6b30..3c9e5ba8 100644 --- a/python/src/uagents/context.py +++ b/python/src/uagents/context.py @@ -10,13 +10,14 @@ from typing import ( TYPE_CHECKING, Any, + Callable, Dict, List, Optional, Set, Tuple, Type, - Union, Callable, + Union, ) import requests diff --git a/python/src/uagents/wallet_messaging.py b/python/src/uagents/wallet_messaging.py index dfde52d4..22e8cee9 100644 --- a/python/src/uagents/wallet_messaging.py +++ b/python/src/uagents/wallet_messaging.py @@ -11,10 +11,10 @@ from requests import HTTPError, JSONDecodeError from uagents.config import WALLET_MESSAGING_POLL_INTERVAL_SECONDS +from uagents.context import ContextFactory from uagents.crypto import Identity from uagents.types import WalletMessageCallback from uagents.utils import get_logger -from uagents.context import ContextFactory class WalletMessagingClient: From a697391020e968668e9e48eeb1dcad64e6c7a532 Mon Sep 17 00:00:00 2001 From: Edward FitzGerald Date: Thu, 3 Oct 2024 12:43:17 +0100 Subject: [PATCH 6/9] ruff formatting --- python/src/uagents/agent.py | 15 ++++++++++++--- python/src/uagents/wallet_messaging.py | 4 +--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/python/src/uagents/agent.py b/python/src/uagents/agent.py index 41bc760e..7eab36aa 100644 --- a/python/src/uagents/agent.py +++ b/python/src/uagents/agent.py @@ -71,7 +71,12 @@ from uagents.utils import get_logger -async def _run_interval(func: IntervalCallback, logger: logging.Logger, context_factory: ContextFactory, period: float): +async def _run_interval( + func: IntervalCallback, + logger: logging.Logger, + context_factory: ContextFactory, + period: float, +): """ Run the provided interval callback function at a specified period. @@ -1073,7 +1078,9 @@ def start_interval_tasks(self): """ for func, period in self._interval_handlers: - self._loop.create_task(_run_interval(func, self._logger, self._build_context, period)) + self._loop.create_task( + _run_interval(func, self._logger, self._build_context, period) + ) def start_message_receivers(self): """ @@ -1087,7 +1094,9 @@ def start_message_receivers(self): if self._wallet_messaging_client is not None: for task in [ self._wallet_messaging_client.poll_server(), - self._wallet_messaging_client.process_message_queue(self._build_context), + self._wallet_messaging_client.process_message_queue( + self._build_context + ), ]: self._loop.create_task(task) diff --git a/python/src/uagents/wallet_messaging.py b/python/src/uagents/wallet_messaging.py index 22e8cee9..cb66b560 100644 --- a/python/src/uagents/wallet_messaging.py +++ b/python/src/uagents/wallet_messaging.py @@ -79,9 +79,7 @@ async def poll_server(self): ) await asyncio.sleep(self._poll_interval) - async def process_message_queue( - self, context_factory: ContextFactory - ): # noqa: F821 + async def process_message_queue(self, context_factory: ContextFactory): # noqa: F821 while True: msg: WalletMessage = await self._message_queue.get() for handler in self._message_handlers: From d223be10a86646e6bf4778612447a525fd4ef786 Mon Sep 17 00:00:00 2001 From: Archento Date: Tue, 8 Oct 2024 12:32:22 +0200 Subject: [PATCH 7/9] add: minor changes --- python/src/uagents/agent.py | 18 ++++++++++++------ python/src/uagents/context.py | 3 +-- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/python/src/uagents/agent.py b/python/src/uagents/agent.py index 7eab36aa..792456ce 100644 --- a/python/src/uagents/agent.py +++ b/python/src/uagents/agent.py @@ -82,7 +82,8 @@ async def _run_interval( Args: func (IntervalCallback): The interval callback function to run. - agent (Agent): The agent that is running the interval callback. + logger (logging.Logger): The logger instance for logging interval handler activities. + context_factory (ContextFactory): The factory function for creating the context. period (float): The time period at which to run the callback function. """ while True: @@ -419,9 +420,10 @@ async def _handle_get_messages(_ctx: Context): def _build_context(self) -> InternalContext: """ - An internal method to build the context for the agent + An internal method to build the context for the agent. - @return: + Returns: + InternalContext: The internal context for the agent. """ return InternalContext( agent=AgentRepresentation( @@ -1009,8 +1011,8 @@ async def handle_rest( if not handler: return None - context = self._build_context() - args = [context] + args = [] + args.append(self._build_context()) if message: args.append(message) @@ -1184,7 +1186,11 @@ async def _process_message_queue(self): ) context = ExternalContext( - agent=self, + agent=AgentRepresentation( + address=self.address, + name=self._name, + signing_callback=self._identity.sign_digest, + ), storage=self._storage, ledger=self._ledger, resolver=self._resolver, diff --git a/python/src/uagents/context.py b/python/src/uagents/context.py index 3c9e5ba8..d2c8158d 100644 --- a/python/src/uagents/context.py +++ b/python/src/uagents/context.py @@ -60,6 +60,7 @@ class Context(ABC): storage (KeyValueStore): The key-value store for storage operations. ledger (LedgerClient): The client for interacting with the blockchain ledger. logger (logging.Logger): The logger instance. + session (uuid.UUID): The session UUID associated with the context. Methods: get_agents_by_protocol(protocol_digest, limit, logger): Retrieve a list of agent addresses @@ -563,7 +564,6 @@ class ExternalContext(InternalContext): Attributes: _queries (Dict[str, asyncio.Future]): Dictionary mapping query senders to their response Futures. - _session (Optional[uuid.UUID]): The session UUID. _replies (Optional[Dict[str, Dict[str, Type[Model]]]]): Dictionary of allowed reply digests for each type of incoming message. _message_received (Optional[MsgDigest]): The message digest received. @@ -586,7 +586,6 @@ def __init__( message_received (MsgDigest): The optional message digest received. queries (Dict[str, asyncio.Future]): Dictionary mapping query senders to their response Futures. - session (Optional[uuid.UUID]): The optional session UUID. replies (Optional[Dict[str, Dict[str, Type[Model]]]]): Dictionary of allowed replies for each type of incoming message. protocol (Optional[Tuple[str, Protocol]]): The optional Tuple of protocols. From ccf0ab79e22c2041facaf03d84cc6ec87128bbbd Mon Sep 17 00:00:00 2001 From: Archento Date: Tue, 8 Oct 2024 12:34:51 +0200 Subject: [PATCH 8/9] move geolocation model to types.py --- python/src/uagents/registration.py | 11 +---------- python/src/uagents/types.py | 9 +++++++++ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/python/src/uagents/registration.py b/python/src/uagents/registration.py index 0a90c566..c44d94ea 100644 --- a/python/src/uagents/registration.py +++ b/python/src/uagents/registration.py @@ -20,7 +20,7 @@ ) from uagents.crypto import Identity from uagents.network import AlmanacContract, InsufficientFundsError, add_testnet_funds -from uagents.types import AgentEndpoint +from uagents.types import AgentEndpoint, AgentGeoLocation class AgentRegistrationPolicy(ABC): @@ -32,15 +32,6 @@ async def register( pass -class AgentGeoLocation(BaseModel): - # Latitude and longitude of the agent - latitude: float - longitude: float - - # Radius around the agent location, expressed in meters - radius: float - - class AgentRegistrationAttestation(BaseModel): agent_address: str protocols: List[str] diff --git a/python/src/uagents/types.py b/python/src/uagents/types.py index 1379dad9..31216894 100644 --- a/python/src/uagents/types.py +++ b/python/src/uagents/types.py @@ -42,6 +42,15 @@ class AgentEndpoint(BaseModel): weight: int +class AgentGeoLocation(BaseModel): + # Latitude and longitude of the agent + latitude: float + longitude: float + + # Radius around the agent location, expressed in meters + radius: float + + class AgentInfo(BaseModel): agent_address: str endpoints: List[AgentEndpoint] From 619f2b27ac35de7342b2e9aa4c60a572c3283668 Mon Sep 17 00:00:00 2001 From: Archento Date: Tue, 8 Oct 2024 12:35:28 +0200 Subject: [PATCH 9/9] chore: update docs --- python/docs/api/uagents/agent.md | 11 +++++++++++ python/docs/api/uagents/context.md | 8 +++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/python/docs/api/uagents/agent.md b/python/docs/api/uagents/agent.md index bb8f01d6..9d0efb55 100644 --- a/python/docs/api/uagents/agent.md +++ b/python/docs/api/uagents/agent.md @@ -159,6 +159,7 @@ An agent that interacts within a communication environment. corresponding protocols. - `_ctx` _Context_ - The context for agent interactions. - `_test` _bool_ - True if the agent will register and transact on the testnet. +- `_enable_agent_inspector` _bool_ - Enable the agent inspector REST endpoints. Properties: - `name` _str_ - The name of the agent. @@ -732,6 +733,16 @@ def start_message_receivers() Start message receiving tasks for the agent. + + +#### start`_`server + +```python +async def start_server() +``` + +Start the agent's server. + #### run`_`async diff --git a/python/docs/api/uagents/context.md b/python/docs/api/uagents/context.md index 21151e27..2a46794c 100644 --- a/python/docs/api/uagents/context.md +++ b/python/docs/api/uagents/context.md @@ -19,6 +19,7 @@ agent (AgentRepresentation): The agent representation associated with the contex storage (KeyValueStore): The key-value store for storage operations. ledger (LedgerClient): The client for interacting with the blockchain ledger. logger (logging.Logger): The logger instance. +session (uuid.UUID): The session UUID associated with the context. **Methods**: @@ -101,7 +102,7 @@ Get the logger instance associated with the context. ```python @property @abstractmethod -def session() -> Union[uuid.UUID, None] +def session() -> uuid.UUID ``` Get the session UUID associated with the context. @@ -267,7 +268,7 @@ Represents the agent internal context for proactive behaviour. ```python @property -def session() -> Union[uuid.UUID, None] +def session() -> uuid.UUID ``` Get the session UUID associated with the context. @@ -339,7 +340,6 @@ Represents the reactive context in which messages are handled and processed. - `_queries` _Dict[str, asyncio.Future]_ - Dictionary mapping query senders to their response Futures. -- `_session` _Optional[uuid.UUID]_ - The session UUID. - `_replies` _Optional[Dict[str, Dict[str, Type[Model]]]]_ - Dictionary of allowed reply digests for each type of incoming message. - `_message_received` _Optional[MsgDigest]_ - The message digest received. @@ -352,7 +352,6 @@ Represents the reactive context in which messages are handled and processed. ```python def __init__(message_received: MsgDigest, - session: Optional[uuid.UUID] = None, queries: Optional[Dict[str, asyncio.Future]] = None, replies: Optional[Dict[str, Dict[str, Type[Model]]]] = None, protocol: Optional[Tuple[str, Protocol]] = None, @@ -366,7 +365,6 @@ Initialize the ExternalContext instance and attributes needed from the InternalC - `message_received` _MsgDigest_ - The optional message digest received. - `queries` _Dict[str, asyncio.Future]_ - Dictionary mapping query senders to their response Futures. -- `session` _Optional[uuid.UUID]_ - The optional session UUID. - `replies` _Optional[Dict[str, Dict[str, Type[Model]]]]_ - Dictionary of allowed replies for each type of incoming message. - `protocol` _Optional[Tuple[str, Protocol]]_ - The optional Tuple of protocols.