diff --git a/python/docs/api/uagents/agent.md b/python/docs/api/uagents/agent.md
new file mode 100644
index 00000000..38880c10
--- /dev/null
+++ b/python/docs/api/uagents/agent.md
@@ -0,0 +1,569 @@
+
+
+# src.uagents.agent
+
+Agent
+
+
+
+## Agent Objects
+
+```python
+class Agent(Sink)
+```
+
+An agent that interacts within a communication environment.
+
+**Attributes**:
+
+- `_name` _str_ - The name of the agent.
+- `_port` _int_ - The port on which the agent runs.
+- `_background_tasks` _Set[asyncio.Task]_ - Set of background tasks associated with the agent.
+- `_resolver` _Resolver_ - The resolver for agent communication.
+- `_loop` _asyncio.AbstractEventLoop_ - The asyncio event loop used by the agent.
+- `_logger` - The logger instance for logging agent activities.
+- `_endpoints` _List[dict]_ - List of communication endpoints.
+- `_use_mailbox` _bool_ - Indicates if the agent uses a mailbox for communication.
+- `_agentverse` _dict_ - Agentverse configuration settings.
+- `_mailbox_client` _MailboxClient_ - Client for interacting with the mailbox.
+- `_ledger` - The ledger for recording agent transactions.
+- `_almanac_contract` - The almanac contract for agent metadata.
+- `_storage` - Key-value store for agent data storage.
+- `_interval_handlers` _List[Tuple[IntervalCallback, float]]_ - List of interval
+ handlers and their periods.
+- `_interval_messages` _Set[str]_ - Set of interval message names.
+- `_signed_message_handlers` _Dict[str, MessageCallback]_ - Handlers for signed messages.
+- `_unsigned_message_handlers` _Dict[str, MessageCallback]_ - Handlers for
+ unsigned messages.
+- `_models` _Dict[str, Type[Model]]_ - Dictionary of supported data models.
+- `_replies` _Dict[str, Set[Type[Model]]]_ - Dictionary of reply data models.
+- `_queries` _Dict[str, asyncio.Future]_ - Dictionary of active queries.
+- `_dispatcher` - The dispatcher for message handling.
+- `_message_queue` - Asynchronous queue for incoming messages.
+- `_on_startup` _List[Callable]_ - List of functions to run on agent startup.
+- `_on_shutdown` _List[Callable]_ - List of functions to run on agent shutdown.
+- `_version` _str_ - The version of the agent.
+- `_protocol` _Protocol_ - The internal agent protocol.
+- `protocols` _Dict[str, Protocol]_ - Dictionary of supported protocols.
+- `_ctx` _Context_ - The context for agent interactions.
+
+
+**Methods**:
+
+- `__init__` - Initialize the Agent instance.
+
+
+
+#### `__`init`__`
+
+```python
+def __init__(name: Optional[str] = None,
+ port: Optional[int] = None,
+ seed: Optional[str] = None,
+ endpoint: Optional[Union[str, List[str], Dict[str, dict]]] = None,
+ agentverse: Optional[Union[str, Dict[str, str]]] = None,
+ mailbox: Optional[Union[str, Dict[str, str]]] = None,
+ resolve: Optional[Resolver] = None,
+ version: Optional[str] = None)
+```
+
+Initialize an Agent instance.
+
+**Arguments**:
+
+- `name` _Optional[str]_ - The name of the agent.
+- `port` _Optional[int]_ - The port on which the agent will run.
+- `seed` _Optional[str]_ - The seed for generating keys.
+- `endpoint` _Optional[Union[str, List[str], Dict[str, dict]]]_ - The endpoint configuration.
+- `agentverse` _Optional[Union[str, Dict[str, str]]]_ - The agentverse configuration.
+- `mailbox` _Optional[Union[str, Dict[str, str]]]_ - The mailbox configuration.
+- `resolve` _Optional[Resolver]_ - The resolver to use for agent communication.
+- `version` _Optional[str]_ - The version of the agent.
+
+
+
+#### name
+
+```python
+@property
+def name() -> str
+```
+
+Get the name of the agent.
+
+**Returns**:
+
+- `str` - The name of the agent.
+
+
+
+#### address
+
+```python
+@property
+def address() -> str
+```
+
+Get the address of the agent's identity.
+
+**Returns**:
+
+- `str` - The address of the agent's identity.
+
+
+
+#### wallet
+
+```python
+@property
+def wallet() -> LocalWallet
+```
+
+Get the wallet of the agent.
+
+**Returns**:
+
+- `LocalWallet` - The agent's wallet.
+
+
+
+#### storage
+
+```python
+@property
+def storage() -> KeyValueStore
+```
+
+Get the key-value store used by the agent for data storage.
+
+**Returns**:
+
+- `KeyValueStore` - The key-value store instance.
+
+
+
+#### mailbox
+
+```python
+@property
+def mailbox() -> Dict[str, str]
+```
+
+Get the mailbox configuration of the agent.
+
+**Returns**:
+
+ Dict[str, str]: The mailbox configuration.
+
+
+
+#### agentverse
+
+```python
+@property
+def agentverse() -> Dict[str, str]
+```
+
+Get the agentverse configuration of the agent.
+
+**Returns**:
+
+ Dict[str, str]: The agentverse configuration.
+
+
+
+#### mailbox`_`client
+
+```python
+@property
+def mailbox_client() -> MailboxClient
+```
+
+Get the mailbox client used by the agent for mailbox communication.
+
+**Returns**:
+
+- `MailboxClient` - The mailbox client instance.
+
+
+
+#### mailbox
+
+```python
+@mailbox.setter
+def mailbox(config: Union[str, Dict[str, str]])
+```
+
+Set the mailbox configuration for the agent.
+
+**Arguments**:
+
+- `config` _Union[str, Dict[str, str]]_ - The new mailbox configuration.
+
+
+
+#### agentverse
+
+```python
+@agentverse.setter
+def agentverse(config: Union[str, Dict[str, str]])
+```
+
+Set the agentverse configuration for the agent.
+
+**Arguments**:
+
+- `config` _Union[str, Dict[str, str]]_ - The new agentverse configuration.
+
+
+
+#### sign
+
+```python
+def sign(data: bytes) -> str
+```
+
+Sign the provided data.
+
+**Arguments**:
+
+- `data` _bytes_ - The data to be signed.
+
+
+**Returns**:
+
+- `str` - The signature of the data.
+
+
+
+#### sign`_`digest
+
+```python
+def sign_digest(digest: bytes) -> str
+```
+
+Sign the provided digest.
+
+**Arguments**:
+
+- `digest` _bytes_ - The digest to be signed.
+
+
+**Returns**:
+
+- `str` - The signature of the digest.
+
+
+
+#### sign`_`registration
+
+```python
+def sign_registration() -> str
+```
+
+Sign the registration data for Almanac contract.
+
+**Returns**:
+
+- `str` - The signature of the registration data.
+
+
+**Raises**:
+
+- `AssertionError` - If the Almanac contract address is None.
+
+
+
+#### update`_`endpoints
+
+```python
+def update_endpoints(endpoints: List[Dict[str, Any]])
+```
+
+Update the list of endpoints.
+
+**Arguments**:
+
+- `endpoints` _List[Dict[str, Any]]_ - List of endpoint dictionaries.
+
+
+
+#### update`_`loop
+
+```python
+def update_loop(loop)
+```
+
+Update the event loop.
+
+**Arguments**:
+
+- `loop` - The event loop.
+
+
+
+#### update`_`queries
+
+```python
+def update_queries(queries)
+```
+
+Update the queries attribute.
+
+**Arguments**:
+
+- `queries` - The queries attribute.
+
+
+
+#### register
+
+```python
+async def register()
+```
+
+Register with the Almanac contract.
+
+This method checks for registration conditions and performs registration
+if necessary.
+
+
+
+#### on`_`interval
+
+```python
+def on_interval(period: float,
+ messages: Optional[Union[Type[Model],
+ Set[Type[Model]]]] = None)
+```
+
+Set up an interval event with a callback.
+
+**Arguments**:
+
+- `period` _float_ - The interval period.
+- `messages` _Optional[Union[Type[Model], Set[Type[Model]]]]_ - Optional message types.
+
+
+**Returns**:
+
+- `Callable` - The callback function for the interval event.
+
+
+
+#### on`_`query
+
+```python
+def on_query(model: Type[Model],
+ replies: Optional[Union[Model, Set[Model]]] = None)
+```
+
+Set up a query event with a callback.
+
+**Arguments**:
+
+- `model` _Type[Model]_ - The query model.
+- `replies` _Optional[Union[Model, Set[Model]]]_ - Optional reply models.
+
+
+**Returns**:
+
+- `Callable` - The callback function for the query event.
+
+
+
+#### on`_`message
+
+```python
+def on_message(model: Type[Model],
+ replies: Optional[Union[Type[Model], Set[Type[Model]]]] = None,
+ allow_unverified: Optional[bool] = False)
+```
+
+Set up a message event with a callback.
+
+**Arguments**:
+
+- `model` _Type[Model]_ - The message model.
+- `replies` _Optional[Union[Type[Model], Set[Type[Model]]]]_ - Optional reply models.
+- `allow_unverified` _Optional[bool]_ - Allow unverified messages.
+
+
+**Returns**:
+
+- `Callable` - The callback function for the message event.
+
+
+
+#### on`_`event
+
+```python
+def on_event(event_type: str)
+```
+
+Decorator to register an event handler for a specific event type.
+
+**Arguments**:
+
+- `event_type` _str_ - The type of event.
+
+
+**Returns**:
+
+- `Callable` - The decorator function for registering event handlers.
+
+
+
+#### include
+
+```python
+def include(protocol: Protocol, publish_manifest: Optional[bool] = False)
+```
+
+Include a protocol into the agent's capabilities.
+
+**Arguments**:
+
+- `protocol` _Protocol_ - The protocol to include.
+- `publish_manifest` _Optional[bool]_ - Flag to publish the protocol's manifest.
+
+
+**Raises**:
+
+- `RuntimeError` - If a duplicate model, signed message handler, or message handler
+ is encountered.
+
+
+
+#### publish`_`manifest
+
+```python
+def publish_manifest(manifest: Dict[str, Any])
+```
+
+Publish a protocol manifest to the Almanac.
+
+**Arguments**:
+
+- `manifest` _Dict[str, Any]_ - The protocol manifest.
+
+
+
+#### handle`_`message
+
+```python
+async def handle_message(sender, schema_digest: str, message: JsonStr,
+ session: uuid.UUID)
+```
+
+Handle an incoming message asynchronously.
+
+**Arguments**:
+
+- `sender` - The sender of the message.
+- `schema_digest` _str_ - The schema digest of the message.
+- `message` _JsonStr_ - The message content in JSON format.
+- `session` _uuid.UUID_ - The session UUID.
+
+
+
+#### setup
+
+```python
+def setup()
+```
+
+Set up the agent.
+
+
+
+#### start`_`background`_`tasks
+
+```python
+def start_background_tasks()
+```
+
+Start background tasks for the agent.
+
+
+
+#### run
+
+```python
+def run()
+```
+
+Run the agent.
+
+
+
+## Bureau Objects
+
+```python
+class Bureau()
+```
+
+A class representing a Bureau of agents.
+
+This class manages a collection of agents and orchestrates their execution.
+
+**Arguments**:
+
+- `port` _Optional[int]_ - The port number for the server.
+- `endpoint` _Optional[Union[str, List[str], Dict[str, dict]]]_ - Configuration
+ for agent endpoints.
+
+
+**Attributes**:
+
+- `_loop` _asyncio.AbstractEventLoop_ - The event loop.
+- `_agents` _List[Agent]_ - A list of Agent instances within the bureau.
+- `_endpoints` _List[Dict[str, Any]]_ - A list of endpoint dictionaries for the agents.
+- `_port` _int_ - The port number for the server.
+- `_queries` _Dict[str, asyncio.Future]_ - A dictionary of query identifiers to asyncio futures.
+- `_logger` _Logger_ - The logger instance.
+- `_server` _ASGIServer_ - The ASGI server instance for handling requests.
+- `_use_mailbox` _bool_ - A flag indicating whether mailbox functionality is enabled.
+
+
+
+#### `__`init`__`
+
+```python
+def __init__(port: Optional[int] = None,
+ endpoint: Optional[Union[str, List[str], Dict[str,
+ dict]]] = None)
+```
+
+Initialize a Bureau instance.
+
+**Arguments**:
+
+- `port` _Optional[int]_ - The port number for the server.
+- `endpoint` _Optional[Union[str, List[str], Dict[str, dict]]]_ - Configuration
+ for agent endpoints.
+
+
+
+#### add
+
+```python
+def add(agent: Agent)
+```
+
+Add an agent to the bureau.
+
+**Arguments**:
+
+- `agent` _Agent_ - The agent instance to be added.
+
+
+
+#### run
+
+```python
+def run()
+```
+
+Run the agents managed by the bureau.
+
diff --git a/python/docs/api/uagents/context.md b/python/docs/api/uagents/context.md
new file mode 100644
index 00000000..22762f71
--- /dev/null
+++ b/python/docs/api/uagents/context.md
@@ -0,0 +1,292 @@
+
+
+# src.uagents.context
+
+Agent Context and Message Handling
+
+
+
+## MsgDigest Objects
+
+```python
+@dataclass
+class MsgDigest()
+```
+
+Represents a message digest containing a message and its schema digest.
+
+**Attributes**:
+
+- `message` _Any_ - The message content.
+- `schema_digest` _str_ - The schema digest of the message.
+
+
+
+## Context Objects
+
+```python
+class Context()
+```
+
+Represents the context in which messages are handled and processed.
+
+**Attributes**:
+
+- `storage` _KeyValueStore_ - The key-value store for storage operations.
+- `wallet` _LocalWallet_ - The local wallet instance for managing identities.
+- `ledger` _LedgerClient_ - The ledger client for interacting with distributed ledgers.
+- `_name` _Optional[str]_ - The optional name associated with the context.
+- `_address` _str_ - The address of the context.
+- `_resolver` _Resolver_ - The resolver for name-to-address resolution.
+- `_identity` _Identity_ - The identity associated with the context.
+- `_queries` _Dict[str, asyncio.Future]_ - Dictionary of query names and their asyncio Futures.
+- `_session` _Optional[uuid.UUID]_ - The optional session UUID.
+- `_replies` _Optional[Dict[str, Set[Type[Model]]]]_ - The optional dictionary of reply models.
+- `_interval_messages` _Optional[Set[str]]_ - The optional set of interval messages.
+- `_message_received` _Optional[MsgDigest]_ - The optional message digest received.
+- `_protocols` _Optional[Dict[str, Protocol]]_ - The optional dictionary of protocols.
+- `_logger` _Optional[logging.Logger]_ - The optional logger instance.
+
+ Properties:
+- `name` _str_ - The name associated with the context, or a truncated address if name is None.
+- `address` _str_ - The address of the context.
+- `logger` _logging.Logger_ - The logger instance.
+- `protocols` _Optional[Dict[str, Protocol]]_ - The dictionary of protocols.
+
+
+**Methods**:
+
+- `get_message_protocol(message_schema_digest)` - Get the protocol associated
+ with a message schema digest.
+ send(destination, message, timeout): Send a message to a destination.
+
+
+
+#### `__`init`__`
+
+```python
+def __init__(address: str,
+ name: Optional[str],
+ storage: KeyValueStore,
+ resolve: Resolver,
+ identity: Identity,
+ wallet: LocalWallet,
+ ledger: LedgerClient,
+ queries: Dict[str, asyncio.Future],
+ session: Optional[uuid.UUID] = None,
+ replies: Optional[Dict[str, Set[Type[Model]]]] = None,
+ interval_messages: Optional[Set[str]] = None,
+ message_received: Optional[MsgDigest] = None,
+ protocols: Optional[Dict[str, Protocol]] = None,
+ logger: Optional[logging.Logger] = None)
+```
+
+Initialize the Context instance.
+
+**Arguments**:
+
+- `address` _str_ - The address of the context.
+- `name` _Optional[str]_ - The optional name associated with the context.
+- `storage` _KeyValueStore_ - The key-value store for storage operations.
+- `resolve` _Resolver_ - The resolver for name-to-address resolution.
+- `identity` _Identity_ - The identity associated with the context.
+- `wallet` _LocalWallet_ - The local wallet instance for managing identities.
+- `ledger` _LedgerClient_ - The ledger client for interacting with distributed ledgers.
+- `queries` _Dict[str, asyncio.Future]_ - Dictionary of query names and their Futures.
+- `session` _Optional[uuid.UUID]_ - The optional session UUID.
+- `replies` _Optional[Dict[str, Set[Type[Model]]]]_ - Optional dictionary of reply models.
+- `interval_messages` _Optional[Set[str]]_ - The optional set of interval messages.
+- `message_received` _Optional[MsgDigest]_ - The optional message digest received.
+- `protocols` _Optional[Dict[str, Protocol]]_ - The optional dictionary of protocols.
+- `logger` _Optional[logging.Logger]_ - The optional logger instance.
+
+
+
+#### name
+
+```python
+@property
+def name() -> str
+```
+
+Get the name associated with the context or a truncated address if name is None.
+
+**Returns**:
+
+- `str` - The name or truncated address.
+
+
+
+#### address
+
+```python
+@property
+def address() -> str
+```
+
+Get the address of the context.
+
+**Returns**:
+
+- `str` - The address of the context.
+
+
+
+#### logger
+
+```python
+@property
+def logger() -> logging.Logger
+```
+
+Get the logger instance associated with the context.
+
+**Returns**:
+
+- `logging.Logger` - The logger instance.
+
+
+
+#### protocols
+
+```python
+@property
+def protocols() -> Optional[Dict[str, Protocol]]
+```
+
+Get the dictionary of protocols associated with the context.
+
+**Returns**:
+
+ Optional[Dict[str, Protocol]]: The dictionary of protocols.
+
+
+
+#### session
+
+```python
+@property
+def session() -> uuid.UUID
+```
+
+Get the session UUID associated with the context.
+
+**Returns**:
+
+- `uuid.UUID` - The session UUID.
+
+
+
+#### get`_`message`_`protocol
+
+```python
+def get_message_protocol(message_schema_digest) -> Optional[str]
+```
+
+Get the protocol associated with a given message schema digest.
+
+**Arguments**:
+
+- `message_schema_digest` _str_ - The schema digest of the message.
+
+
+**Returns**:
+
+- `Optional[str]` - The protocol digest associated with the message schema digest,
+ or None if not found.
+
+
+
+#### get`_`agents`_`by`_`protocol
+
+```python
+def get_agents_by_protocol(protocol_digest: str,
+ limit: Optional[int] = None) -> List[str]
+```
+
+Retrieve a list of agent addresses using a specific protocol digest.
+
+This method queries the Almanac API to retrieve a list of agent addresses
+that are associated with a given protocol digest. The list can be optionally
+limited to a specified number of addresses.
+
+**Arguments**:
+
+- `protocol_digest` _str_ - The protocol digest to search for, starting with "proto:".
+- `limit` _int, optional_ - The maximum number of agent addresses to return.
+
+
+**Returns**:
+
+- `List[str]` - A list of agent addresses using the specified protocol digest.
+
+
+
+#### send
+
+```python
+async def send(destination: str,
+ message: Model,
+ timeout: Optional[int] = DEFAULT_ENVELOPE_TIMEOUT_SECONDS)
+```
+
+Send a message to the specified destination.
+
+**Arguments**:
+
+- `destination` _str_ - The destination address to send the message to.
+- `message` _Model_ - The message to be sent.
+- `timeout` _Optional[int]_ - The optional timeout for sending the message, in seconds.
+
+
+
+#### experimental`_`broadcast
+
+```python
+async def experimental_broadcast(
+ destination_protocol: str,
+ message: Model,
+ limit: Optional[int] = DEFAULT_SEARCH_LIMIT,
+ timeout: Optional[int] = DEFAULT_ENVELOPE_TIMEOUT_SECONDS)
+```
+
+Broadcast a message to agents with a specific protocol.
+
+This asynchronous method broadcasts a given message to agents associated
+with a specific protocol. The message is sent to multiple agents concurrently.
+The schema digest of the message is used for verification.
+
+**Arguments**:
+
+- `destination_protocol` _str_ - The protocol to filter agents by.
+- `message` _Model_ - The message to broadcast.
+- `limit` _int, optional_ - The maximum number of agents to send the message to.
+- `timeout` _int, optional_ - The timeout for sending each message.
+
+
+**Returns**:
+
+ None
+
+
+
+#### send`_`raw
+
+```python
+async def send_raw(destination: str,
+ json_message: JsonStr,
+ schema_digest: str,
+ message_type: Optional[Type[Model]] = None,
+ timeout: Optional[int] = DEFAULT_ENVELOPE_TIMEOUT_SECONDS)
+```
+
+Send a raw message to the specified destination.
+
+**Arguments**:
+
+- `destination` _str_ - The destination address to send the message to.
+- `json_message` _JsonStr_ - The JSON-encoded message to be sent.
+- `schema_digest` _str_ - The schema digest of the message.
+- `message_type` _Optional[Type[Model]]_ - The optional type of the message being sent.
+- `timeout` _Optional[int]_ - The optional timeout for sending the message, in seconds.
+
diff --git a/python/docs/api/uagents/envelope.md b/python/docs/api/uagents/envelope.md
new file mode 100644
index 00000000..4f393939
--- /dev/null
+++ b/python/docs/api/uagents/envelope.md
@@ -0,0 +1,85 @@
+
+
+# src.uagents.envelope
+
+Agent Envelope.
+
+
+
+## Envelope Objects
+
+```python
+class Envelope(BaseModel)
+```
+
+Represents an envelope for message communication between agents.
+
+**Attributes**:
+
+- `version` _int_ - The envelope version.
+- `sender` _str_ - The sender's address.
+- `target` _str_ - The target's address.
+- `session` _UUID4_ - The session UUID.
+- `schema_digest` _str_ - The schema digest (alias for protocol).
+- `protocol_digest` _Optional[str]_ - The protocol digest (optional).
+- `payload` _Optional[str]_ - The payload data (optional).
+- `expires` _Optional[int]_ - The expiration timestamp (optional).
+- `nonce` _Optional[int]_ - The nonce value (optional).
+- `signature` _Optional[str]_ - The envelope signature (optional).
+
+
+
+#### encode`_`payload
+
+```python
+def encode_payload(value: JsonStr)
+```
+
+Encode the payload value and store it in the envelope.
+
+**Arguments**:
+
+- `value` _JsonStr_ - The payload value to be encoded.
+
+
+
+#### decode`_`payload
+
+```python
+def decode_payload() -> Optional[Any]
+```
+
+Decode and retrieve the payload value from the envelope.
+
+**Returns**:
+
+- `Optional[Any]` - The decoded payload value, or None if payload is not present.
+
+
+
+#### sign
+
+```python
+def sign(identity: Identity)
+```
+
+Sign the envelope using the provided identity.
+
+**Arguments**:
+
+- `identity` _Identity_ - The identity used for signing.
+
+
+
+#### verify
+
+```python
+def verify() -> bool
+```
+
+Verify the envelope's signature.
+
+**Returns**:
+
+- `bool` - True if the signature is valid, False otherwise.
+
diff --git a/python/docs/api/uagents/network.md b/python/docs/api/uagents/network.md
new file mode 100644
index 00000000..255076ce
--- /dev/null
+++ b/python/docs/api/uagents/network.md
@@ -0,0 +1,353 @@
+
+
+# src.uagents.network
+
+Network and Contracts.
+
+
+
+#### get`_`ledger
+
+```python
+def get_ledger() -> LedgerClient
+```
+
+Get the Ledger client.
+
+**Returns**:
+
+- `LedgerClient` - The Ledger client instance.
+
+
+
+#### get`_`faucet
+
+```python
+def get_faucet() -> FaucetApi
+```
+
+Get the Faucet API instance.
+
+**Returns**:
+
+- `FaucetApi` - The Faucet API instance.
+
+
+
+#### wait`_`for`_`tx`_`to`_`complete
+
+```python
+async def wait_for_tx_to_complete(
+ tx_hash: str,
+ timeout: Optional[timedelta] = None,
+ poll_period: Optional[timedelta] = None) -> TxResponse
+```
+
+Wait for a transaction to complete on the Ledger.
+
+**Arguments**:
+
+- `tx_hash` _str_ - The hash of the transaction to monitor.
+- `timeout` _Optional[timedelta], optional_ - The maximum time to wait for
+ the transaction to complete. Defaults to None.
+- `poll_period` _Optional[timedelta], optional_ - The time interval to poll
+ the Ledger for the transaction status. Defaults to None.
+
+
+**Returns**:
+
+- `TxResponse` - The response object containing the transaction details.
+
+
+
+## AlmanacContract Objects
+
+```python
+class AlmanacContract(LedgerContract)
+```
+
+A class representing the Almanac contract for agent registration.
+
+This class provides methods to interact with the Almanac contract, including
+checking if an agent is registered, retrieving the expiry height of an agent's
+registration, and getting the endpoints associated with an agent's registration.
+
+**Arguments**:
+
+ ledger contract (LedgerContract): An instance of the LedgeContract class.
+
+
+**Attributes**:
+
+ ledger contract (LedgerContract): An instance of the LedgeContract class.
+
+
+
+#### is`_`registered
+
+```python
+def is_registered(address: str) -> bool
+```
+
+Check if an agent is registered in the Almanac contract.
+
+**Arguments**:
+
+- `address` _str_ - The agent's address.
+
+
+**Returns**:
+
+- `bool` - True if the agent is registered, False otherwise.
+
+
+
+#### get`_`expiry
+
+```python
+def get_expiry(address: str) -> int
+```
+
+Get the expiry height of an agent's registration.
+
+**Arguments**:
+
+- `address` _str_ - The agent's address.
+
+
+**Returns**:
+
+- `int` - The expiry height of the agent's registration.
+
+
+
+#### get`_`endpoints
+
+```python
+def get_endpoints(address: str)
+```
+
+Get the endpoints associated with an agent's registration.
+
+**Arguments**:
+
+- `address` _str_ - The agent's address.
+
+
+**Returns**:
+
+- `Any` - The endpoints associated with the agent's registration.
+
+
+
+#### get`_`protocols
+
+```python
+def get_protocols(address: str)
+```
+
+Get the protocols associated with an agent's registration.
+
+**Arguments**:
+
+- `address` _str_ - The agent's address.
+
+
+**Returns**:
+
+- `Any` - The protocols associated with the agent's registration.
+
+
+
+#### register
+
+```python
+async def register(ledger: LedgerClient, wallet: LocalWallet,
+ agent_address: str, protocols: List[str],
+ endpoints: List[Dict[str, Any]], signature: str)
+```
+
+Register an agent with the Almanac contract.
+
+**Arguments**:
+
+- `ledger` _LedgerClient_ - The Ledger client.
+- `wallet` _LocalWallet_ - The agent's wallet.
+- `agent_address` _str_ - The agent's address.
+- `protocols` _List[str]_ - List of protocols.
+- `endpoints` _List[Dict[str, Any]]_ - List of endpoint dictionaries.
+- `signature` _str_ - The agent's signature.
+
+
+
+#### get`_`sequence
+
+```python
+def get_sequence(address: str) -> int
+```
+
+Get the agent's sequence number.
+
+**Arguments**:
+
+- `address` _str_ - The agent's address.
+
+
+**Returns**:
+
+- `int` - The agent's sequence number.
+
+
+
+#### get`_`almanac`_`contract
+
+```python
+def get_almanac_contract() -> AlmanacContract
+```
+
+Get the AlmanacContract instance.
+
+**Returns**:
+
+- `AlmanacContract` - The AlmanacContract instance.
+
+
+
+## NameServiceContract Objects
+
+```python
+class NameServiceContract(LedgerContract)
+```
+
+A class representing the NameService contract for managing domain names and ownership.
+
+This class provides methods to interact with the NameService contract, including
+checking name availability, checking ownership, querying domain public status,
+obtaining registration transaction details, and registering a name within a domain.
+
+**Arguments**:
+
+ ledger contract (LedgerContract): An instance of the LedgeContract class.
+
+
+**Attributes**:
+
+ ledger contract (LedgerContract): An instance of the LedgeContract class.
+
+
+
+#### is`_`name`_`available
+
+```python
+def is_name_available(name: str, domain: str)
+```
+
+Check if a name is available within a domain.
+
+**Arguments**:
+
+- `name` _str_ - The name to check.
+- `domain` _str_ - The domain to check within.
+
+
+**Returns**:
+
+- `bool` - True if the name is available, False otherwise.
+
+
+
+#### is`_`owner
+
+```python
+def is_owner(name: str, domain: str, wallet_address: str)
+```
+
+Check if the provided wallet address is the owner of a name within a domain.
+
+**Arguments**:
+
+- `name` _str_ - The name to check ownership for.
+- `domain` _str_ - The domain to check within.
+- `wallet_address` _str_ - The wallet address to check ownership against.
+
+
+**Returns**:
+
+- `bool` - True if the wallet address is the owner, False otherwise.
+
+
+
+#### is`_`domain`_`public
+
+```python
+def is_domain_public(domain: str)
+```
+
+Check if a domain is public.
+
+**Arguments**:
+
+- `domain` _str_ - The domain to check.
+
+
+**Returns**:
+
+- `bool` - True if the domain is public, False otherwise.
+
+
+
+#### get`_`registration`_`tx
+
+```python
+def get_registration_tx(name: str, wallet_address: str, agent_address: str,
+ domain: str)
+```
+
+Get the registration transaction for registering a name within a domain.
+
+**Arguments**:
+
+- `name` _str_ - The name to be registered.
+- `wallet_address` _str_ - The wallet address initiating the registration.
+- `agent_address` _str_ - The address of the agent.
+- `domain` _str_ - The domain in which the name is registered.
+
+
+**Returns**:
+
+- `Optional[Transaction]` - The registration transaction, or None if the name is not
+ available or not owned by the wallet address.
+
+
+
+#### register
+
+```python
+async def register(ledger: LedgerClient, wallet: LocalWallet,
+ agent_address: str, name: str, domain: str)
+```
+
+Register a name within a domain using the NameService contract.
+
+**Arguments**:
+
+- `ledger` _LedgerClient_ - The Ledger client.
+- `wallet` _LocalWallet_ - The wallet of the agent.
+- `agent_address` _str_ - The address of the agent.
+- `name` _str_ - The name to be registered.
+- `domain` _str_ - The domain in which the name is registered.
+
+
+
+#### get`_`name`_`service`_`contract
+
+```python
+def get_name_service_contract() -> NameServiceContract
+```
+
+Get the NameServiceContract instance.
+
+**Returns**:
+
+- `NameServiceContract` - The NameServiceContract instance.
+
diff --git a/python/docs/api/uagents/protocol.md b/python/docs/api/uagents/protocol.md
new file mode 100644
index 00000000..fd170eab
--- /dev/null
+++ b/python/docs/api/uagents/protocol.md
@@ -0,0 +1,286 @@
+
+
+# src.uagents.protocol
+
+Exchange Protocol
+
+
+
+## Protocol Objects
+
+```python
+class Protocol()
+```
+
+
+
+#### `__`init`__`
+
+```python
+def __init__(name: Optional[str] = None, version: Optional[str] = None)
+```
+
+Initialize a Protocol instance.
+
+**Arguments**:
+
+- `name` _Optional[str], optional_ - The name of the protocol. Defaults to None.
+- `version` _Optional[str], optional_ - The version of the protocol. Defaults to None.
+
+
+
+#### intervals
+
+```python
+@property
+def intervals()
+```
+
+Property to access the interval handlers.
+
+**Returns**:
+
+ List[Tuple[IntervalCallback, float]]: List of interval handlers and their periods.
+
+
+
+#### models
+
+```python
+@property
+def models()
+```
+
+Property to access the registered models.
+
+**Returns**:
+
+ Dict[str, Type[Model]]: Dictionary of registered models with schema digests as keys.
+
+
+
+#### replies
+
+```python
+@property
+def replies()
+```
+
+Property to access the registered replies.
+
+**Returns**:
+
+ Dict[str, Dict[str, Type[Model]]]: Dictionary of registered replies with request
+ schema digests as keys.
+
+
+
+#### interval`_`messages
+
+```python
+@property
+def interval_messages()
+```
+
+Property to access the interval message digests.
+
+**Returns**:
+
+- `Set[str]` - Set of message digests associated with interval messages.
+
+
+
+#### signed`_`message`_`handlers
+
+```python
+@property
+def signed_message_handlers()
+```
+
+Property to access the signed message handlers.
+
+**Returns**:
+
+ Dict[str, MessageCallback]: Dictionary of signed message handlers with message schema
+ digests as keys.
+
+
+
+#### unsigned`_`message`_`handlers
+
+```python
+@property
+def unsigned_message_handlers()
+```
+
+Property to access the unsigned message handlers.
+
+**Returns**:
+
+ Dict[str, MessageCallback]: Dictionary of unsigned message handlers with message schema
+ digests as keys.
+
+
+
+#### name
+
+```python
+@property
+def name()
+```
+
+Property to access the protocol name.
+
+**Returns**:
+
+- `str` - The protocol name.
+
+
+
+#### version
+
+```python
+@property
+def version()
+```
+
+Property to access the protocol version.
+
+**Returns**:
+
+- `str` - The protocol version.
+
+
+
+#### canonical`_`name
+
+```python
+@property
+def canonical_name()
+```
+
+Property to access the canonical name of the protocol.
+
+**Returns**:
+
+- `str` - The canonical name of the protocol.
+
+
+
+#### digest
+
+```python
+@property
+def digest()
+```
+
+Property to access the digest of the protocol's manifest.
+
+**Returns**:
+
+- `str` - The digest of the protocol's manifest.
+
+
+
+#### on`_`interval
+
+```python
+def on_interval(period: float,
+ messages: Optional[Union[Type[Model],
+ Set[Type[Model]]]] = None)
+```
+
+Decorator to register an interval handler for the protocol.
+
+**Arguments**:
+
+- `period` _float_ - The interval period in seconds.
+- `messages` _Optional[Union[Type[Model], Set[Type[Model]]]], optional_ - The associated
+ message types. Defaults to None.
+
+
+**Returns**:
+
+- `Callable` - The decorator to register the interval handler.
+
+
+
+#### on`_`query
+
+```python
+def on_query(model: Type[Model],
+ replies: Optional[Union[Type[Model], Set[Type[Model]]]] = None)
+```
+
+Decorator to register a query handler for the protocol.
+
+**Arguments**:
+
+- `model` _Type[Model]_ - The message model type.
+- `replies` _Optional[Union[Type[Model], Set[Type[Model]]]], optional_ - The associated
+ reply types. Defaults to None.
+
+
+**Returns**:
+
+- `Callable` - The decorator to register the query handler.
+
+
+
+#### on`_`message
+
+```python
+def on_message(model: Type[Model],
+ replies: Optional[Union[Type[Model], Set[Type[Model]]]] = None,
+ allow_unverified: Optional[bool] = False)
+```
+
+Decorator to register a message handler for the protocol.
+
+**Arguments**:
+
+- `model` _Type[Model]_ - The message model type.
+- `replies` _Optional[Union[Type[Model], Set[Type[Model]]]], optional_ - The associated
+ reply types. Defaults to None.
+- `allow_unverified` _Optional[bool], optional_ - Whether to allow unverified messages.
+ Defaults to False.
+
+
+**Returns**:
+
+- `Callable` - The decorator to register the message handler.
+
+
+
+#### manifest
+
+```python
+def manifest() -> Dict[str, Any]
+```
+
+Generate the protocol's manifest.
+
+**Returns**:
+
+ Dict[str, Any]: The protocol's manifest.
+
+
+
+#### compute`_`digest
+
+```python
+@staticmethod
+def compute_digest(manifest: Dict[str, Any]) -> str
+```
+
+Compute the digest of a given manifest.
+
+**Arguments**:
+
+- `manifest` _Dict[str, Any]_ - The manifest to compute the digest for.
+
+
+**Returns**:
+
+- `str` - The computed digest.
+
diff --git a/python/docs/api/uagents/query.md b/python/docs/api/uagents/query.md
new file mode 100644
index 00000000..1238b9f0
--- /dev/null
+++ b/python/docs/api/uagents/query.md
@@ -0,0 +1,76 @@
+
+
+# src.uagents.query
+
+Query Envelopes.
+
+
+
+#### query
+
+```python
+async def query(destination: str,
+ message: Model,
+ resolver: Optional[Resolver] = None,
+ timeout: Optional[int] = 30) -> Optional[Envelope]
+```
+
+Query a remote agent with a message and retrieve the response envelope.
+
+**Arguments**:
+
+- `destination` _str_ - The destination address of the remote agent.
+- `message` _Model_ - The message to send.
+- `resolver` _Optional[Resolver], optional_ - The resolver to use for endpoint resolution.
+ Defaults to GlobalResolver.
+- `timeout` _Optional[int], optional_ - The timeout for the query in seconds. Defaults to 30.
+
+
+**Returns**:
+
+- `Optional[Envelope]` - The response envelope if successful, otherwise None.
+
+
+
+#### enclose`_`response
+
+```python
+def enclose_response(message: Model, sender: str, session: str) -> str
+```
+
+Enclose a response message within an envelope.
+
+**Arguments**:
+
+- `message` _Model_ - The response message to enclose.
+- `sender` _str_ - The sender's address.
+- `session` _str_ - The session identifier.
+
+
+**Returns**:
+
+- `str` - The JSON representation of the response envelope.
+
+
+
+#### enclose`_`response`_`raw
+
+```python
+def enclose_response_raw(json_message: JsonStr, schema_digest: str,
+ sender: str, session: str) -> str
+```
+
+Enclose a raw response message within an envelope.
+
+**Arguments**:
+
+- `json_message` _JsonStr_ - The JSON-formatted response message to enclose.
+- `schema_digest` _str_ - The schema digest of the message.
+- `sender` _str_ - The sender's address.
+- `session` _str_ - The session identifier.
+
+
+**Returns**:
+
+- `str` - The JSON representation of the response envelope.
+
diff --git a/python/docs/api/uagents/resolver.md b/python/docs/api/uagents/resolver.md
new file mode 100644
index 00000000..57191638
--- /dev/null
+++ b/python/docs/api/uagents/resolver.md
@@ -0,0 +1,214 @@
+
+
+# src.uagents.resolver
+
+Endpoint Resolver.
+
+
+
+#### query`_`record
+
+```python
+def query_record(agent_address: str, service: str) -> dict
+```
+
+Query a record from the Almanac contract.
+
+**Arguments**:
+
+- `agent_address` _str_ - The address of the agent.
+- `service` _str_ - The type of service to query.
+
+
+**Returns**:
+
+- `dict` - The query result.
+
+
+
+#### get`_`agent`_`address
+
+```python
+def get_agent_address(name: str) -> str
+```
+
+Get the agent address associated with the provided name from the name service contract.
+
+**Arguments**:
+
+- `name` _str_ - The name to query.
+
+
+**Returns**:
+
+- `str` - The associated agent address.
+
+
+
+#### is`_`agent`_`address
+
+```python
+def is_agent_address(address)
+```
+
+Check if the provided address is a valid agent address.
+
+**Arguments**:
+
+- `address` - The address to check.
+
+
+**Returns**:
+
+- `bool` - True if the address is a valid agent address, False otherwise.
+
+
+
+## Resolver Objects
+
+```python
+class Resolver(ABC)
+```
+
+
+
+#### resolve
+
+```python
+@abstractmethod
+async def resolve(destination: str) -> Optional[str]
+```
+
+Resolve the destination to an endpoint.
+
+**Arguments**:
+
+- `destination` _str_ - The destination to resolve.
+
+
+**Returns**:
+
+- `Optional[str]` - The resolved endpoint or None.
+
+
+
+## GlobalResolver Objects
+
+```python
+class GlobalResolver(Resolver)
+```
+
+
+
+#### resolve
+
+```python
+async def resolve(destination: str) -> Optional[str]
+```
+
+Resolve the destination using a combination of Almanac and NameService resolvers.
+
+**Arguments**:
+
+- `destination` _str_ - The destination to resolve.
+
+
+**Returns**:
+
+- `Optional[str]` - The resolved endpoint or None.
+
+
+
+## AlmanacResolver Objects
+
+```python
+class AlmanacResolver(Resolver)
+```
+
+
+
+#### resolve
+
+```python
+async def resolve(destination: str) -> Optional[str]
+```
+
+Resolve the destination using the Almanac contract.
+
+**Arguments**:
+
+- `destination` _str_ - The destination to resolve.
+
+
+**Returns**:
+
+- `Optional[str]` - The resolved endpoint or None.
+
+
+
+## NameServiceResolver Objects
+
+```python
+class NameServiceResolver(Resolver)
+```
+
+
+
+#### resolve
+
+```python
+async def resolve(destination: str) -> Optional[str]
+```
+
+Resolve the destination using the NameService contract.
+
+**Arguments**:
+
+- `destination` _str_ - The destination to resolve.
+
+
+**Returns**:
+
+- `Optional[str]` - The resolved endpoint or None.
+
+
+
+## RulesBasedResolver Objects
+
+```python
+class RulesBasedResolver(Resolver)
+```
+
+
+
+#### `__`init`__`
+
+```python
+def __init__(rules: Dict[str, str])
+```
+
+Initialize the RulesBasedResolver with the provided rules.
+
+**Arguments**:
+
+- `rules` _Dict[str, str]_ - A dictionary of rules mapping destinations to endpoints.
+
+
+
+#### resolve
+
+```python
+async def resolve(destination: str) -> Optional[str]
+```
+
+Resolve the destination using the provided rules.
+
+**Arguments**:
+
+- `destination` _str_ - The destination to resolve.
+
+
+**Returns**:
+
+- `Optional[str]` - The resolved endpoint or None.
+
diff --git a/python/docs/api/uagents/setup.md b/python/docs/api/uagents/setup.md
new file mode 100644
index 00000000..5d755b65
--- /dev/null
+++ b/python/docs/api/uagents/setup.md
@@ -0,0 +1,45 @@
+
+
+# src.uagents.setup
+
+Agent's Setup.
+
+
+
+#### fund`_`agent`_`if`_`low
+
+```python
+def fund_agent_if_low(wallet_address: str)
+```
+
+Checks the agent's wallet balance and adds funds if it's below the registration fee.
+
+**Arguments**:
+
+- `wallet_address` _str_ - The wallet address of the agent.
+
+
+**Returns**:
+
+ None
+
+
+
+#### register`_`agent`_`with`_`mailbox
+
+```python
+def register_agent_with_mailbox(agent: Agent, email: str)
+```
+
+Registers the agent on a mailbox server using the provided email.
+
+**Arguments**:
+
+- `agent` _Agent_ - The agent object to be registered.
+- `email` _str_ - The email address associated with the agent.
+
+
+**Returns**:
+
+ None
+
diff --git a/python/docs/api/uagents/storage/__init__.md b/python/docs/api/uagents/storage/__init__.md
new file mode 100644
index 00000000..92a6a77d
--- /dev/null
+++ b/python/docs/api/uagents/storage/__init__.md
@@ -0,0 +1,96 @@
+
+
+# src.uagents.storage.`__`init`__`
+
+
+
+## KeyValueStore Objects
+
+```python
+class KeyValueStore()
+```
+
+A simple key-value store implementation for data storage.
+
+**Attributes**:
+
+- `_data` _dict_ - The internal data storage dictionary.
+- `_name` _str_ - The name associated with the store.
+- `_path` _str_ - The file path where the store data is stored.
+
+
+**Methods**:
+
+- `__init__` - Initialize the KeyValueStore instance.
+- `get` - Get the value associated with a key from the store.
+- `has` - Check if a key exists in the store.
+- `set` - Set a value associated with a key in the store.
+- `remove` - Remove a key and its associated value from the store.
+- `clear` - Clear all data from the store.
+- `_load` - Load data from the file into the store.
+- `_save` - Save the store data to the file.
+
+
+
+#### `__`init`__`
+
+```python
+def __init__(name: str, cwd: str = None)
+```
+
+Initialize the KeyValueStore instance.
+
+**Arguments**:
+
+- `name` _str_ - The name associated with the store.
+- `cwd` _str, optional_ - The current working directory. Defaults to None.
+
+
+
+#### load`_`all`_`keys
+
+```python
+def load_all_keys() -> dict
+```
+
+Load all private keys from the private keys file.
+
+**Returns**:
+
+- `dict` - A dictionary containing loaded private keys.
+
+
+
+#### save`_`private`_`keys
+
+```python
+def save_private_keys(name: str, identity_key: str, wallet_key: str)
+```
+
+Save private keys to the private keys file.
+
+**Arguments**:
+
+- `name` _str_ - The name associated with the private keys.
+- `identity_key` _str_ - The identity private key.
+- `wallet_key` _str_ - The wallet private key.
+
+
+
+#### get`_`or`_`create`_`private`_`keys
+
+```python
+def get_or_create_private_keys(name: str) -> Tuple[str, str]
+```
+
+Get or create private keys associated with a name.
+
+**Arguments**:
+
+- `name` _str_ - The name associated with the private keys.
+
+
+**Returns**:
+
+ Tuple[str, str]: A tuple containing the identity key and wallet key.
+
diff --git a/python/mkdocs.yml b/python/mkdocs.yml
index f29c4a5e..c7837814 100644
--- a/python/mkdocs.yml
+++ b/python/mkdocs.yml
@@ -25,6 +25,17 @@ nav:
- Endpoint weighting: almanac-endpoint.md
- Exchange protocol: 'protocol.md'
# - Marketplace contract:
+ - API:
+ - Agent: 'api/uagents/agent.md'
+ - Context: 'api/uagents/context.md'
+ - Envelope: 'api/uagents/envelope.md'
+ - Network and Contracts: 'api/uagents/network.md'
+ - Protocol: 'api/uagents/protocol.md'
+ - Envelope query: 'api/uagents/query.md'
+ - Endpoint resolver: 'api/uagents/resolver.md'
+ - Agent setup: 'api/uagents/setup.md'
+ - Storage:
+ - Storage functionality: 'api/uagents/storage/__init__.md'
theme:
name: material
diff --git a/python/scripts/generate_api_docs.py b/python/scripts/generate_api_docs.py
new file mode 100644
index 00000000..96a1890b
--- /dev/null
+++ b/python/scripts/generate_api_docs.py
@@ -0,0 +1,162 @@
+"""This tool generates the API docs."""
+import argparse
+import re
+import shutil
+import subprocess # nosec
+import sys
+from pathlib import Path
+
+
+DOCS_DIR = Path("docs/")
+API_DIR = DOCS_DIR / "api/"
+UAGENTS_DIR = Path("src/uagents")
+
+IGNORE_NAMES = {}
+IGNORE_PREFIXES = {
+ Path(UAGENTS_DIR, "__init__.py"),
+ Path(UAGENTS_DIR, "asgi.py"),
+ Path(UAGENTS_DIR, "config.py"),
+ Path(UAGENTS_DIR, "contrib"),
+ Path(UAGENTS_DIR, "crypto"),
+ Path(UAGENTS_DIR, "dispatch.py"),
+ Path(UAGENTS_DIR, "mailbox.py"),
+ Path(UAGENTS_DIR, "models.py"),
+}
+
+
+def create_subdir(path: str) -> None:
+ """
+ Create a subdirectory.
+ :param path: the directory path
+ """
+ directory = "/".join(path.split("/")[:-1])
+ Path(directory).mkdir(parents=True, exist_ok=True)
+
+
+def replace_underscores(text: str) -> str:
+ """
+ Replace escaped underscores in a text.
+ :param text: the text to replace underscores in
+ :return: the processed text
+ """
+ text_a = text.replace("\\_\\_", "`__`")
+ text_b = text_a.replace("\\_", "`_`")
+ return text_b
+
+
+def is_relative_to(path_1: Path, path_2: Path) -> bool:
+ """Check if a path is relative to another path."""
+ return str(path_1).startswith(str(path_2))
+
+
+def is_not_dir(path: Path) -> bool:
+ """Call p.is_dir() method and negate the result."""
+ return not path.is_dir()
+
+
+def should_skip(module_path: Path) -> bool:
+ """Return true if the file should be skipped."""
+ if any(re.search(pattern, module_path.name) for pattern in IGNORE_NAMES):
+ print("Skipping, it's in ignore patterns")
+ return True
+ if module_path.suffix != ".py":
+ print("Skipping, it's not a Python module.")
+ return True
+ if any(is_relative_to(module_path, prefix) for prefix in IGNORE_PREFIXES):
+ print(f"Ignoring prefix {module_path}")
+ return True
+ return False
+
+
+def _generate_apidocs_uagents_modules() -> None:
+ """Generate API docs for uagents.* modules."""
+ for module_path in filter(is_not_dir, Path(UAGENTS_DIR).rglob("*")):
+ print(f"Processing {module_path}... ", end="")
+ if should_skip(module_path):
+ continue
+ parents = module_path.parts[:-1]
+ parents_without_root = module_path.parts[1:-1]
+ last = module_path.stem
+ doc_file = API_DIR / Path(*parents_without_root) / f"{last}.md"
+ dotted_path = ".".join(parents) + "." + last
+ make_pydoc(dotted_path, doc_file)
+
+
+def make_pydoc(dotted_path: str, destination_file: Path) -> None:
+ """Make a PyDoc file."""
+ print(
+ f"Running with dotted path={dotted_path} and destination_file={destination_file}... ",
+ end="",
+ )
+ try:
+ api_doc_content = run_pydoc_markdown(dotted_path)
+ destination_file.parent.mkdir(parents=True, exist_ok=True)
+ destination_file.write_text(api_doc_content)
+ except Exception as ex: # pylint: disable=broad-except
+ print(f"Error: {str(ex)}")
+ return
+ print("Done!")
+
+
+def run_pydoc_markdown(module: str) -> str:
+ """
+ Run pydoc-markdown.
+ :param module: the dotted path.
+ :return: the PyDoc content (pre-processed).
+ """
+ with subprocess.Popen(
+ ["pydoc-markdown", "-m", module, "-I", "."], stdout=subprocess.PIPE
+ ) as pydoc:
+ stdout, _ = pydoc.communicate()
+ pydoc.wait()
+ stdout_text = stdout.decode("utf-8")
+ text = replace_underscores(stdout_text)
+ return text
+
+
+def generate_api_docs() -> None:
+ """Generate the api docs."""
+ shutil.rmtree(API_DIR, ignore_errors=True)
+ API_DIR.mkdir()
+ _generate_apidocs_uagents_modules()
+
+
+def install(package: str) -> int:
+ """
+ Install a PyPI package by calling pip.
+ :param package: the package name and version specifier.
+ :return: the return code.
+ """
+ return subprocess.check_call( # nosec
+ [sys.executable, "-m", "pip", "install", package]
+ )
+
+
+def check_working_tree_is_dirty() -> None:
+ """Check if the current Git working tree is dirty."""
+ print("Checking whether the Git working tree is dirty...")
+ result = subprocess.check_output(["git", "diff", "--stat"]) # nosec
+ if len(result) > 0:
+ print("Git working tree is dirty:")
+ print(result.decode("utf-8"))
+ sys.exit(1)
+ else:
+ print("All good!")
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser("generate_api_docs")
+ parser.add_argument(
+ "--check-clean", action="store_true", help="Check if the working tree is clean."
+ )
+ arguments = parser.parse_args()
+
+ res = shutil.which("pydoc-markdown")
+ if res is None:
+ install("pydoc-markdown")
+ sys.exit(1)
+
+ generate_api_docs()
+
+ if arguments.check_clean:
+ check_working_tree_is_dirty()
diff --git a/python/src/uagents/agent.py b/python/src/uagents/agent.py
index 6e3ce246..ea8bde4e 100644
--- a/python/src/uagents/agent.py
+++ b/python/src/uagents/agent.py
@@ -1,3 +1,5 @@
+"""Agent"""
+
import asyncio
import functools
from typing import Dict, List, Optional, Set, Union, Type, Tuple, Any, Coroutine
@@ -39,6 +41,14 @@
async def _run_interval(func: IntervalCallback, ctx: Context, 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.
+ period (float): The time period at which to run the callback function.
+ """
while True:
try:
await func(ctx)
@@ -53,15 +63,69 @@ async def _run_interval(func: IntervalCallback, ctx: Context, period: float):
async def _delay(coroutine: Coroutine, delay_seconds: float):
+ """
+ Delay the execution of the provided coroutine by the specified number of seconds.
+
+ Args:
+ coroutine (Coroutine): The coroutine to delay.
+ delay_seconds (float): The delay time in seconds.
+ """
await asyncio.sleep(delay_seconds)
await coroutine
async def _handle_error(ctx: Context, destination: str, msg: ErrorMessage):
+ """
+ Handle an error message by sending it to the specified destination.
+
+ Args:
+ ctx (Context): The context for the agent.
+ destination (str): The destination address to send the error message to.
+ msg (ErrorMessage): The error message to handle.
+ """
await ctx.send(destination, msg)
class Agent(Sink):
+ """
+ An agent that interacts within a communication environment.
+
+ Attributes:
+ _name (str): The name of the agent.
+ _port (int): The port on which the agent runs.
+ _background_tasks (Set[asyncio.Task]): Set of background tasks associated with the agent.
+ _resolver (Resolver): The resolver for agent communication.
+ _loop (asyncio.AbstractEventLoop): The asyncio event loop used by the agent.
+ _logger: The logger instance for logging agent activities.
+ _endpoints (List[dict]): List of communication endpoints.
+ _use_mailbox (bool): Indicates if the agent uses a mailbox for communication.
+ _agentverse (dict): Agentverse configuration settings.
+ _mailbox_client (MailboxClient): Client for interacting with the mailbox.
+ _ledger: The ledger for recording agent transactions.
+ _almanac_contract: The almanac contract for agent metadata.
+ _storage: Key-value store for agent data storage.
+ _interval_handlers (List[Tuple[IntervalCallback, float]]): List of interval
+ handlers and their periods.
+ _interval_messages (Set[str]): Set of interval message names.
+ _signed_message_handlers (Dict[str, MessageCallback]): Handlers for signed messages.
+ _unsigned_message_handlers (Dict[str, MessageCallback]): Handlers for
+ unsigned messages.
+ _models (Dict[str, Type[Model]]): Dictionary of supported data models.
+ _replies (Dict[str, Set[Type[Model]]]): Dictionary of reply data models.
+ _queries (Dict[str, asyncio.Future]): Dictionary of active queries.
+ _dispatcher: The dispatcher for message handling.
+ _message_queue: Asynchronous queue for incoming messages.
+ _on_startup (List[Callable]): List of functions to run on agent startup.
+ _on_shutdown (List[Callable]): List of functions to run on agent shutdown.
+ _version (str): The version of the agent.
+ _protocol (Protocol): The internal agent protocol.
+ protocols (Dict[str, Protocol]): Dictionary of supported protocols.
+ _ctx (Context): The context for agent interactions.
+
+ Methods:
+ __init__: Initialize the Agent instance.
+ """
+
def __init__(
self,
name: Optional[str] = None,
@@ -73,6 +137,19 @@ def __init__(
resolve: Optional[Resolver] = None,
version: Optional[str] = None,
):
+ """
+ Initialize an Agent instance.
+
+ Args:
+ name (Optional[str]): The name of the agent.
+ port (Optional[int]): The port on which the agent will run.
+ seed (Optional[str]): The seed for generating keys.
+ endpoint (Optional[Union[str, List[str], Dict[str, dict]]]): The endpoint configuration.
+ agentverse (Optional[Union[str, Dict[str, str]]]): The agentverse configuration.
+ mailbox (Optional[Union[str, Dict[str, str]]]): The mailbox configuration.
+ resolve (Optional[Resolver]): The resolver to use for agent communication.
+ version (Optional[str]): The version of the agent.
+ """
self._name = name
self._port = port if port is not None else 8000
self._background_tasks: Set[asyncio.Task] = set()
@@ -159,6 +236,16 @@ def __init__(
)
def _initialize_wallet_and_identity(self, seed, name):
+ """
+ Initialize the wallet and identity for the agent.
+
+ If seed is provided, the identity and wallet are derived from the seed.
+ If seed is not provided, they are either generated or fetched based on the provided name.
+
+ Args:
+ seed (str or None): The seed for generating keys.
+ name (str or None): The name of the agent.
+ """
if seed is None:
if name is None:
self._wallet = LocalWallet.generate()
@@ -178,47 +265,131 @@ def _initialize_wallet_and_identity(self, seed, name):
@property
def name(self) -> str:
+ """
+ Get the name of the agent.
+
+ Returns:
+ str: The name of the agent.
+ """
return self._name
@property
def address(self) -> str:
+ """
+ Get the address of the agent's identity.
+
+ Returns:
+ str: The address of the agent's identity.
+ """
return self._identity.address
@property
def wallet(self) -> LocalWallet:
+ """
+ Get the wallet of the agent.
+
+ Returns:
+ LocalWallet: The agent's wallet.
+ """
return self._wallet
@property
def storage(self) -> KeyValueStore:
+ """
+ Get the key-value store used by the agent for data storage.
+
+ Returns:
+ KeyValueStore: The key-value store instance.
+ """
return self._storage
@property
def mailbox(self) -> Dict[str, str]:
+ """
+ Get the mailbox configuration of the agent.
+
+ Returns:
+ Dict[str, str]: The mailbox configuration.
+ """
return self._agentverse
@property
def agentverse(self) -> Dict[str, str]:
+ """
+ Get the agentverse configuration of the agent.
+
+ Returns:
+ Dict[str, str]: The agentverse configuration.
+ """
return self._agentverse
@property
def mailbox_client(self) -> MailboxClient:
+ """
+ Get the mailbox client used by the agent for mailbox communication.
+
+ Returns:
+ MailboxClient: The mailbox client instance.
+ """
return self._mailbox_client
@mailbox.setter
def mailbox(self, config: Union[str, Dict[str, str]]):
+ """
+ Set the mailbox configuration for the agent.
+
+ Args:
+ config (Union[str, Dict[str, str]]): The new mailbox configuration.
+ """
self._agentverse = parse_agentverse_config(config)
@agentverse.setter
def agentverse(self, config: Union[str, Dict[str, str]]):
+ """
+ Set the agentverse configuration for the agent.
+
+ Args:
+ config (Union[str, Dict[str, str]]): The new agentverse configuration.
+ """
self._agentverse = parse_agentverse_config(config)
def sign(self, data: bytes) -> str:
+ """
+ Sign the provided data.
+
+ Args:
+ data (bytes): The data to be signed.
+
+ Returns:
+ str: The signature of the data.
+
+ """
return self._identity.sign(data)
def sign_digest(self, digest: bytes) -> str:
+ """
+ Sign the provided digest.
+
+ Args:
+ digest (bytes): The digest to be signed.
+
+ Returns:
+ str: The signature of the digest.
+
+ """
return self._identity.sign_digest(digest)
def sign_registration(self) -> str:
+ """
+ Sign the registration data for Almanac contract.
+
+ Returns:
+ str: The signature of the registration data.
+
+ Raises:
+ AssertionError: If the Almanac contract address is None.
+
+ """
assert self._almanac_contract.address is not None
return self._identity.sign_registration(
str(self._almanac_contract.address),
@@ -226,15 +397,46 @@ def sign_registration(self) -> str:
)
def update_endpoints(self, endpoints: List[Dict[str, Any]]):
+ """
+ Update the list of endpoints.
+
+ Args:
+ endpoints (List[Dict[str, Any]]): List of endpoint dictionaries.
+
+ """
+
self._endpoints = endpoints
def update_loop(self, loop):
+ """
+ Update the event loop.
+
+ Args:
+ loop: The event loop.
+
+ """
+
self._loop = loop
def update_queries(self, queries):
+ """
+ Update the queries attribute.
+
+ Args:
+ queries: The queries attribute.
+
+ """
+
self._queries = queries
async def register(self):
+ """
+ Register with the Almanac contract.
+
+ This method checks for registration conditions and performs registration
+ if necessary.
+
+ """
if self._endpoints is None:
self._logger.warning(
"I have no endpoint and cannot receive external messages"
@@ -276,6 +478,14 @@ async def register(self):
self._logger.info("Almanac registration is up to date!")
async def _registration_loop(self):
+ """
+ Execute the registration loop.
+
+ This method registers with the Almanac contract and schedules the next
+ registration.
+
+ """
+
time_until_next_registration = REGISTRATION_UPDATE_INTERVAL_SECONDS
try:
await self.register()
@@ -292,6 +502,18 @@ def on_interval(
period: float,
messages: Optional[Union[Type[Model], Set[Type[Model]]]] = None,
):
+ """
+ Set up an interval event with a callback.
+
+ Args:
+ period (float): The interval period.
+ messages (Optional[Union[Type[Model], Set[Type[Model]]]]): Optional message types.
+
+ Returns:
+ Callable: The callback function for the interval event.
+
+ """
+
return self._protocol.on_interval(period, messages)
def on_query(
@@ -299,6 +521,18 @@ def on_query(
model: Type[Model],
replies: Optional[Union[Model, Set[Model]]] = None,
):
+ """
+ Set up a query event with a callback.
+
+ Args:
+ model (Type[Model]): The query model.
+ replies (Optional[Union[Model, Set[Model]]]): Optional reply models.
+
+ Returns:
+ Callable: The callback function for the query event.
+
+ """
+
return self._protocol.on_query(model, replies)
def on_message(
@@ -307,10 +541,45 @@ def on_message(
replies: Optional[Union[Type[Model], Set[Type[Model]]]] = None,
allow_unverified: Optional[bool] = False,
):
+ """
+ Set up a message event with a callback.
+
+ Args:
+ model (Type[Model]): The message model.
+ replies (Optional[Union[Type[Model], Set[Type[Model]]]]): Optional reply models.
+ allow_unverified (Optional[bool]): Allow unverified messages.
+
+ Returns:
+ Callable: The callback function for the message event.
+
+ """
+
return self._protocol.on_message(model, replies, allow_unverified)
def on_event(self, event_type: str):
+ """
+ Decorator to register an event handler for a specific event type.
+
+ Args:
+ event_type (str): The type of event.
+
+ Returns:
+ Callable: The decorator function for registering event handlers.
+
+ """
+
def decorator_on_event(func: EventCallback) -> EventCallback:
+ """
+ Decorator function to register an event handler for a specific event type.
+
+ Args:
+ func (EventCallback): The event handler function.
+
+ Returns:
+ EventCallback: The decorated event handler function.
+
+ """
+
@functools.wraps(func)
def handler(*args, **kwargs):
return func(*args, **kwargs)
@@ -326,12 +595,33 @@ def _add_event_handler(
event_type: str,
func: EventCallback,
) -> None:
+ """
+ Add an event handler function to the specified event type.
+
+ Args:
+ event_type (str): The type of event.
+ func (EventCallback): The event handler function.
+
+ """
+
if event_type == "startup":
self._on_startup.append(func)
elif event_type == "shutdown":
self._on_shutdown.append(func)
def include(self, protocol: Protocol, publish_manifest: Optional[bool] = False):
+ """
+ Include a protocol into the agent's capabilities.
+
+ Args:
+ protocol (Protocol): The protocol to include.
+ publish_manifest (Optional[bool]): Flag to publish the protocol's manifest.
+
+ Raises:
+ RuntimeError: If a duplicate model, signed message handler, or message handler
+ is encountered.
+
+ """
for func, period in protocol.intervals:
self._interval_handlers.append((func, period))
@@ -365,6 +655,13 @@ def include(self, protocol: Protocol, publish_manifest: Optional[bool] = False):
self.publish_manifest(protocol.manifest())
def publish_manifest(self, manifest: Dict[str, Any]):
+ """
+ Publish a protocol manifest to the Almanac.
+
+ Args:
+ manifest (Dict[str, Any]): The protocol manifest.
+
+ """
try:
resp = requests.post(
f"{self._agentverse['http_prefix']}://{self._agentverse['base_url']}"
@@ -384,9 +681,23 @@ def publish_manifest(self, manifest: Dict[str, Any]):
async def handle_message(
self, sender, schema_digest: str, message: JsonStr, session: uuid.UUID
):
+ """
+ Handle an incoming message asynchronously.
+
+ Args:
+ sender: The sender of the message.
+ schema_digest (str): The schema digest of the message.
+ message (JsonStr): The message content in JSON format.
+ session (uuid.UUID): The session UUID.
+
+ """
await self._message_queue.put((schema_digest, sender, message, session))
async def _startup(self):
+ """
+ Perform startup actions asynchronously.
+
+ """
await self._registration_loop()
for handler in self._on_startup:
try:
@@ -399,6 +710,10 @@ async def _startup(self):
self._logger.exception(f"Exception in startup handler: {ex}")
async def _shutdown(self):
+ """
+ Perform shutdown actions asynchronously.
+
+ """
for handler in self._on_shutdown:
try:
await handler(self._ctx)
@@ -410,6 +725,10 @@ async def _shutdown(self):
self._logger.exception(f"Exception in shutdown handler: {ex}")
def setup(self):
+ """
+ Set up the agent.
+
+ """
# register the internal agent protocol
self.include(self._protocol)
self._loop.run_until_complete(self._startup())
@@ -420,6 +739,10 @@ def setup(self):
self.start_background_tasks()
def start_background_tasks(self):
+ """
+ Start background tasks for the agent.
+
+ """
# Start the interval tasks
for func, period in self._interval_handlers:
task = self._loop.create_task(_run_interval(func, self._ctx, period))
@@ -432,6 +755,10 @@ def start_background_tasks(self):
task.add_done_callback(self._background_tasks.discard)
def run(self):
+ """
+ Run the agent.
+
+ """
self.setup()
try:
if self._use_mailbox:
@@ -443,6 +770,10 @@ def run(self):
self._loop.run_until_complete(self._shutdown())
async def _process_message_queue(self):
+ """
+ Process the message queue asynchronously.
+
+ """
while True:
# get an element from the queue
schema_digest, sender, message, session = await self._message_queue.get()
@@ -507,11 +838,41 @@ async def _process_message_queue(self):
class Bureau:
+ """
+ A class representing a Bureau of agents.
+
+ This class manages a collection of agents and orchestrates their execution.
+
+ Args:
+ port (Optional[int]): The port number for the server.
+ endpoint (Optional[Union[str, List[str], Dict[str, dict]]]): Configuration
+ for agent endpoints.
+
+ Attributes:
+ _loop (asyncio.AbstractEventLoop): The event loop.
+ _agents (List[Agent]): A list of Agent instances within the bureau.
+ _endpoints (List[Dict[str, Any]]): A list of endpoint dictionaries for the agents.
+ _port (int): The port number for the server.
+ _queries (Dict[str, asyncio.Future]): A dictionary of query identifiers to asyncio futures.
+ _logger (Logger): The logger instance.
+ _server (ASGIServer): The ASGI server instance for handling requests.
+ _use_mailbox (bool): A flag indicating whether mailbox functionality is enabled.
+
+ """
+
def __init__(
self,
port: Optional[int] = None,
endpoint: Optional[Union[str, List[str], Dict[str, dict]]] = None,
):
+ """
+ Initialize a Bureau instance.
+
+ Args:
+ port (Optional[int]): The port number for the server.
+ endpoint (Optional[Union[str, List[str], Dict[str, dict]]]): Configuration
+ for agent endpoints.
+ """
self._loop = asyncio.get_event_loop_policy().get_event_loop()
self._agents: List[Agent] = []
self._endpoints = parse_endpoint_config(endpoint)
@@ -522,6 +883,13 @@ def __init__(
self._use_mailbox = False
def add(self, agent: Agent):
+ """
+ Add an agent to the bureau.
+
+ Args:
+ agent (Agent): The agent instance to be added.
+
+ """
agent.update_loop(self._loop)
agent.update_queries(self._queries)
if agent.agentverse["use_mailbox"]:
@@ -531,6 +899,10 @@ def add(self, agent: Agent):
self._agents.append(agent)
def run(self):
+ """
+ Run the agents managed by the bureau.
+
+ """
tasks = []
for agent in self._agents:
agent.setup()
diff --git a/python/src/uagents/context.py b/python/src/uagents/context.py
index 8ccd92db..dc13ab2d 100644
--- a/python/src/uagents/context.py
+++ b/python/src/uagents/context.py
@@ -1,3 +1,5 @@
+"""Agent Context and Message Handling"""
+
from __future__ import annotations
import asyncio
@@ -44,6 +46,14 @@
@dataclass
class MsgDigest:
+ """
+ Represents a message digest containing a message and its schema digest.
+
+ Attributes:
+ message (Any): The message content.
+ schema_digest (str): The schema digest of the message.
+ """
+
message: Any
schema_digest: str
@@ -52,6 +62,38 @@ class MsgDigest:
class Context:
+ """
+ Represents the context in which messages are handled and processed.
+
+ Attributes:
+ storage (KeyValueStore): The key-value store for storage operations.
+ wallet (LocalWallet): The local wallet instance for managing identities.
+ ledger (LedgerClient): The ledger client for interacting with distributed ledgers.
+ _name (Optional[str]): The optional name associated with the context.
+ _address (str): The address of the context.
+ _resolver (Resolver): The resolver for name-to-address resolution.
+ _identity (Identity): The identity associated with the context.
+ _queries (Dict[str, asyncio.Future]): Dictionary of query names and their asyncio Futures.
+ _session (Optional[uuid.UUID]): The optional session UUID.
+ _replies (Optional[Dict[str, Set[Type[Model]]]]): The optional dictionary of reply models.
+ _interval_messages (Optional[Set[str]]): The optional set of interval messages.
+ _message_received (Optional[MsgDigest]): The optional message digest received.
+ _protocols (Optional[Dict[str, Protocol]]): The optional dictionary of protocols.
+ _logger (Optional[logging.Logger]): The optional logger instance.
+
+ Properties:
+ name (str): The name associated with the context, or a truncated address if name is None.
+ address (str): The address of the context.
+ logger (logging.Logger): The logger instance.
+ protocols (Optional[Dict[str, Protocol]]): The dictionary of protocols.
+
+ Methods:
+ get_message_protocol(message_schema_digest): Get the protocol associated
+ with a message schema digest.
+ send(destination, message, timeout): Send a message to a destination.
+
+ """
+
def __init__(
self,
address: str,
@@ -69,6 +111,25 @@ def __init__(
protocols: Optional[Dict[str, Protocol]] = None,
logger: Optional[logging.Logger] = None,
):
+ """
+ Initialize the Context instance.
+
+ Args:
+ address (str): The address of the context.
+ name (Optional[str]): The optional name associated with the context.
+ storage (KeyValueStore): The key-value store for storage operations.
+ resolve (Resolver): The resolver for name-to-address resolution.
+ identity (Identity): The identity associated with the context.
+ wallet (LocalWallet): The local wallet instance for managing identities.
+ ledger (LedgerClient): The ledger client for interacting with distributed ledgers.
+ queries (Dict[str, asyncio.Future]): Dictionary of query names and their Futures.
+ session (Optional[uuid.UUID]): The optional session UUID.
+ replies (Optional[Dict[str, Set[Type[Model]]]]): Optional dictionary of reply models.
+ interval_messages (Optional[Set[str]]): The optional set of interval messages.
+ message_received (Optional[MsgDigest]): The optional message digest received.
+ protocols (Optional[Dict[str, Protocol]]): The optional dictionary of protocols.
+ logger (Optional[logging.Logger]): The optional logger instance.
+ """
self.storage = storage
self.wallet = wallet
self.ledger = ledger
@@ -86,27 +147,67 @@ def __init__(
@property
def name(self) -> str:
+ """
+ Get the name associated with the context or a truncated address if name is None.
+
+ Returns:
+ str: The name or truncated address.
+ """
if self._name is not None:
return self._name
return self._address[:10]
@property
def address(self) -> str:
+ """
+ Get the address of the context.
+
+ Returns:
+ str: The address of the context.
+ """
return self._address
@property
def logger(self) -> logging.Logger:
+ """
+ Get the logger instance associated with the context.
+
+ Returns:
+ logging.Logger: The logger instance.
+ """
return self._logger
@property
def protocols(self) -> Optional[Dict[str, Protocol]]:
+ """
+ Get the dictionary of protocols associated with the context.
+
+ Returns:
+ Optional[Dict[str, Protocol]]: The dictionary of protocols.
+ """
return self._protocols
@property
def session(self) -> uuid.UUID:
+ """
+ Get the session UUID associated with the context.
+
+ Returns:
+ uuid.UUID: The session UUID.
+ """
return self._session
def get_message_protocol(self, message_schema_digest) -> Optional[str]:
+ """
+ Get the protocol associated with a given message schema digest.
+
+ Args:
+ message_schema_digest (str): The schema digest of the message.
+
+ Returns:
+ Optional[str]: The protocol digest associated with the message schema digest,
+ or None if not found.
+ """
for protocol_digest, protocol in self._protocols.items():
for reply_models in protocol.replies.values():
if message_schema_digest in reply_models:
@@ -116,6 +217,19 @@ def get_message_protocol(self, message_schema_digest) -> Optional[str]:
def get_agents_by_protocol(
self, protocol_digest: str, limit: Optional[int] = None
) -> List[str]:
+ """Retrieve a list of agent addresses using a specific protocol digest.
+
+ This method queries the Almanac API to retrieve a list of agent addresses
+ that are associated with a given protocol digest. The list can be optionally
+ limited to a specified number of addresses.
+
+ Args:
+ protocol_digest (str): The protocol digest to search for, starting with "proto:".
+ limit (int, optional): The maximum number of agent addresses to return.
+
+ Returns:
+ List[str]: A list of agent addresses using the specified protocol digest.
+ """
if not isinstance(protocol_digest, str) or not protocol_digest.startswith(
"proto:"
):
@@ -138,6 +252,14 @@ async def send(
message: Model,
timeout: Optional[int] = DEFAULT_ENVELOPE_TIMEOUT_SECONDS,
):
+ """
+ Send a message to the specified destination.
+
+ Args:
+ destination (str): The destination address to send the message to.
+ message (Model): The message to be sent.
+ timeout (Optional[int]): The optional timeout for sending the message, in seconds.
+ """
schema_digest = Model.build_schema_digest(message)
await self.send_raw(
destination,
@@ -154,6 +276,21 @@ async def experimental_broadcast(
limit: Optional[int] = DEFAULT_SEARCH_LIMIT,
timeout: Optional[int] = DEFAULT_ENVELOPE_TIMEOUT_SECONDS,
):
+ """Broadcast a message to agents with a specific protocol.
+
+ This asynchronous method broadcasts a given message to agents associated
+ with a specific protocol. The message is sent to multiple agents concurrently.
+ The schema digest of the message is used for verification.
+
+ Args:
+ destination_protocol (str): The protocol to filter agents by.
+ message (Model): The message to broadcast.
+ limit (int, optional): The maximum number of agents to send the message to.
+ timeout (int, optional): The timeout for sending each message.
+
+ Returns:
+ None
+ """
agents = self.get_agents_by_protocol(destination_protocol, limit=limit)
if not agents:
self.logger.error(f"No active agents found for: {destination_protocol}")
@@ -181,7 +318,17 @@ async def send_raw(
message_type: Optional[Type[Model]] = None,
timeout: Optional[int] = DEFAULT_ENVELOPE_TIMEOUT_SECONDS,
):
- # check if this message is a reply
+ """
+ Send a raw message to the specified destination.
+
+ Args:
+ destination (str): The destination address to send the message to.
+ json_message (JsonStr): The JSON-encoded message to be sent.
+ schema_digest (str): The schema digest of the message.
+ message_type (Optional[Type[Model]]): The optional type of the message being sent.
+ timeout (Optional[int]): The optional timeout for sending the message, in seconds.
+ """
+ # Check if this message is a reply
if (
self._message_received is not None
and self._replies
@@ -189,7 +336,7 @@ async def send_raw(
):
received = self._message_received
if received.schema_digest in self._replies:
- # ensure the reply is valid
+ # Ensure the reply is valid
if schema_digest not in self._replies[received.schema_digest]:
self._logger.exception(
f"Outgoing message {message_type or ''} "
@@ -197,7 +344,7 @@ async def send_raw(
)
return
- # check if this message is a valid interval message
+ # Check if this message is a valid interval message
if self._message_received is None and self._interval_messages:
if schema_digest not in self._interval_messages:
self._logger.exception(
@@ -205,20 +352,20 @@ async def send_raw(
)
return
- # handle local dispatch of messages
+ # Handle local dispatch of messages
if dispatcher.contains(destination):
await dispatcher.dispatch(
self.address, destination, schema_digest, json_message, self._session
)
return
- # handle queries waiting for a response
+ # Handle queries waiting for a response
if destination in self._queries:
self._queries[destination].set_result((json_message, schema_digest))
del self._queries[destination]
return
- # resolve the endpoint
+ # Resolve the endpoint
destination_address, endpoint = await self._resolver.resolve(destination)
if endpoint is None:
self._logger.exception(
@@ -226,10 +373,10 @@ async def send_raw(
)
return
- # calculate when envelope expires
+ # Calculate when the envelope expires
expires = int(time()) + timeout
- # handle external dispatch of messages
+ # Handle external dispatch of messages
env = Envelope(
version=1,
sender=self.address,
diff --git a/python/src/uagents/envelope.py b/python/src/uagents/envelope.py
index 5349be10..8e87206f 100644
--- a/python/src/uagents/envelope.py
+++ b/python/src/uagents/envelope.py
@@ -1,3 +1,5 @@
+"""Agent Envelope."""
+
import base64
import hashlib
import struct
@@ -10,6 +12,22 @@
class Envelope(BaseModel):
+ """
+ Represents an envelope for message communication between agents.
+
+ Attributes:
+ version (int): The envelope version.
+ sender (str): The sender's address.
+ target (str): The target's address.
+ session (UUID4): The session UUID.
+ schema_digest (str): The schema digest (alias for protocol).
+ protocol_digest (Optional[str]): The protocol digest (optional).
+ payload (Optional[str]): The payload data (optional).
+ expires (Optional[int]): The expiration timestamp (optional).
+ nonce (Optional[int]): The nonce value (optional).
+ signature (Optional[str]): The envelope signature (optional).
+ """
+
version: int
sender: str
target: str
@@ -25,24 +43,54 @@ class Config:
allow_population_by_field_name = True
def encode_payload(self, value: JsonStr):
+ """
+ Encode the payload value and store it in the envelope.
+
+ Args:
+ value (JsonStr): The payload value to be encoded.
+ """
self.payload = base64.b64encode(value.encode()).decode()
def decode_payload(self) -> Optional[Any]:
+ """
+ Decode and retrieve the payload value from the envelope.
+
+ Returns:
+ Optional[Any]: The decoded payload value, or None if payload is not present.
+ """
if self.payload is None:
return None
return base64.b64decode(self.payload).decode()
def sign(self, identity: Identity):
+ """
+ Sign the envelope using the provided identity.
+
+ Args:
+ identity (Identity): The identity used for signing.
+ """
self.signature = identity.sign_digest(self._digest())
def verify(self) -> bool:
+ """
+ Verify the envelope's signature.
+
+ Returns:
+ bool: True if the signature is valid, False otherwise.
+ """
if self.signature is None:
return False
return Identity.verify_digest(self.sender, self._digest(), self.signature)
def _digest(self) -> bytes:
+ """
+ Compute the digest of the envelope's content.
+
+ Returns:
+ bytes: The computed digest.
+ """
hasher = hashlib.sha256()
hasher.update(self.sender.encode())
hasher.update(self.target.encode())
diff --git a/python/src/uagents/network.py b/python/src/uagents/network.py
index caa19277..09b0986f 100644
--- a/python/src/uagents/network.py
+++ b/python/src/uagents/network.py
@@ -1,3 +1,5 @@
+"""Network and Contracts."""
+
import asyncio
from datetime import datetime, timedelta
from typing import Any, Optional, Dict, List
@@ -31,6 +33,7 @@
logger = get_logger("network")
+# Setting up the Ledger and Faucet based on Agent Network
if AGENT_NETWORK == AgentNetwork.FETCHAI_TESTNET:
_ledger = LedgerClient(NetworkConfig.fetchai_stable_testnet())
_faucet_api = FaucetApi(NetworkConfig.fetchai_stable_testnet())
@@ -41,10 +44,22 @@
def get_ledger() -> LedgerClient:
+ """
+ Get the Ledger client.
+
+ Returns:
+ LedgerClient: The Ledger client instance.
+ """
return _ledger
def get_faucet() -> FaucetApi:
+ """
+ Get the Faucet API instance.
+
+ Returns:
+ FaucetApi: The Faucet API instance.
+ """
return _faucet_api
@@ -53,6 +68,19 @@ async def wait_for_tx_to_complete(
timeout: Optional[timedelta] = None,
poll_period: Optional[timedelta] = None,
) -> TxResponse:
+ """
+ Wait for a transaction to complete on the Ledger.
+
+ Args:
+ tx_hash (str): The hash of the transaction to monitor.
+ timeout (Optional[timedelta], optional): The maximum time to wait for
+ the transaction to complete. Defaults to None.
+ poll_period (Optional[timedelta], optional): The time interval to poll
+ the Ledger for the transaction status. Defaults to None.
+
+ Returns:
+ TxResponse: The response object containing the transaction details.
+ """
if timeout is None:
timeout = timedelta(seconds=DEFAULT_QUERY_TIMEOUT_SECS)
if poll_period is None:
@@ -72,7 +100,31 @@ async def wait_for_tx_to_complete(
class AlmanacContract(LedgerContract):
+ """
+ A class representing the Almanac contract for agent registration.
+
+ This class provides methods to interact with the Almanac contract, including
+ checking if an agent is registered, retrieving the expiry height of an agent's
+ registration, and getting the endpoints associated with an agent's registration.
+
+ Args:
+ ledger contract (LedgerContract): An instance of the LedgeContract class.
+
+ Attributes:
+ ledger contract (LedgerContract): An instance of the LedgeContract class.
+
+ """
+
def is_registered(self, address: str) -> bool:
+ """
+ Check if an agent is registered in the Almanac contract.
+
+ Args:
+ address (str): The agent's address.
+
+ Returns:
+ bool: True if the agent is registered, False otherwise.
+ """
query_msg = {"query_records": {"agent_address": address}}
response = self.query(query_msg)
@@ -81,6 +133,15 @@ def is_registered(self, address: str) -> bool:
return True
def get_expiry(self, address: str) -> int:
+ """
+ Get the expiry height of an agent's registration.
+
+ Args:
+ address (str): The agent's address.
+
+ Returns:
+ int: The expiry height of the agent's registration.
+ """
query_msg = {"query_records": {"agent_address": address}}
response = self.query(query_msg)
@@ -95,6 +156,15 @@ def get_expiry(self, address: str) -> int:
return (expiry - height) * AVERAGE_BLOCK_INTERVAL
def get_endpoints(self, address: str):
+ """
+ Get the endpoints associated with an agent's registration.
+
+ Args:
+ address (str): The agent's address.
+
+ Returns:
+ Any: The endpoints associated with the agent's registration.
+ """
query_msg = {"query_records": {"agent_address": address}}
response = self.query(query_msg)
@@ -103,6 +173,15 @@ def get_endpoints(self, address: str):
return response.get("record")[0]["record"]["service"]["endpoints"]
def get_protocols(self, address: str):
+ """
+ Get the protocols associated with an agent's registration.
+
+ Args:
+ address (str): The agent's address.
+
+ Returns:
+ Any: The protocols associated with the agent's registration.
+ """
query_msg = {"query_records": {"agent_address": address}}
response = self.query(query_msg)
@@ -119,6 +198,17 @@ async def register(
endpoints: List[Dict[str, Any]],
signature: str,
):
+ """
+ Register an agent with the Almanac contract.
+
+ Args:
+ ledger (LedgerClient): The Ledger client.
+ wallet (LocalWallet): The agent's wallet.
+ agent_address (str): The agent's address.
+ protocols (List[str]): List of protocols.
+ endpoints (List[Dict[str, Any]]): List of endpoint dictionaries.
+ signature (str): The agent's signature.
+ """
transaction = Transaction()
almanac_msg = {
@@ -150,6 +240,15 @@ async def register(
await wait_for_tx_to_complete(transaction.tx_hash)
def get_sequence(self, address: str) -> int:
+ """
+ Get the agent's sequence number.
+
+ Args:
+ address (str): The agent's address.
+
+ Returns:
+ int: The agent's sequence number.
+ """
query_msg = {"query_sequence": {"agent_address": address}}
sequence = self.query(query_msg)["sequence"]
@@ -160,15 +259,57 @@ def get_sequence(self, address: str) -> int:
def get_almanac_contract() -> AlmanacContract:
+ """
+ Get the AlmanacContract instance.
+
+ Returns:
+ AlmanacContract: The AlmanacContract instance.
+ """
return _almanac_contract
class NameServiceContract(LedgerContract):
+ """
+ A class representing the NameService contract for managing domain names and ownership.
+
+ This class provides methods to interact with the NameService contract, including
+ checking name availability, checking ownership, querying domain public status,
+ obtaining registration transaction details, and registering a name within a domain.
+
+ Args:
+ ledger contract (LedgerContract): An instance of the LedgeContract class.
+
+ Attributes:
+ ledger contract (LedgerContract): An instance of the LedgeContract class.
+
+ """
+
def is_name_available(self, name: str, domain: str):
+ """
+ Check if a name is available within a domain.
+
+ Args:
+ name (str): The name to check.
+ domain (str): The domain to check within.
+
+ Returns:
+ bool: True if the name is available, False otherwise.
+ """
query_msg = {"domain_record": {"domain": f"{name}.{domain}"}}
return self.query(query_msg)["is_available"]
def is_owner(self, name: str, domain: str, wallet_address: str):
+ """
+ Check if the provided wallet address is the owner of a name within a domain.
+
+ Args:
+ name (str): The name to check ownership for.
+ domain (str): The domain to check within.
+ wallet_address (str): The wallet address to check ownership against.
+
+ Returns:
+ bool: True if the wallet address is the owner, False otherwise.
+ """
query_msg = {
"permissions": {
"domain": f"{name}.{domain}",
@@ -179,12 +320,34 @@ def is_owner(self, name: str, domain: str, wallet_address: str):
return permission == "admin"
def is_domain_public(self, domain: str):
+ """
+ Check if a domain is public.
+
+ Args:
+ domain (str): The domain to check.
+
+ Returns:
+ bool: True if the domain is public, False otherwise.
+ """
res = self.query({"domain_record": {"domain": f".{domain}"}})
return res["is_public"]
def get_registration_tx(
self, name: str, wallet_address: str, agent_address: str, domain: str
):
+ """
+ Get the registration transaction for registering a name within a domain.
+
+ Args:
+ name (str): The name to be registered.
+ wallet_address (str): The wallet address initiating the registration.
+ agent_address (str): The address of the agent.
+ domain (str): The domain in which the name is registered.
+
+ Returns:
+ Optional[Transaction]: The registration transaction, or None if the name is not
+ available or not owned by the wallet address.
+ """
if not self.is_name_available(name, domain) and not self.is_owner(
name, domain, wallet_address
):
@@ -214,6 +377,16 @@ async def register(
name: str,
domain: str,
):
+ """
+ Register a name within a domain using the NameService contract.
+
+ Args:
+ ledger (LedgerClient): The Ledger client.
+ wallet (LocalWallet): The wallet of the agent.
+ agent_address (str): The address of the agent.
+ name (str): The name to be registered.
+ domain (str): The domain in which the name is registered.
+ """
logger.info("Registering name...")
if not get_almanac_contract().is_registered(agent_address):
@@ -248,4 +421,10 @@ async def register(
def get_name_service_contract() -> NameServiceContract:
+ """
+ Get the NameServiceContract instance.
+
+ Returns:
+ NameServiceContract: The NameServiceContract instance.
+ """
return _name_service_contract
diff --git a/python/src/uagents/protocol.py b/python/src/uagents/protocol.py
index 40e2a17d..a81a2108 100644
--- a/python/src/uagents/protocol.py
+++ b/python/src/uagents/protocol.py
@@ -1,3 +1,5 @@
+"""Exchange Protocol"""
+
import copy
import functools
import hashlib
@@ -14,6 +16,13 @@
class Protocol:
def __init__(self, name: Optional[str] = None, version: Optional[str] = None):
+ """
+ Initialize a Protocol instance.
+
+ Args:
+ name (Optional[str], optional): The name of the protocol. Defaults to None.
+ version (Optional[str], optional): The version of the protocol. Defaults to None.
+ """
self._interval_handlers: List[Tuple[IntervalCallback, float]] = []
self._interval_messages: Set[str] = set()
self._signed_message_handlers: Dict[str, MessageCallback] = {}
@@ -33,42 +42,105 @@ def __init__(self, name: Optional[str] = None, version: Optional[str] = None):
@property
def intervals(self):
+ """
+ Property to access the interval handlers.
+
+ Returns:
+ List[Tuple[IntervalCallback, float]]: List of interval handlers and their periods.
+ """
return self._interval_handlers
@property
def models(self):
+ """
+ Property to access the registered models.
+
+ Returns:
+ Dict[str, Type[Model]]: Dictionary of registered models with schema digests as keys.
+ """
return self._models
@property
def replies(self):
+ """
+ Property to access the registered replies.
+
+ Returns:
+ Dict[str, Dict[str, Type[Model]]]: Dictionary of registered replies with request
+ schema digests as keys.
+ """
return self._replies
@property
def interval_messages(self):
+ """
+ Property to access the interval message digests.
+
+ Returns:
+ Set[str]: Set of message digests associated with interval messages.
+ """
return self._interval_messages
@property
def signed_message_handlers(self):
+ """
+ Property to access the signed message handlers.
+
+ Returns:
+ Dict[str, MessageCallback]: Dictionary of signed message handlers with message schema
+ digests as keys.
+ """
return self._signed_message_handlers
@property
def unsigned_message_handlers(self):
+ """
+ Property to access the unsigned message handlers.
+
+ Returns:
+ Dict[str, MessageCallback]: Dictionary of unsigned message handlers with message schema
+ digests as keys.
+ """
return self._unsigned_message_handlers
@property
def name(self):
+ """
+ Property to access the protocol name.
+
+ Returns:
+ str: The protocol name.
+ """
return self._name
@property
def version(self):
+ """
+ Property to access the protocol version.
+
+ Returns:
+ str: The protocol version.
+ """
return self._version
@property
def canonical_name(self):
+ """
+ Property to access the canonical name of the protocol.
+
+ Returns:
+ str: The canonical name of the protocol.
+ """
return self._canonical_name
@property
def digest(self):
+ """
+ Property to access the digest of the protocol's manifest.
+
+ Returns:
+ str: The digest of the protocol's manifest.
+ """
return self.manifest()["metadata"]["digest"]
def on_interval(
@@ -76,6 +148,18 @@ def on_interval(
period: float,
messages: Optional[Union[Type[Model], Set[Type[Model]]]] = None,
):
+ """
+ Decorator to register an interval handler for the protocol.
+
+ Args:
+ period (float): The interval period in seconds.
+ messages (Optional[Union[Type[Model], Set[Type[Model]]]], optional): The associated
+ message types. Defaults to None.
+
+ Returns:
+ Callable: The decorator to register the interval handler.
+ """
+
def decorator_on_interval(func: IntervalCallback):
@functools.wraps(func)
def handler(*args, **kwargs):
@@ -93,6 +177,14 @@ def _add_interval_handler(
func: IntervalCallback,
messages: Optional[Union[Type[Model], Set[Type[Model]]]],
):
+ """
+ Add an interval handler to the protocol.
+
+ Args:
+ period (float): The interval period in seconds.
+ func (IntervalCallback): The interval handler function.
+ messages (Optional[Union[Type[Model], Set[Type[Model]]]]): The associated message types.
+ """
# store the interval handler for later
self._interval_handlers.append((func, period))
@@ -109,6 +201,17 @@ def on_query(
model: Type[Model],
replies: Optional[Union[Type[Model], Set[Type[Model]]]] = None,
):
+ """
+ Decorator to register a query handler for the protocol.
+
+ Args:
+ model (Type[Model]): The message model type.
+ replies (Optional[Union[Type[Model], Set[Type[Model]]]], optional): The associated
+ reply types. Defaults to None.
+
+ Returns:
+ Callable: The decorator to register the query handler.
+ """
return self.on_message(model, replies, allow_unverified=True)
def on_message(
@@ -117,6 +220,20 @@ def on_message(
replies: Optional[Union[Type[Model], Set[Type[Model]]]] = None,
allow_unverified: Optional[bool] = False,
):
+ """
+ Decorator to register a message handler for the protocol.
+
+ Args:
+ model (Type[Model]): The message model type.
+ replies (Optional[Union[Type[Model], Set[Type[Model]]]], optional): The associated
+ reply types. Defaults to None.
+ allow_unverified (Optional[bool], optional): Whether to allow unverified messages.
+ Defaults to False.
+
+ Returns:
+ Callable: The decorator to register the message handler.
+ """
+
def decorator_on_message(func: MessageCallback):
@functools.wraps(func)
def handler(*args, **kwargs):
@@ -135,6 +252,16 @@ def _add_message_handler(
replies: Optional[Union[Type[Model], Set[Type[Model]]]],
allow_unverified: Optional[bool] = False,
):
+ """
+ Add a message handler to the protocol.
+
+ Args:
+ model (Type[Model]): The message model type.
+ func (MessageCallback): The message handler function.
+ replies (Optional[Union[Type[Model], Set[Type[Model]]]]): The associated reply types.
+ allow_unverified (Optional[bool], optional): Whether to allow unverified messages.
+ Defaults to False.
+ """
model_digest = Model.build_schema_digest(model)
# update the model database
@@ -151,6 +278,12 @@ def _add_message_handler(
}
def manifest(self) -> Dict[str, Any]:
+ """
+ Generate the protocol's manifest.
+
+ Returns:
+ Dict[str, Any]: The protocol's manifest.
+ """
metadata = {
"name": self._name,
"version": self._version,
@@ -206,6 +339,15 @@ def manifest(self) -> Dict[str, Any]:
@staticmethod
def compute_digest(manifest: Dict[str, Any]) -> str:
+ """
+ Compute the digest of a given manifest.
+
+ Args:
+ manifest (Dict[str, Any]): The manifest to compute the digest for.
+
+ Returns:
+ str: The computed digest.
+ """
cleaned_manifest = copy.deepcopy(manifest)
if "metadata" in cleaned_manifest:
del cleaned_manifest["metadata"]
diff --git a/python/src/uagents/query.py b/python/src/uagents/query.py
index 52cd3cc3..bbacb6f6 100644
--- a/python/src/uagents/query.py
+++ b/python/src/uagents/query.py
@@ -1,3 +1,5 @@
+"""Query Envelopes."""
+
import uuid
from time import time
from typing import Optional
@@ -21,6 +23,19 @@ async def query(
resolver: Optional[Resolver] = None,
timeout: Optional[int] = 30,
) -> Optional[Envelope]:
+ """
+ Query a remote agent with a message and retrieve the response envelope.
+
+ Args:
+ destination (str): The destination address of the remote agent.
+ message (Model): The message to send.
+ resolver (Optional[Resolver], optional): The resolver to use for endpoint resolution.
+ Defaults to GlobalResolver.
+ timeout (Optional[int], optional): The timeout for the query in seconds. Defaults to 30.
+
+ Returns:
+ Optional[Envelope]: The response envelope if successful, otherwise None.
+ """
if resolver is None:
resolver = GlobalResolver()
@@ -69,6 +84,17 @@ async def query(
def enclose_response(message: Model, sender: str, session: str) -> str:
+ """
+ Enclose a response message within an envelope.
+
+ Args:
+ message (Model): The response message to enclose.
+ sender (str): The sender's address.
+ session (str): The session identifier.
+
+ Returns:
+ str: The JSON representation of the response envelope.
+ """
schema_digest = Model.build_schema_digest(message)
return enclose_response_raw(message.json(), schema_digest, sender, session)
@@ -76,6 +102,18 @@ def enclose_response(message: Model, sender: str, session: str) -> str:
def enclose_response_raw(
json_message: JsonStr, schema_digest: str, sender: str, session: str
) -> str:
+ """
+ Enclose a raw response message within an envelope.
+
+ Args:
+ json_message (JsonStr): The JSON-formatted response message to enclose.
+ schema_digest (str): The schema digest of the message.
+ sender (str): The sender's address.
+ session (str): The session identifier.
+
+ Returns:
+ str: The JSON representation of the response envelope.
+ """
response_env = Envelope(
version=1,
sender=sender,
diff --git a/python/src/uagents/resolver.py b/python/src/uagents/resolver.py
index 7190851c..ec3b2b0c 100644
--- a/python/src/uagents/resolver.py
+++ b/python/src/uagents/resolver.py
@@ -1,3 +1,5 @@
+"""Endpoint Resolver."""
+
from abc import ABC, abstractmethod
from typing import Dict, Optional
import random
@@ -6,6 +8,16 @@
def query_record(agent_address: str, service: str) -> dict:
+ """
+ Query a record from the Almanac contract.
+
+ Args:
+ agent_address (str): The address of the agent.
+ service (str): The type of service to query.
+
+ Returns:
+ dict: The query result.
+ """
contract = get_almanac_contract()
query_msg = {
"query_record": {"agent_address": agent_address, "record_type": service}
@@ -15,6 +27,15 @@ def query_record(agent_address: str, service: str) -> dict:
def get_agent_address(name: str) -> str:
+ """
+ Get the agent address associated with the provided name from the name service contract.
+
+ Args:
+ name (str): The name to query.
+
+ Returns:
+ str: The associated agent address.
+ """
query_msg = {"domain_record": {"domain": f"{name}"}}
result = get_name_service_contract().query(query_msg)
if result["record"] is not None:
@@ -26,6 +47,15 @@ def get_agent_address(name: str) -> str:
def is_agent_address(address):
+ """
+ Check if the provided address is a valid agent address.
+
+ Args:
+ address: The address to check.
+
+ Returns:
+ bool: True if the address is a valid agent address, False otherwise.
+ """
if not isinstance(address, str):
return False
@@ -37,12 +67,31 @@ def is_agent_address(address):
class Resolver(ABC):
@abstractmethod
+ # pylint: disable=unnecessary-pass
async def resolve(self, destination: str) -> Optional[str]:
+ """
+ Resolve the destination to an endpoint.
+
+ Args:
+ destination (str): The destination to resolve.
+
+ Returns:
+ Optional[str]: The resolved endpoint or None.
+ """
pass
class GlobalResolver(Resolver):
async def resolve(self, destination: str) -> Optional[str]:
+ """
+ Resolve the destination using a combination of Almanac and NameService resolvers.
+
+ Args:
+ destination (str): The destination to resolve.
+
+ Returns:
+ Optional[str]: The resolved endpoint or None.
+ """
almanac_resolver = AlmanacResolver()
name_service_resolver = NameServiceResolver()
address = (
@@ -58,6 +107,15 @@ async def resolve(self, destination: str) -> Optional[str]:
class AlmanacResolver(Resolver):
async def resolve(self, destination: str) -> Optional[str]:
+ """
+ Resolve the destination using the Almanac contract.
+
+ Args:
+ destination (str): The destination to resolve.
+
+ Returns:
+ Optional[str]: The resolved endpoint or None.
+ """
result = query_record(destination, "service")
if result is not None:
record = result.get("record") or {}
@@ -75,12 +133,36 @@ async def resolve(self, destination: str) -> Optional[str]:
class NameServiceResolver(Resolver):
async def resolve(self, destination: str) -> Optional[str]:
+ """
+ Resolve the destination using the NameService contract.
+
+ Args:
+ destination (str): The destination to resolve.
+
+ Returns:
+ Optional[str]: The resolved endpoint or None.
+ """
return get_agent_address(destination)
class RulesBasedResolver(Resolver):
def __init__(self, rules: Dict[str, str]):
+ """
+ Initialize the RulesBasedResolver with the provided rules.
+
+ Args:
+ rules (Dict[str, str]): A dictionary of rules mapping destinations to endpoints.
+ """
self._rules = rules
async def resolve(self, destination: str) -> Optional[str]:
+ """
+ Resolve the destination using the provided rules.
+
+ Args:
+ destination (str): The destination to resolve.
+
+ Returns:
+ Optional[str]: The resolved endpoint or None.
+ """
return self._rules.get(destination)
diff --git a/python/src/uagents/setup.py b/python/src/uagents/setup.py
index 607060ee..50d189e8 100644
--- a/python/src/uagents/setup.py
+++ b/python/src/uagents/setup.py
@@ -1,3 +1,5 @@
+"""Agent's Setup."""
+
import requests
from cosmpy.crypto.address import Address
@@ -9,6 +11,15 @@
def fund_agent_if_low(wallet_address: str):
+ """
+ Checks the agent's wallet balance and adds funds if it's below the registration fee.
+
+ Args:
+ wallet_address (str): The wallet address of the agent.
+
+ Returns:
+ None
+ """
ledger = get_ledger()
faucet = get_faucet()
@@ -22,6 +33,16 @@ def fund_agent_if_low(wallet_address: str):
def register_agent_with_mailbox(agent: Agent, email: str):
+ """
+ Registers the agent on a mailbox server using the provided email.
+
+ Args:
+ agent (Agent): The agent object to be registered.
+ email (str): The email address associated with the agent.
+
+ Returns:
+ None
+ """
mailbox = agent.mailbox
register_url = f"{mailbox['http_prefix']}://{mailbox['base_url']}/v1/auth/register"
resp = requests.post(
diff --git a/python/src/uagents/storage/__init__.py b/python/src/uagents/storage/__init__.py
index be9ea0a9..9747e175 100644
--- a/python/src/uagents/storage/__init__.py
+++ b/python/src/uagents/storage/__init__.py
@@ -7,7 +7,35 @@
class KeyValueStore:
+ """
+ A simple key-value store implementation for data storage.
+
+ Attributes:
+ _data (dict): The internal data storage dictionary.
+ _name (str): The name associated with the store.
+ _path (str): The file path where the store data is stored.
+
+ Methods:
+ __init__: Initialize the KeyValueStore instance.
+ get: Get the value associated with a key from the store.
+ has: Check if a key exists in the store.
+ set: Set a value associated with a key in the store.
+ remove: Remove a key and its associated value from the store.
+ clear: Clear all data from the store.
+ _load: Load data from the file into the store.
+ _save: Save the store data to the file.
+
+ """
+
def __init__(self, name: str, cwd: str = None):
+ """
+ Initialize the KeyValueStore instance.
+
+ Args:
+ name (str): The name associated with the store.
+ cwd (str, optional): The current working directory. Defaults to None.
+
+ """
self._data = {}
self._name = name or "my"
@@ -46,6 +74,13 @@ def _save(self):
def load_all_keys() -> dict:
+ """
+ Load all private keys from the private keys file.
+
+ Returns:
+ dict: A dictionary containing loaded private keys.
+
+ """
private_keys_path = os.path.join(os.getcwd(), "private_keys.json")
if os.path.exists(private_keys_path):
with open(private_keys_path, encoding="utf-8") as load_file:
@@ -54,6 +89,15 @@ def load_all_keys() -> dict:
def save_private_keys(name: str, identity_key: str, wallet_key: str):
+ """
+ Save private keys to the private keys file.
+
+ Args:
+ name (str): The name associated with the private keys.
+ identity_key (str): The identity private key.
+ wallet_key (str): The wallet private key.
+
+ """
private_keys = load_all_keys()
private_keys[name] = {"identity_key": identity_key, "wallet_key": wallet_key}
@@ -63,6 +107,16 @@ def save_private_keys(name: str, identity_key: str, wallet_key: str):
def get_or_create_private_keys(name: str) -> Tuple[str, str]:
+ """
+ Get or create private keys associated with a name.
+
+ Args:
+ name (str): The name associated with the private keys.
+
+ Returns:
+ Tuple[str, str]: A tuple containing the identity key and wallet key.
+
+ """
keys = load_all_keys()
if name in keys.keys():
private_keys = keys.get(name)