diff --git a/README.md b/README.md index 0f132760b..5632633f1 100644 --- a/README.md +++ b/README.md @@ -88,11 +88,9 @@ Please see the [official documentation](https://fetch.ai/docs) for full setup in ## 🌱 Examples -The [`examples`](https://github.com/fetchai/uAgents/tree/main/python/examples) folder contains several examples of how to create and run various types of agents. +The [`uAgent examples`](https://github.com/fetchai/uAgent-Examples) repository contains examples of uAgents, collections of uAgents that work together to solve a problem (i.e. solutions), and uAgent-based applications. -## 🌲 Integrations - -The [`integrations`](https://github.com/fetchai/uAgents/tree/main/integrations) folder contains examples that provide a more in depth use of the uAgents library. +See the [uagent-101 folder](https://github.com/fetchai/uAgent-Examples/tree/main/1-uagents/uagent-101) for a collection of educational examples to get you started. ## Python Library diff --git a/python/docs/booking-demo.md b/python/docs/booking-demo.md deleted file mode 100644 index 0164a468e..000000000 --- a/python/docs/booking-demo.md +++ /dev/null @@ -1,179 +0,0 @@ -# Restaurant Booking Demo - -To showcase how easy it is to create μAgents for a particular application, here is an example of how to create a custom message type using the `Model` class. - -```python -from uagents import Model - -class TableStatus(Model): - seats: int - time_start: int - time_end: int -``` - -For this example, we will create a restaurant booking service with two agents: a `restaurant` with tables available and a `user` requesting table availability. - -## Restaurant Setup - -We can create a restaurant agent with its corresponding http endpoint. We will also make sure that the agent is funded so it is able to register in the Almanac contract. - - -```python -from uagents import Agent -from uagents.setup import fund_agent_if_low - - -restaurant = Agent( - name="restaurant", - port=8001, - seed="restaurant secret phrase", - endpoint=["http://127.0.0.1:8001/submit"], -) - -fund_agent_if_low(restaurant.wallet.address()) -``` -The protocols `query_proto` and `book_proto` are built from message handlers in the same way as agents. See [query protocol](https://github.com/fetchai/uAgents/blob/main/python/examples/09-booking-protocol-demo/protocols/query.py) and [book protocol](https://github.com/fetchai/uAgents/blob/main/python/examples/09-booking-protocol-demo/protocols/book.py) for the details and logic behind these protocols, but for now we will simply import them. You will need to add these files inside a `protocols` folder in the same directory you are running your agent. See [agent protocols](agent-protocols.md) for more information. -Next we build the restaurant agent from these protocols and set the table availability information. - -```python -from protocols.book import book_proto -from protocols.query import query_proto, TableStatus - -# build the restaurant agent from stock protocols -restaurant.include(query_proto) -restaurant.include(book_proto) - -TABLES = { - 1: TableStatus(seats=2, time_start=16, time_end=22), - 2: TableStatus(seats=4, time_start=19, time_end=21), - 3: TableStatus(seats=4, time_start=17, time_end=19), -} - -``` - -### Storage - -We will now store the `TABLES` information in the restaurant agent and run it. - -```python -# set the table availability information in the restaurant protocols -for (number, status) in TABLES.items(): - restaurant._storage.set(number, status.dict()) - -if __name__ == "__main__": - restaurant.run() -``` -The restaurant agent is now online and listing for messages. - -## User Setup - -We will first import the needed objects and protocols. We will also need the restaurant agent's address to be able to communicate with it. - -```python -from protocols.book import BookTableRequest, BookTableResponse -from protocols.query import ( - QueryTableRequest, - QueryTableResponse, -) - -from uagents import Agent, Context -from uagents.setup import fund_agent_if_low - - -RESTAURANT_ADDRESS = "agent1qw50wcs4nd723ya9j8mwxglnhs2kzzhh0et0yl34vr75hualsyqvqdzl990" - -user = Agent( - name="user", - port=8000, - seed="user secret phrase", - endpoint=["http://127.0.0.1:8000/submit"], -) - -fund_agent_if_low(user.wallet.address()) - -``` - -Now we create the table query to generate the `QueryTableRequest` using the restaurant address. If the request has not been completed before, we send the request to the restaurant agent. - -```python -table_query = QueryTableRequest( - guests=3, - time_start=19, - duration=2, -) - -# This on_interval agent function performs a request on a defined period - -@user.on_interval(period=3.0, messages=QueryTableRequest) -async def interval(ctx: Context): - completed = ctx.storage.get("completed") - - if not completed: - await ctx.send(RESTAURANT_ADDRESS, table_query) -``` - -The function below activates when a message is received back from the restaurant agent. -`handle_query_response` will evaluate if there is a table available, and if so, respond with a `BookTableRequest` to complete the reservation. - -```python -@user.on_message(QueryTableResponse, replies={BookTableRequest}) -async def handle_query_response(ctx: Context, sender: str, msg: QueryTableResponse): - if len(msg.tables) > 0: - ctx.logger.info("There is a free table, attempting to book one now") - table_number = msg.tables[0] - request = BookTableRequest( - table_number=table_number, - time_start=table_query.time_start, - duration=table_query.duration, - ) - await ctx.send(sender, request) - else: - ctx.logger.info("No free tables - nothing more to do") - ctx.storage.set("completed", True) - -``` - -Then, `handle_book_response` will handle messages from the restaurant agent on whether the reservation was successful or unsuccessful. - -```python -@user.on_message(BookTableResponse, replies=set()) -async def handle_book_response(ctx: Context, _sender: str, msg: BookTableResponse): - if msg.success: - ctx.logger.info("Table reservation was successful") - else: - ctx.logger.info("Table reservation was UNSUCCESSFUL") - - ctx.storage.set("completed", True) - - -if __name__ == "__main__": - user.run() -``` - -Finally, run the restaurant agent and then the user agent from different terminals. - -!!! example "Run restaurant agent from one terminal" - - ``` bash - python restaurant.py - ``` - -!!! example "Run user agent from a second terminal" - - ``` bash - python user.py - ``` - -You should see this printed on the user terminal: - -
-INFO:root:Adding funds to agent...complete -INFO:root:Registering Agent user... -INFO:root:Registering Agent user...complete. -Wallet address: fetchnfu3hd87323mw484ma3v3nz2v0q6uhds7d -There is a free table, attempting to book one now -Table reservation was successful -
- -See the full example scripts at [restaurant](https://github.com/fetchai/uAgents/blob/main/python/examples/09-booking-protocol-demo/restaurant.py) and -[user](https://github.com/fetchai/uAgents/blob/main/python/examples/09-booking-protocol-demo/user.py). diff --git a/python/examples/01-first-agent/main.py b/python/examples/01-first-agent/main.py deleted file mode 100644 index ac0fae074..000000000 --- a/python/examples/01-first-agent/main.py +++ /dev/null @@ -1,12 +0,0 @@ -from uagents import Agent, Context - -agent = Agent(name="alice") - - -@agent.on_event("startup") -async def introduce_agent(ctx: Context): - ctx.logger.info(f"Hello, I'm agent {agent.name} and my address is {agent.address}.") - - -if __name__ == "__main__": - agent.run() diff --git a/python/examples/02-interval-task/main.py b/python/examples/02-interval-task/main.py deleted file mode 100644 index f539992b6..000000000 --- a/python/examples/02-interval-task/main.py +++ /dev/null @@ -1,17 +0,0 @@ -from uagents import Agent, Context - -agent = Agent(name="alice") - - -@agent.on_event("startup") -async def introduce_agent(ctx: Context): - ctx.logger.info(f"Hello, I'm agent {agent.name} and my address is {agent.address}.") - - -@agent.on_interval(period=2.0) -async def say_hello(ctx: Context): - ctx.logger.info("Hello!") - - -if __name__ == "__main__": - agent.run() diff --git a/python/examples/03-multiple-agents/main.py b/python/examples/03-multiple-agents/main.py deleted file mode 100644 index 88e316a25..000000000 --- a/python/examples/03-multiple-agents/main.py +++ /dev/null @@ -1,22 +0,0 @@ -from uagents import Agent, Bureau, Context - -alice = Agent(name="alice") -bob = Agent(name="bob") - - -@alice.on_interval(period=2.0) -async def introduce_alice(ctx: Context): - ctx.logger.info(f"Hello, I'm agent {alice.name}.") - - -@bob.on_interval(period=2.0) -async def introduce_bob(ctx: Context): - ctx.logger.info(f"Hello, I'm agent {bob.name}.") - - -bureau = Bureau() -bureau.add(alice) -bureau.add(bob) - -if __name__ == "__main__": - bureau.run() diff --git a/python/examples/04-storage/main.py b/python/examples/04-storage/main.py deleted file mode 100644 index 149cbfb4a..000000000 --- a/python/examples/04-storage/main.py +++ /dev/null @@ -1,19 +0,0 @@ -from uagents import Agent, Context - -agent = Agent(name="bob") - - -@agent.on_event("startup") -async def initialize_storage(ctx: Context): - ctx.storage.set("count", 0) - - -@agent.on_interval(period=1.0) -async def on_interval(ctx: Context): - current_count = ctx.storage.get("count") - ctx.logger.info(f"My count is: {current_count}") - ctx.storage.set("count", current_count + 1) - - -if __name__ == "__main__": - agent.run() diff --git a/python/examples/05-send-msg/main.py b/python/examples/05-send-msg/main.py deleted file mode 100644 index 8a9589312..000000000 --- a/python/examples/05-send-msg/main.py +++ /dev/null @@ -1,28 +0,0 @@ -from uagents import Agent, Bureau, Context, Model - - -class Message(Model): - text: str - - -alice = Agent(name="alice", seed="alice recovery phrase") -bob = Agent(name="bob", seed="bob recovery phrase") - - -@alice.on_interval(period=2.0) -async def send_message(ctx: Context): - msg = f"Hello there {bob.name} my name is {alice.name}." - await ctx.send(bob.address, Message(text=msg)) - - -@bob.on_message(model=Message) -async def message_handler(ctx: Context, sender: str, msg: Message): - ctx.logger.info(f"Received message from {sender}: {msg.text}") - - -bureau = Bureau() -bureau.add(alice) -bureau.add(bob) - -if __name__ == "__main__": - bureau.run() diff --git a/python/examples/06-send-tokens/main.py b/python/examples/06-send-tokens/main.py deleted file mode 100644 index 4f61b9fef..000000000 --- a/python/examples/06-send-tokens/main.py +++ /dev/null @@ -1,68 +0,0 @@ -from uagents import Agent, Bureau, Context, Model -from uagents.network import wait_for_tx_to_complete -from uagents.setup import fund_agent_if_low - - -class PaymentRequest(Model): - wallet_address: str - amount: int - denom: str - - -class TransactionInfo(Model): - tx_hash: str - - -AMOUNT = 100 -DENOM = "atestfet" - -alice = Agent(name="alice", seed="alice secret phrase") -bob = Agent(name="bob", seed="bob secret phrase") - - -fund_agent_if_low(bob.wallet.address(), min_balance=AMOUNT) - - -@alice.on_interval(period=10.0) -async def request_funds(ctx: Context): - await ctx.send( - bob.address, - PaymentRequest( - wallet_address=str(alice.wallet.address()), amount=AMOUNT, denom=DENOM - ), - ) - - -@alice.on_message(model=TransactionInfo) -async def confirm_transaction(ctx: Context, sender: str, msg: TransactionInfo): - ctx.logger.info(f"Received transaction info from {sender}: {msg}") - tx_resp = await wait_for_tx_to_complete(msg.tx_hash, ctx.ledger) - - coin_received = tx_resp.events["coin_received"] - if ( - coin_received["receiver"] == str(alice.wallet.address()) - and coin_received["amount"] == f"{AMOUNT}{DENOM}" - ): - ctx.logger.info(f"Transaction was successful: {coin_received}") - - -@bob.on_message(model=PaymentRequest, replies=TransactionInfo) -async def send_payment(ctx: Context, sender: str, msg: PaymentRequest): - ctx.logger.info(f"Received payment request from {sender}: {msg}") - - # send the payment - transaction = ctx.ledger.send_tokens( - msg.wallet_address, msg.amount, msg.denom, bob.wallet - ) - - # send the tx hash so alice can confirm - await ctx.send(alice.address, TransactionInfo(tx_hash=transaction.tx_hash)) - - -bureau = Bureau() -bureau.add(alice) -bureau.add(bob) - - -if __name__ == "__main__": - bureau.run() diff --git a/python/examples/07-msg-verification/main.py b/python/examples/07-msg-verification/main.py deleted file mode 100644 index 25e3aa8bd..000000000 --- a/python/examples/07-msg-verification/main.py +++ /dev/null @@ -1,69 +0,0 @@ -import hashlib - -from uagents import Agent, Bureau, Context, Model -from uagents.crypto import Identity - - -class Message(Model): - message: str - digest: str - signature: str - - -def encode(message: str) -> bytes: - hasher = hashlib.sha256() - hasher.update(message.encode()) - return hasher.digest() - - -alice = Agent(name="alice", seed="alice recovery password") -bob = Agent(name="bob", seed="bob recovery password") - - -@alice.on_interval(period=3.0) -async def send_message(ctx: Context): - msg = "Hello there bob." - digest = encode(msg) - await ctx.send( - bob.address, - Message(message=msg, digest=digest.hex(), signature=alice.sign_digest(digest)), - ) - - -@alice.on_message(model=Message) -async def alice_rx_message(ctx: Context, sender: str, msg: Message): - assert Identity.verify_digest( - sender, bytes.fromhex(msg.digest), msg.signature - ), "couldn't verify bob's message" - - ctx.logger.info("Bob's message verified!") - ctx.logger.info(f"Received message from {sender}: {msg.message}") - - -@bob.on_message(model=Message) -async def bob_rx_message(ctx: Context, sender: str, msg: Message): - assert Identity.verify_digest( - sender, bytes.fromhex(msg.digest), msg.signature - ), "couldn't verify alice's message" - ctx.logger.info("Alice's message verified!") - - ctx.logger.info(f"Received message from {sender}: {msg.message}") - - outbound_msg = "Hello there alice." - digest = encode(outbound_msg) - - # send the response - await ctx.send( - alice.address, - Message( - message=outbound_msg, digest=digest.hex(), signature=bob.sign_digest(digest) - ), - ) - - -bureau = Bureau() -bureau.add(alice) -bureau.add(bob) - -if __name__ == "__main__": - bureau.run() diff --git a/python/examples/08-local-network-interaction/agent1.py b/python/examples/08-local-network-interaction/agent1.py deleted file mode 100644 index 209e4cff8..000000000 --- a/python/examples/08-local-network-interaction/agent1.py +++ /dev/null @@ -1,27 +0,0 @@ -from uagents import Agent, Context, Model - -# NOTE: Run agent1.py before running agent2.py - - -class Message(Model): - message: str - - -bob = Agent( - name="bob", - port=8001, - seed="bob secret phrase", - endpoint=["http://127.0.0.1:8001/submit"], -) - - -@bob.on_message(model=Message) -async def message_handler(ctx: Context, sender: str, msg: Message): - ctx.logger.info(f"Received message from {sender}: {msg.message}") - - # send the response - await ctx.send(sender, Message(message="Hello there.")) - - -if __name__ == "__main__": - bob.run() diff --git a/python/examples/08-local-network-interaction/agent2.py b/python/examples/08-local-network-interaction/agent2.py deleted file mode 100644 index f46c4bf23..000000000 --- a/python/examples/08-local-network-interaction/agent2.py +++ /dev/null @@ -1,31 +0,0 @@ -from uagents import Agent, Context, Model - - -class Message(Model): - message: str - - -RECIPIENT_ADDRESS = ( - "test-agent://agent1q2kxet3vh0scsf0sm7y2erzz33cve6tv5uk63x64upw5g68kr0chkv7hw50" -) - -alice = Agent( - name="alice", - port=8000, - seed="alice secret phrase", - endpoint=["http://127.0.0.1:8000/submit"], -) - - -@alice.on_interval(period=2.0) -async def send_message(ctx: Context): - await ctx.send(RECIPIENT_ADDRESS, Message(message="Hello there bob.")) - - -@alice.on_message(model=Message) -async def message_handler(ctx: Context, sender: str, msg: Message): - ctx.logger.info(f"Received message from {sender}: {msg.message}") - - -if __name__ == "__main__": - alice.run() diff --git a/python/examples/08-local-network-interaction/sync_sender.py b/python/examples/08-local-network-interaction/sync_sender.py deleted file mode 100644 index b44e768db..000000000 --- a/python/examples/08-local-network-interaction/sync_sender.py +++ /dev/null @@ -1,23 +0,0 @@ -import asyncio - -from uagents import Model -from uagents.communication import send_sync_message - -RECIPIENT_ADDRESS = ( - "test-agent://agent1q2kxet3vh0scsf0sm7y2erzz33cve6tv5uk63x64upw5g68kr0chkv7hw50" -) - - -class Message(Model): - message: str - - -async def main(): - response = await send_sync_message( - RECIPIENT_ADDRESS, Message(message="Hello there bob."), response_type=Message - ) - print(response) - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/python/examples/09-booking-protocol-demo/protocols/__init__.py b/python/examples/09-booking-protocol-demo/protocols/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/python/examples/09-booking-protocol-demo/protocols/book.py b/python/examples/09-booking-protocol-demo/protocols/book.py deleted file mode 100644 index fd5b15388..000000000 --- a/python/examples/09-booking-protocol-demo/protocols/book.py +++ /dev/null @@ -1,42 +0,0 @@ -from uagents import Context, Model, Protocol - -from .query import TableStatus - - -class BookTableRequest(Model): - table_number: int - time_start: int - duration: int - - -class BookTableResponse(Model): - success: bool - - -book_proto = Protocol(name="RestaurantBookingProtocolExample", version="0.1.0") - - -@book_proto.on_message(model=BookTableRequest, replies=BookTableResponse) -async def handle_book_request(ctx: Context, sender: str, msg: BookTableRequest): - tables = { - int(num): TableStatus(**status) - for ( - num, - status, - ) in ctx.storage._data.items() # pylint: disable=protected-access - if isinstance(num, int) - } - table = tables[msg.table_number] - - if ( - table.time_start <= msg.time_start - and table.time_end >= msg.time_start + msg.duration - ): - success = True - table.time_start = msg.time_start + msg.duration - ctx.storage.set(msg.table_number, table.dict()) - else: - success = False - - # send the response - await ctx.send(sender, BookTableResponse(success=success)) diff --git a/python/examples/09-booking-protocol-demo/protocols/query.py b/python/examples/09-booking-protocol-demo/protocols/query.py deleted file mode 100644 index 8ed3be2dd..000000000 --- a/python/examples/09-booking-protocol-demo/protocols/query.py +++ /dev/null @@ -1,64 +0,0 @@ -from typing import List - -from uagents import Context, Model, Protocol - - -class TableStatus(Model): - seats: int - time_start: int - time_end: int - - -class QueryTableRequest(Model): - guests: int - time_start: int - duration: int - - -class QueryTableResponse(Model): - tables: List[int] - - -class GetTotalQueries(Model): - pass - - -class TotalQueries(Model): - total_queries: int - - -query_proto = Protocol(name="RestaurantQueryProtocolExample", version="0.1.0") - - -@query_proto.on_message(model=QueryTableRequest, replies=QueryTableResponse) -async def handle_query_request(ctx: Context, sender: str, msg: QueryTableRequest): - tables = { - int(num): TableStatus(**status) - for ( - num, - status, - ) in ctx.storage._data.items() # pylint: disable=protected-access - if isinstance(num, int) - } - - available_tables = [] - for number, status in tables.items(): - if ( - status.seats >= msg.guests - and status.time_start <= msg.time_start - and status.time_end >= msg.time_start + msg.duration - ): - available_tables.append(int(number)) - - ctx.logger.info(f"Query: {msg}. Available tables: {available_tables}.") - - await ctx.send(sender, QueryTableResponse(tables=available_tables)) - - total_queries = int(ctx.storage.get("total_queries") or 0) - ctx.storage.set("total_queries", total_queries + 1) - - -@query_proto.on_query(model=GetTotalQueries, replies=TotalQueries) -async def handle_get_total_queries(ctx: Context, sender: str, _msg: GetTotalQueries): - total_queries = int(ctx.storage.get("total_queries") or 0) - await ctx.send(sender, TotalQueries(total_queries=total_queries)) diff --git a/python/examples/09-booking-protocol-demo/query.py b/python/examples/09-booking-protocol-demo/query.py deleted file mode 100644 index 846e7993c..000000000 --- a/python/examples/09-booking-protocol-demo/query.py +++ /dev/null @@ -1,17 +0,0 @@ -import asyncio - -from protocols.query import GetTotalQueries, TotalQueries - -from uagents.query import query - -RESTAURANT_ADDRESS = "agent1qfpqn9jhvp9cg33f27q6jvmuv52dgyg9rfuu37rmxrletlqe7lewwjed5gy" - - -async def main(): - env = await query(RESTAURANT_ADDRESS, GetTotalQueries()) - msg = TotalQueries.parse_raw(env.decode_payload()) - print(f"Query response: {msg.json()}\n\n") - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/python/examples/09-booking-protocol-demo/restaurant.py b/python/examples/09-booking-protocol-demo/restaurant.py deleted file mode 100644 index 32629067e..000000000 --- a/python/examples/09-booking-protocol-demo/restaurant.py +++ /dev/null @@ -1,31 +0,0 @@ -from protocols.book import book_proto -from protocols.query import TableStatus, query_proto - -from uagents import Agent - -restaurant = Agent( - name="restaurant", - port=8001, - seed="restaurant recovery phrase", - endpoint={ - "http://127.0.0.1:8001/submit": {}, - }, -) - - -# build the restaurant agent from stock protocols and publish their details -restaurant.include(query_proto, publish_manifest=True) -restaurant.include(book_proto, publish_manifest=True) - - -TABLES = { - 1: TableStatus(seats=2, time_start=16, time_end=22), - 2: TableStatus(seats=4, time_start=19, time_end=21), - 3: TableStatus(seats=4, time_start=17, time_end=19), -} - -for number, status in TABLES.items(): - restaurant.storage.set(number, status.dict()) - -if __name__ == "__main__": - restaurant.run() diff --git a/python/examples/09-booking-protocol-demo/user.py b/python/examples/09-booking-protocol-demo/user.py deleted file mode 100644 index e91885447..000000000 --- a/python/examples/09-booking-protocol-demo/user.py +++ /dev/null @@ -1,65 +0,0 @@ -from protocols.book import BookTableRequest, BookTableResponse -from protocols.query import ( - QueryTableRequest, - QueryTableResponse, -) - -from uagents import Agent, Context - -RESTAURANT_ADDRESS = ( - "test-agent://agent1qfpqn9jhvp9cg33f27q6jvmuv52dgyg9rfuu37rmxrletlqe7lewwjed5gy" -) - -user = Agent( - name="user", - port=8000, - seed="user recovery phrase", - endpoint={ - "http://127.0.0.1:8000/submit": {}, - }, -) - - -table_query = QueryTableRequest( - guests=3, - time_start=19, - duration=2, -) - - -@user.on_interval(period=3.0, messages=QueryTableRequest) -async def interval(ctx: Context): - completed = ctx.storage.get("completed") - - if not completed: - await ctx.send(RESTAURANT_ADDRESS, table_query) - - -@user.on_message(QueryTableResponse, replies={BookTableRequest}) -async def handle_query_response(ctx: Context, sender: str, msg: QueryTableResponse): - if len(msg.tables) > 0: - ctx.logger.info("There is a free table, attempting to book one now") - table_number = msg.tables[0] - request = BookTableRequest( - table_number=table_number, - time_start=table_query.time_start, - duration=table_query.duration, - ) - await ctx.send(sender, request) - else: - ctx.logger.info("No free tables - nothing more to do") - ctx.storage.set("completed", True) - - -@user.on_message(BookTableResponse, replies=set()) -async def handle_book_response(ctx: Context, _sender: str, msg: BookTableResponse): - if msg.success: - ctx.logger.info("Table reservation was successful") - else: - ctx.logger.info("Table reservation was UNSUCCESSFUL") - - ctx.storage.set("completed", True) - - -if __name__ == "__main__": - user.run() diff --git a/python/examples/10-cleaning-demo/cleaner.py b/python/examples/10-cleaning-demo/cleaner.py deleted file mode 100644 index d9b76b03e..000000000 --- a/python/examples/10-cleaning-demo/cleaner.py +++ /dev/null @@ -1,56 +0,0 @@ -from datetime import datetime - -from protocols.cleaning import cleaning_proto -from protocols.cleaning.models import Availability, Provider, Service, ServiceType -from pytz import utc -from tortoise import Tortoise - -from uagents import Agent, Context - -cleaner = Agent( - name="cleaner", - port=8001, - seed="cleaner secret phrase", - endpoint={ - "http://127.0.0.1:8001/submit": {}, - }, -) - - -# build the cleaning service agent from the cleaning protocol -cleaner.include(cleaning_proto) - - -@cleaner.on_event("startup") -async def startup(_ctx: Context): - await Tortoise.init( - db_url="sqlite://db.sqlite3", modules={"models": ["protocols.cleaning.models"]} - ) - await Tortoise.generate_schemas() - - provider = await Provider.create(name=cleaner.name, location="London Kings Cross") - - floor = await Service.create(type=ServiceType.FLOOR) - window = await Service.create(type=ServiceType.WINDOW) - laundry = await Service.create(type=ServiceType.LAUNDRY) - - await provider.services.add(floor) - await provider.services.add(window) - await provider.services.add(laundry) - - await Availability.create( - provider=provider, - time_start=utc.localize(datetime.fromisoformat("2022-01-31 00:00:00")), - time_end=utc.localize(datetime.fromisoformat("2023-05-01 00:00:00")), - max_distance=10, - min_hourly_price=5, - ) - - -@cleaner.on_event("shutdown") -async def shutdown(_ctx: Context): - await Tortoise.close_connections() - - -if __name__ == "__main__": - cleaner.run() diff --git a/python/examples/10-cleaning-demo/protocols/__init__.py b/python/examples/10-cleaning-demo/protocols/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/python/examples/10-cleaning-demo/protocols/cleaning/__init__.py b/python/examples/10-cleaning-demo/protocols/cleaning/__init__.py deleted file mode 100644 index 32906cff2..000000000 --- a/python/examples/10-cleaning-demo/protocols/cleaning/__init__.py +++ /dev/null @@ -1,119 +0,0 @@ -from datetime import datetime, timedelta -from typing import List - -from geopy.distance import geodesic -from geopy.geocoders import Nominatim - -from uagents import Context, Model, Protocol - -from .models import Availability, Provider, User - -PROTOCOL_NAME = "cleaning" -PROTOCOL_VERSION = "0.1.0" - - -class ServiceRequest(Model): - user: str - location: str - time_start: datetime - duration: timedelta - services: List[int] - max_price: float - - -class ServiceResponse(Model): - accept: bool - price: float - - -class ServiceBooking(Model): - location: str - time_start: datetime - duration: timedelta - services: List[int] - price: float - - -class BookingResponse(Model): - success: bool - - -cleaning_proto = Protocol(name=PROTOCOL_NAME, version=PROTOCOL_VERSION) - - -def in_service_region( - location: str, availability: Availability, provider: Provider -) -> bool: - geolocator = Nominatim(user_agent="micro_agents") - - user_location = geolocator.geocode(location) - cleaner_location = geolocator.geocode(provider.location) - - if user_location is None: - raise RuntimeError(f"user location {location} not found") - - if cleaner_location is None: - raise RuntimeError(f"provider location {provider.location} not found") - - cleaner_coordinates = (cleaner_location.latitude, cleaner_location.longitude) - user_coordinates = (user_location.latitude, user_location.longitude) - - service_distance = geodesic(user_coordinates, cleaner_coordinates).miles - in_range = service_distance <= availability.max_distance - - return in_range - - -@cleaning_proto.on_message(model=ServiceRequest, replies=ServiceResponse) -async def handle_query_request(ctx: Context, sender: str, msg: ServiceRequest): - provider = await Provider.filter(name=ctx.agent.name).first() - availability = await Availability.get(provider=provider) - services = [int(service.type) for service in await provider.services] - markup = provider.markup - - user, _ = await User.get_or_create(name=msg.user, address=sender) - msg_duration_hours: float = msg.duration.total_seconds() / 3600 - ctx.logger.info(f"Received service request from user `{user.name}`") - - if ( - set(msg.services) <= set(services) - and in_service_region(msg.location, availability, provider) - and availability.time_start <= msg.time_start - and availability.time_end >= msg.time_start + msg.duration - and availability.min_hourly_price * msg_duration_hours < msg.max_price - ): - accept = True - price = markup * availability.min_hourly_price * msg_duration_hours - ctx.logger.info(f"I am available! Proposing price: {price}.") - else: - accept = False - price = 0 - ctx.logger.info("I am not available. Declining request.") - - await ctx.send(sender, ServiceResponse(accept=accept, price=price)) - - -@cleaning_proto.on_message(model=ServiceBooking, replies=BookingResponse) -async def handle_book_request(ctx: Context, sender: str, msg: ServiceBooking): - provider = await Provider.filter(name=ctx.agent.name).first() - availability = await Availability.get(provider=provider) - services = [int(service.type) for service in await provider.services] - - user = await User.get(address=sender) - msg_duration_hours: float = msg.duration.total_seconds() / 3600 - ctx.logger.info(f"Received booking request from user `{user.name}`") - - success = ( - set(msg.services) <= set(services) - and availability.time_start <= msg.time_start - and availability.time_end >= msg.time_start + msg.duration - and msg.price <= availability.min_hourly_price * msg_duration_hours - ) - - if success: - availability.time_start = msg.time_start + msg.duration - await availability.save() - ctx.logger.info("Accepted task and updated availability.") - - # send the response - await ctx.send(sender, BookingResponse(success=success)) diff --git a/python/examples/10-cleaning-demo/protocols/cleaning/models.py b/python/examples/10-cleaning-demo/protocols/cleaning/models.py deleted file mode 100644 index cac7ab457..000000000 --- a/python/examples/10-cleaning-demo/protocols/cleaning/models.py +++ /dev/null @@ -1,42 +0,0 @@ -from enum import IntEnum - -from tortoise import fields, models - - -class ServiceType(IntEnum): - FLOOR = 1 - WINDOW = 2 - LAUNDRY = 3 - IRON = 4 - BATHROOM = 5 - - -class User(models.Model): - id = fields.IntField(pk=True) - name = fields.CharField(max_length=64) - address = fields.CharField(max_length=100) - created_at = fields.DatetimeField(auto_now_add=True) - - -class Service(models.Model): - id = fields.IntField(pk=True) - type = fields.IntEnumField(ServiceType) - - -class Provider(models.Model): - id = fields.IntField(pk=True) - name = fields.CharField(max_length=64) - location = fields.CharField(max_length=64) - created_at = fields.DatetimeField(auto_now_add=True) - availability = fields.ReverseRelation["Availability"] - services = fields.ManyToManyField("models.Service") - markup = fields.FloatField(default=1.1) - - -class Availability(models.Model): - id = fields.IntField(pk=True) - provider = fields.OneToOneField("models.Provider", related_name="availability") - max_distance = fields.IntField(default=10) - time_start = fields.DatetimeField() - time_end = fields.DatetimeField() - min_hourly_price = fields.FloatField(default=0.0) diff --git a/python/examples/10-cleaning-demo/user.py b/python/examples/10-cleaning-demo/user.py deleted file mode 100644 index 265167066..000000000 --- a/python/examples/10-cleaning-demo/user.py +++ /dev/null @@ -1,79 +0,0 @@ -from datetime import datetime, timedelta - -from protocols.cleaning import ( - BookingResponse, - ServiceBooking, - ServiceRequest, - ServiceResponse, -) -from protocols.cleaning.models import ServiceType -from pytz import utc - -from uagents import Agent, Context - -CLEANER_ADDRESS = ( - "test-agent://agent1qdfdx6952trs028fxyug7elgcktam9f896ays6u9art4uaf75hwy2j9m87w" -) - -user = Agent( - name="user", - port=8000, - seed="cleaning user recovery phrase", - endpoint={ - "http://127.0.0.1:8000/submit": {}, - }, -) - - -request = ServiceRequest( - user=user.name, - location="London Kings Cross", - time_start=utc.localize(datetime.fromisoformat("2023-04-10 16:00:00")), - duration=timedelta(hours=4), - services=[ServiceType.WINDOW, ServiceType.LAUNDRY], - max_price=60, -) - -MARKDOWN = 0.8 - - -@user.on_interval(period=3.0, messages=ServiceRequest) -async def interval(ctx: Context): - ctx.storage.set("markdown", MARKDOWN) - completed = ctx.storage.get("completed") - - if not completed: - ctx.logger.info(f"Requesting cleaning service: {request}") - await ctx.send(CLEANER_ADDRESS, request) - - -@user.on_message(ServiceResponse, replies=ServiceBooking) -async def handle_query_response(ctx: Context, sender: str, msg: ServiceResponse): - markdown = ctx.storage.get("markdown") - if msg.accept: - ctx.logger.info("Cleaner is available, attempting to book now") - booking = ServiceBooking( - location=request.location, - time_start=request.time_start, - duration=request.duration, - services=request.services, - price=markdown * msg.price, - ) - await ctx.send(sender, booking) - else: - ctx.logger.info("Cleaner is not available - nothing more to do") - ctx.storage.set("completed", True) - - -@user.on_message(BookingResponse, replies=set()) -async def handle_book_response(ctx: Context, _sender: str, msg: BookingResponse): - if msg.success: - ctx.logger.info("Booking was successful") - else: - ctx.logger.info("Booking was UNSUCCESSFUL") - - ctx.storage.set("completed", True) - - -if __name__ == "__main__": - user.run() diff --git a/python/examples/11-mailbox-agents/alice.py b/python/examples/11-mailbox-agents/alice.py deleted file mode 100644 index 60c2fc461..000000000 --- a/python/examples/11-mailbox-agents/alice.py +++ /dev/null @@ -1,36 +0,0 @@ -from uagents import Agent, Context, Model - - -class Message(Model): - message: str - - -# First generate a secure seed phrase (e.g. https://pypi.org/project/mnemonic/) -SEED_PHRASE = "put_your_seed_phrase_here" - -# Copy the address shown below -print(f"Your agent's address is: {Agent(seed=SEED_PHRASE).address}") - -# Then go to https://agentverse.ai, register your agent in the Mailroom -# and copy the agent's mailbox key -AGENT_MAILBOX_KEY = "put_your_AGENT_MAILBOX_KEY_here" - -# Now your agent is ready to join the agentverse! -agent = Agent( - name="alice", - seed=SEED_PHRASE, - mailbox=f"{AGENT_MAILBOX_KEY}@https://agentverse.ai", -) - - -@agent.on_message(model=Message, replies={Message}) -async def handle_message(ctx: Context, sender: str, msg: Message): - ctx.logger.info(f"Received message from {sender}: {msg.message}") - - # send the response - ctx.logger.info("Sending message to bob") - await ctx.send(sender, Message(message="hello there bob")) - - -if __name__ == "__main__": - agent.run() diff --git a/python/examples/11-mailbox-agents/bob.py b/python/examples/11-mailbox-agents/bob.py deleted file mode 100644 index 72a50930a..000000000 --- a/python/examples/11-mailbox-agents/bob.py +++ /dev/null @@ -1,41 +0,0 @@ -from uagents import Agent, Context, Model - - -class Message(Model): - message: str - - -# Copy ALICE_ADDRESS generated in alice.py -ALICE_ADDRESS = "paste_alice_address_here" - -# Generate a second seed phrase (e.g. https://pypi.org/project/mnemonic/) -SEED_PHRASE = "put_your_seed_phrase_here" - -# Copy the address shown below -print(f"Your agent's address is: {Agent(seed=SEED_PHRASE).address}") - -# Then go to https://agentverse.ai, register your agent in the Mailroom -# and copy the agent's mailbox key -AGENT_MAILBOX_KEY = "put_your_AGENT_MAILBOX_KEY_here" - -# Now your agent is ready to join the agentverse! -agent = Agent( - name="bob", - seed=SEED_PHRASE, - mailbox=f"{AGENT_MAILBOX_KEY}@https://agentverse.ai", -) - - -@agent.on_interval(period=2.0) -async def send_message(ctx: Context): - ctx.logger.info("Sending message to alice") - await ctx.send(ALICE_ADDRESS, Message(message="hello there alice")) - - -@agent.on_message(model=Message, replies=set()) -async def on_message(ctx: Context, sender: str, msg: Message): - ctx.logger.info(f"Received message from {sender}: {msg.message}") - - -if __name__ == "__main__": - agent.run() diff --git a/python/examples/12-remote-agents/agent1.py b/python/examples/12-remote-agents/agent1.py deleted file mode 100644 index 0fc3e4e02..000000000 --- a/python/examples/12-remote-agents/agent1.py +++ /dev/null @@ -1,43 +0,0 @@ -""" -Remote connection example which uses ngrok to expose the agent as an -alternative to the Fetch.ai Agentverse. (https://agentverse.ai/) - -Preparation: - - pip install pyngrok - - during the first run, the script will download the ngrok application - (no installation or registration required) -""" - -from pyngrok import conf, ngrok - -from uagents import Agent, Context, Model - - -class Message(Model): - message: str - - -PORT = 8000 - -conf.get_default().monitor_thread = False # reduce console output -http_tunnel = ngrok.connect(addr=PORT, proto="http", name="remote_agent") - -alice = Agent( - name="Alice", - seed="agent1 secret phrase", - port=PORT, - endpoint=[f"{http_tunnel.public_url}/submit"], -) - - -@alice.on_message(model=Message) -async def act_on_message(ctx: Context, sender: str, msg: Message): - """On message handler""" - ctx.logger.info(f"Received message from {sender[-8:]}: {msg.message}") - - -print(f"Agent address: {alice.address}") -print(f"Agent public URL: {http_tunnel.public_url}/submit") - -if __name__ == "__main__": - alice.run() diff --git a/python/examples/12-remote-agents/agent2.py b/python/examples/12-remote-agents/agent2.py deleted file mode 100644 index b037e7614..000000000 --- a/python/examples/12-remote-agents/agent2.py +++ /dev/null @@ -1,30 +0,0 @@ -""" -Counterpart for the remote connection example in agent1.py. -""" - -from uagents import Agent, Context, Model - - -class Message(Model): - message: str - - -bob = Agent( - name="bob", - port=8001, - seed="agent2 secret seed phrase", - endpoint=["http://127.0.0.1:8001/submit"], -) - -ALICE_ADDRESS = "agent1qv2l7qzcd2g2rcv2p93tqflrcaq5dk7c2xc7fcnfq3s37zgkhxjmq5mfyvz" - - -@bob.on_interval(period=5.0) -async def send_to_alice(ctx: Context): - """Send a message to alice every 5 seconds.""" - ctx.logger.info("Sending message to alice") - await ctx.send(ALICE_ADDRESS, Message(message="hello there alice")) - - -if __name__ == "__main__": - bob.run() diff --git a/python/examples/13-agent-name-service/agent1.py b/python/examples/13-agent-name-service/agent1.py deleted file mode 100644 index 06604f51f..000000000 --- a/python/examples/13-agent-name-service/agent1.py +++ /dev/null @@ -1,44 +0,0 @@ -from cosmpy.aerial.wallet import LocalWallet - -from uagents import Agent, Context, Model -from uagents.network import get_faucet, get_name_service_contract, logger - -# NOTE: Run agent1.py before running agent2.py - - -class Message(Model): - message: str - - -bob = Agent( - name="bob-0", - seed="agent bob-0 secret phrase", - port=8001, - endpoint=["http://localhost:8001/submit"], -) - - -my_wallet = LocalWallet.from_unsafe_seed("registration test wallet") -name_service_contract = get_name_service_contract(test=True) -faucet = get_faucet() -DOMAIN = "example.agent" - -logger.info(f"Adding testnet funds to {my_wallet.address()}...") -faucet.get_wealth(my_wallet.address()) -logger.info(f"Adding testnet funds to {my_wallet.address()}...complete") - - -@bob.on_interval(10) -async def register_agent_name(ctx: Context): - await name_service_contract.register( - bob.ledger, my_wallet, bob.address, bob.name, DOMAIN - ) - - -@bob.on_message(model=Message) -async def message_handler(ctx: Context, sender: str, msg: Message): - ctx.logger.info(f"Received message from {sender}: {msg.message}") - - -if __name__ == "__main__": - bob.run() diff --git a/python/examples/13-agent-name-service/agent2.py b/python/examples/13-agent-name-service/agent2.py deleted file mode 100644 index 8adc65077..000000000 --- a/python/examples/13-agent-name-service/agent2.py +++ /dev/null @@ -1,26 +0,0 @@ -from uagents import Agent, Context, Model - - -class Message(Model): - message: str - - -alice = Agent( - name="alice-0", - seed="agent alice-0 secret phrase", - port=8000, - endpoint=["http://localhost:8000/submit"], -) - -DOMAIN = "example.agent" - - -@alice.on_interval(period=5) -async def alice_interval_handler(ctx: Context): - bob_name = "bob-0" + "." + DOMAIN - ctx.logger.info(f"Sending message to {bob_name}...") - await ctx.send(bob_name, Message(message="Hello there bob.")) - - -if __name__ == "__main__": - alice.run() diff --git a/python/examples/14-broadcast/main.py b/python/examples/14-broadcast/main.py deleted file mode 100644 index e25bb284b..000000000 --- a/python/examples/14-broadcast/main.py +++ /dev/null @@ -1,56 +0,0 @@ -from uagents import Agent, Bureau, Context, Model, Protocol - -# create agents -# alice and bob will support the protocol -# charles will try to reach all agents supporting the protocol -alice = Agent(name="alice", seed="alice recovery phrase") -bob = Agent(name="bob", seed="bob recovery phrase") -charles = Agent(name="charles", seed="charles recovery phrase") - - -class BroadcastExampleRequest(Model): - pass - - -class BroadcastExampleResponse(Model): - text: str - - -# define protocol -proto = Protocol(name="proto", version="1.0") - - -@proto.on_message(model=BroadcastExampleRequest, replies=BroadcastExampleResponse) -async def handle_request(ctx: Context, sender: str, _msg: BroadcastExampleRequest): - await ctx.send( - sender, BroadcastExampleResponse(text=f"Hello from {ctx.agent.name}") - ) - - -# include protocol -# Note: after the first registration on the almanac smart contract, it will -# take about 5 minutes before the agents can be found through the protocol -alice.include(proto) -bob.include(proto) - - -# let charles send the message to all agents supporting the protocol -@charles.on_interval(period=5) -async def say_hello(ctx: Context): - status_list = await ctx.broadcast(proto.digest, message=BroadcastExampleRequest()) - ctx.logger.info(f"Trying to contact {len(status_list)} agents.") - - -@charles.on_message(model=BroadcastExampleResponse) -async def handle_response(ctx: Context, sender: str, msg: BroadcastExampleResponse): - ctx.logger.info(f"Received response from {sender}: {msg.text}") - - -bureau = Bureau(port=8000, endpoint="http://localhost:8000/submit") -bureau.add(alice) -bureau.add(bob) -bureau.add(charles) - - -if __name__ == "__main__": - bureau.run() diff --git a/python/examples/15-wallet-messaging/main.py b/python/examples/15-wallet-messaging/main.py deleted file mode 100644 index 7fb1c6a55..000000000 --- a/python/examples/15-wallet-messaging/main.py +++ /dev/null @@ -1,31 +0,0 @@ -from uagents import Agent, Bureau, Context -from uagents.wallet_messaging import WalletMessage - -ALICE_SEED = "alice dorado recovery phrase" -BOB_SEED = "bob dorado recovery phrase" - -alice = Agent(name="alice", seed=ALICE_SEED, enable_wallet_messaging=True) -bob = Agent(name="bob", seed=BOB_SEED, enable_wallet_messaging=True) - - -@alice.on_wallet_message() -async def reply(ctx: Context, msg: WalletMessage): - ctx.logger.info(f"Got wallet message: {msg.text}") - await ctx.send_wallet_message(msg.sender, "hey, thanks for the message") - - -@bob.on_interval(period=5) -async def send_message(ctx: Context): - ctx.logger.info("Sending message...") - await ctx.send_wallet_message(alice.address, "hello") - - -@bob.on_wallet_message() -async def wallet_reply(ctx: Context, msg: WalletMessage): - ctx.logger.info(f"Got wallet message: {msg.text}") - - -bureau = Bureau() -bureau.add(alice) -bureau.add(bob) -bureau.run() diff --git a/python/examples/16-on-query-proxy/README.md b/python/examples/16-on-query-proxy/README.md deleted file mode 100644 index c08decda8..000000000 --- a/python/examples/16-on-query-proxy/README.md +++ /dev/null @@ -1,18 +0,0 @@ -## Example of how to query an agent using a proxy API - -In separate terminals: - -1. Run the FastAPI proxy: -```bash -uvicorn proxy:app -``` - -2. Run the agent: -```bash -python agent.py -``` - -3. Query the agent via the proxy: -```bash -curl -d '{"message": "test"}' -H "Content-Type: application/json" -X POST http://localhost:8000/endpoint -``` diff --git a/python/examples/16-on-query-proxy/agent.py b/python/examples/16-on-query-proxy/agent.py deleted file mode 100644 index d4942e570..000000000 --- a/python/examples/16-on-query-proxy/agent.py +++ /dev/null @@ -1,38 +0,0 @@ -from uagents import Agent, Context, Model - - -class TestRequest(Model): - message: str - - -class Response(Model): - text: str - - -agent = Agent( - name="your_agent_name_here", - seed="your_agent_seed_here", - port=8001, - endpoint="http://localhost:8001/submit", -) - - -@agent.on_event("startup") -async def startup(ctx: Context): - ctx.logger.info(f"Starting up {agent.name}") - ctx.logger.info(f"With address: {agent.address}") - ctx.logger.info(f"And wallet address: {agent.wallet.address()}") - - -@agent.on_query(model=TestRequest, replies={Response}) -async def query_handler(ctx: Context, sender: str, _query: TestRequest): - ctx.logger.info("Query received") - try: - # do something here - await ctx.send(sender, Response(text="success")) - except Exception: - await ctx.send(sender, Response(text="fail")) - - -if __name__ == "__main__": - agent.run() diff --git a/python/examples/16-on-query-proxy/proxy.py b/python/examples/16-on-query-proxy/proxy.py deleted file mode 100644 index 488f86e7b..000000000 --- a/python/examples/16-on-query-proxy/proxy.py +++ /dev/null @@ -1,39 +0,0 @@ -import json - -from fastapi import FastAPI, Request - -from uagents import Model -from uagents.envelope import Envelope -from uagents.query import query - -AGENT_ADDRESS = "address_of_your_agent_to_be_queried_here" - - -class TestRequest(Model): - message: str - - -async def agent_query(req): - response = await query(destination=AGENT_ADDRESS, message=req, timeout=15) - if isinstance(response, Envelope): - data = json.loads(response.decode_payload()) - return data["text"] - return response - - -app = FastAPI() - - -@app.get("/") -def read_root(): - return "Hello from the Agent controller" - - -@app.post("/endpoint") -async def make_agent_call(req: Request): - model = TestRequest.parse_obj(await req.json()) - try: - res = await agent_query(model) - return f"successful call - agent response: {res}" - except Exception: - return "unsuccessful agent call" diff --git a/python/examples/17-stateful-communication/Readme.md b/python/examples/17-stateful-communication/Readme.md deleted file mode 100644 index 88d3cda2d..000000000 --- a/python/examples/17-stateful-communication/Readme.md +++ /dev/null @@ -1,32 +0,0 @@ -### How to use Dialogues - -#### Define a pattern (generic dialogue without models) - -1. Define the intended behaviour / communication flow as a directed graph. -2. Create a new class inheriting from `Dialogue`. -3. Specify nodes and edges according to the graph. -4. Specify a concrete transition decorator for each transition (=edge). - -See `chitchat.py` for an example. - -**Note:** The default state represents the state that the agent is in when a dialogue with another agent hasn't been started yet. This state is not shared but exists for each potential dialogue instance with other agents. The default state is included for completeness of the Dialogue graph and has no functional role. - -#### Implement a pattern (attach models to transitions) - -1. Instantiate a dialogue (e.g., `ChitChatDialogue`) with your agents address. -2. Define message models for each transition defined in the dialogue, according to the needs of your use case. -3. Implement all state transition decorators defined in the dialogue, similarly to the `on_message` handler and register the corresponding models with the respective transitions. - -See `agent1.py` and `agent2.py` for an example implementation of the `ChitChatDialogue` defined in `chitchat.py`. - -To use the example start `agent1` first, then `agent2` which will initiate the dialogue after 5 seconds. To exit the dialogue press `ctrl + d` on your keyboard. - -### Examples - -#### Open Dialogue (Agent 1 & 2) - -Agent 1 and 2 make use of an implementation of the ChitChat Dialogue in which each step of the graph is exposed to the user. Both agents will be able to communicate as long as both define the same Models for the predefined graph (ChitChatDialogue class). - -#### Predefined Dialogue (Agent 3 & 4) - -Agent 3 and 4 are given a graph of the same ChitChat Dialogue but most of the interactions are automated and not exposed to the user. Only one interaction to start the dialogue and the cyclic ChitChat message is required to use the graph. diff --git a/python/examples/17-stateful-communication/agent1.py b/python/examples/17-stateful-communication/agent1.py deleted file mode 100644 index 0f6c3dcbe..000000000 --- a/python/examples/17-stateful-communication/agent1.py +++ /dev/null @@ -1,125 +0,0 @@ -"""Chit chat dialogue example""" - -import json - -from dialogues.chitchat import ChitChatDialogue - -from uagents import Agent, Context, Model - -CHAT_AGENT_ADDRESS = "agent1qgp7urkvx24a2gs8e7496fajzy78h4887vz7va4h7klzf7azzhthsz7zymu" - -agent = Agent( - name="chit_agent", - seed="9876543210000000000", - port=8001, - endpoint="http://127.0.0.1:8001/submit", - log_level="DEBUG", -) - - -# define dialogue messages; each transition needs a separate message -class InitiateChitChatDialogue(Model): - pass - - -class AcceptChitChatDialogue(Model): - pass - - -class ChitChatDialogueMessage(Model): - text: str - - -class ConcludeChitChatDialogue(Model): - pass - - -class RejectChitChatDialogue(Model): - pass - - -# instantiate the dialogues -chitchat_dialogue = ChitChatDialogue( - version="0.1", - storage=agent.storage, -) - -# get an overview of the dialogue structure -print("Dialogue overview:") -print(json.dumps(chitchat_dialogue.get_overview(), indent=4)) -print("---") - - -@chitchat_dialogue.on_initiate_session(InitiateChitChatDialogue) -async def start_chitchat( - ctx: Context, - sender: str, - _msg: InitiateChitChatDialogue, -): - ctx.logger.info(f"Received init message from {sender}") - # do something when the dialogue is initiated - await ctx.send(sender, AcceptChitChatDialogue()) - - -@chitchat_dialogue.on_start_dialogue(AcceptChitChatDialogue) -async def accept_chitchat( - ctx: Context, - sender: str, - _msg: AcceptChitChatDialogue, -): - ctx.logger.info( - f"session with {sender} was accepted. I'll say 'Hello!' to start the ChitChat" - ) - # do something after the dialogue is started; e.g. send a message - await ctx.send(sender, ChitChatDialogueMessage(text="Hello!")) - - -@chitchat_dialogue.on_reject_session(RejectChitChatDialogue) -async def reject_chitchat( - ctx: Context, - sender: str, - _msg: RejectChitChatDialogue, -): - # do something when the dialogue is rejected and nothing has been sent yet - ctx.logger.info(f"Received reject message from: {sender}") - - -@chitchat_dialogue.on_continue_dialogue(ChitChatDialogueMessage) -async def continue_chitchat( - ctx: Context, - sender: str, - msg: ChitChatDialogueMessage, -): - # do something when the dialogue continues - ctx.logger.info(f"Received message: {msg.text}") - try: - my_msg = input("Please enter your message:\n> ") - await ctx.send(sender, ChitChatDialogueMessage(text=my_msg)) - except EOFError: - await ctx.send(sender, ConcludeChitChatDialogue()) - - -@chitchat_dialogue.on_end_session(ConcludeChitChatDialogue) -async def conclude_chitchat( - ctx: Context, - sender: str, - _msg: ConcludeChitChatDialogue, -): - # do something when the dialogue is concluded after messages have been exchanged - ctx.logger.info( - f"Received conclude message from: {sender};\n" - "accessing history showing only ChitChat messages:" - ) - ctx.logger.info( - chitchat_dialogue.get_conversation( - ctx.session, ChitChatDialogueMessage.__name__ - ) - ) - - -agent.include(chitchat_dialogue) - - -if __name__ == "__main__": - print(f"Agent address: {agent.address}") - agent.run() diff --git a/python/examples/17-stateful-communication/agent2.py b/python/examples/17-stateful-communication/agent2.py deleted file mode 100644 index f4af3b19e..000000000 --- a/python/examples/17-stateful-communication/agent2.py +++ /dev/null @@ -1,124 +0,0 @@ -"""Chit chat dialogue example""" - -from asyncio import sleep - -from dialogues.chitchat import ChitChatDialogue - -from uagents import Agent, Context, Model - -CHIT_AGENT_ADDRESS = "agent1qvhlqy2a4lk9gge8ug7l65a6k07wc92hh2d5jhwtat0zakrtg08njmfn00j" - -agent = Agent( - name="chat_agent", - seed="9876543210000000001", - port=8002, - endpoint="http://127.0.0.1:8002/submit", - log_level="DEBUG", -) - - -# define dialogue messages; each transition needs a separate message -class InitiateChitChatDialogue(Model): - pass - - -class AcceptChitChatDialogue(Model): - pass - - -class ChitChatDialogueMessage(Model): - text: str - - -class ConcludeChitChatDialogue(Model): - pass - - -class RejectChitChatDialogue(Model): - pass - - -# instantiate the dialogues -chitchat_dialogue = ChitChatDialogue( - version="0.1", - storage=agent.storage, -) - - -@chitchat_dialogue.on_initiate_session(InitiateChitChatDialogue) -async def start_chitchat( - ctx: Context, - sender: str, - _msg: InitiateChitChatDialogue, -): - ctx.logger.info(f"Received init message from {sender}") - # do something when the dialogue is initiated - await ctx.send(sender, AcceptChitChatDialogue()) - - -@chitchat_dialogue.on_start_dialogue(AcceptChitChatDialogue) -async def accept_chitchat( - ctx: Context, - sender: str, - _msg: AcceptChitChatDialogue, -): - ctx.logger.info( - f"session with {sender} was accepted. I'll say 'Hello!' to start the ChitChat" - ) - # do something after the dialogue is started; e.g. send a message - await ctx.send(sender, ChitChatDialogueMessage(text="Hello!")) - - -@chitchat_dialogue.on_reject_session(RejectChitChatDialogue) -async def reject_chitchat( - ctx: Context, - sender: str, - _msg: RejectChitChatDialogue, -): - # do something when the dialogue is rejected and nothing has been sent yet - ctx.logger.info(f"Received reject message from: {sender}") - - -@chitchat_dialogue.on_continue_dialogue(ChitChatDialogueMessage) -async def continue_chitchat( - ctx: Context, - sender: str, - msg: ChitChatDialogueMessage, -): - # do something when the dialogue continues - ctx.logger.info(f"Received message: {msg.text}") - try: - my_msg = input("Please enter your message:\n> ") - await ctx.send(sender, ChitChatDialogueMessage(text=my_msg)) - except EOFError: - await ctx.send(sender, ConcludeChitChatDialogue()) - - -@chitchat_dialogue.on_end_session(ConcludeChitChatDialogue) -async def conclude_chitchat( - ctx: Context, - sender: str, - _msg: ConcludeChitChatDialogue, -): - # do something when the dialogue is concluded after messages have been exchanged - ctx.logger.info(f"Received conclude message from: {sender}; accessing history:") - ctx.logger.info(chitchat_dialogue.get_conversation(ctx.session)) - - -agent.include(chitchat_dialogue) - - -# initiate dialogue -@agent.on_event("startup") -async def start_cycle(ctx: Context): - await sleep(5) - # use 'chitchat_dialogue.set_custom_session_id()' to set a custom session id (UUID4) - # await ctx.send(CHIT_AGENT_ADDRESS, InitiateChitChatDialogue()) - await chitchat_dialogue.start_dialogue( - ctx, CHIT_AGENT_ADDRESS, InitiateChitChatDialogue() - ) - - -if __name__ == "__main__": - print(f"Agent address: {agent.address}") - agent.run() diff --git a/python/examples/17-stateful-communication/agent3.py b/python/examples/17-stateful-communication/agent3.py deleted file mode 100644 index f90489eea..000000000 --- a/python/examples/17-stateful-communication/agent3.py +++ /dev/null @@ -1,58 +0,0 @@ -"""Chit chat dialogue example""" - -import json - -from dialogues.hardcoded_chitchat import ( - ChitChatDialogue, - ChitChatDialogueMessage, - ConcludeChitChatDialogue, -) - -from uagents import Agent, Context - -CHAT_AGENT_ADDRESS = "agent1qwvecfwc255pfqqwtjznh9qqk6skl77xc6fzw8mr3ppfex32sr0kcad62n4" - -agent = Agent( - name="chit_agent", - seed="9876543210000000003", - port=8001, - endpoint="http://127.0.0.1:8001/submit", -) - - -# instantiate the dialogues -chitchat_dialogue = ChitChatDialogue( - version="0.1", - storage=agent.storage, -) - -# get an overview of the dialogue structure -print("Dialogue overview:") -print(json.dumps(chitchat_dialogue.get_overview(), indent=4)) -print("---") - - -# This is the only decorator that is needed to add to your agent with the -# hardcoded dialogue example. If you omit this decorator, the dialogue will -# emit a warning. -@chitchat_dialogue.on_continue_dialogue() -async def continue_chitchat( - ctx: Context, - sender: str, - msg: ChitChatDialogueMessage, -): - # do something when the dialogue continues - ctx.logger.info(f"Received message: {msg.text}") - try: - my_msg = input("Please enter your message:\n> ") - await ctx.send(sender, ChitChatDialogueMessage(text=my_msg)) - except EOFError: - await ctx.send(sender, ConcludeChitChatDialogue()) - - -agent.include(chitchat_dialogue) - - -if __name__ == "__main__": - print(f"Agent address: {agent.address}") - agent.run() diff --git a/python/examples/17-stateful-communication/agent4.py b/python/examples/17-stateful-communication/agent4.py deleted file mode 100644 index e3c0d68aa..000000000 --- a/python/examples/17-stateful-communication/agent4.py +++ /dev/null @@ -1,53 +0,0 @@ -"""Chit chat dialogue example""" - -from asyncio import sleep - -from dialogues.hardcoded_chitchat import ( - ChitChatDialogue, - ChitChatDialogueMessage, - InitiateChitChatDialogue, -) - -from uagents import Agent, Context - -CHIT_AGENT_ADDRESS = "agent1qfjvt60h0kh573fzy9mvmlsr50vff8xmdfeclfgy3g9g6qq6jxkuxh4cu3w" - -agent = Agent( - name="chat_agent", - seed="9876543210000000004", - port=8002, - endpoint="http://127.0.0.1:8002/submit", -) - - -# instantiate the dialogues -chitchat_dialogue = ChitChatDialogue( - version="0.1", - storage=agent.storage, -) - - -@chitchat_dialogue.on_continue_dialogue() -async def continue_chitchat( - ctx: Context, - sender: str, - msg: ChitChatDialogueMessage, -): - ctx.logger.info(f"Returning: {msg.text}") - await ctx.send(sender, ChitChatDialogueMessage(text=msg.text)) - - -# initiate dialogue after 5 seconds -@agent.on_event("startup") -async def start_cycle(ctx: Context): - await sleep(5) - await chitchat_dialogue.start_dialogue( - ctx, CHIT_AGENT_ADDRESS, InitiateChitChatDialogue() - ) - - -agent.include(chitchat_dialogue) - -if __name__ == "__main__": - print(f"Agent address: {agent.address}") - agent.run() diff --git a/python/examples/17-stateful-communication/agent5.py b/python/examples/17-stateful-communication/agent5.py deleted file mode 100644 index d780fc929..000000000 --- a/python/examples/17-stateful-communication/agent5.py +++ /dev/null @@ -1,97 +0,0 @@ -""" -Specific agent example for the Agentverse chit-chat dialogue. - -See python/examples/17-stateful-communication/dialogues/agentverse_chitchat.py -for the dialogue implementation. -""" - -from dialogues.agentverse_chitchat import ( - AgentverseChitChat, - ChitChatDialogueMessage, - ConcludeChitChatDialogue, - InitiateChitChatDialogue, -) - -from uagents import Agent, Context - -HOSTED_AGENT_ADDRESS = "" - -agent = Agent( - name="Agentverse ChitChat Agent", - seed="", - port=8080, - endpoint="http://127.0.0.1:8080/submit", -) - - -chitchat = AgentverseChitChat( - storage=agent.storage, -) - - -@chitchat.on_start_dialogue(InitiateChitChatDialogue) -async def start_dialogue( - ctx: Context, - sender: str, - msg: InitiateChitChatDialogue, -): - """ - Start the dialogue with a message - - This function is called when the dialogue is initiated by another agent. - """ - ctx.logger.info(f"Received init message from {sender}") - await ctx.send(sender, ChitChatDialogueMessage(text="Hello!")) - - -@chitchat.on_continue_dialogue(ChitChatDialogueMessage) -async def continue_dialogue( - ctx: Context, - sender: str, - msg: ChitChatDialogueMessage, -): - """ - Continue the dialogue with a message - - Do something with the received message and send a response. - """ - ctx.logger.info(f"Received message: {msg.text}") - my_msg = input("Please enter your message:\n> ") - - if my_msg == "exit": - await ctx.send(sender, ConcludeChitChatDialogue()) - return - - await ctx.send(sender, ChitChatDialogueMessage(text=my_msg)) - - -@chitchat.on_end_session(ConcludeChitChatDialogue) -async def conclude_dialogue( - ctx: Context, - sender: str, - msg: ConcludeChitChatDialogue, -): - """ - Conclude the dialogue - - Do something when the dialogue is concluded. - """ - ctx.logger.info(f"Received conclude message from {sender} ({msg})") - - -agent.include(chitchat) - - -@agent.on_event("startup") -async def handle_startup(ctx: Context): - """ - Start the dialogue with the hosted agent - - Please note the `Dialogue.start_dialogue` method is used to start the dialogue. - """ - ctx.logger.info("Agent started with address: " + agent.address) - await chitchat.start_dialogue(ctx, HOSTED_AGENT_ADDRESS, InitiateChitChatDialogue()) - - -if __name__ == "__main__": - agent.run() diff --git a/python/examples/17-stateful-communication/dialogues/agentverse_chitchat.py b/python/examples/17-stateful-communication/dialogues/agentverse_chitchat.py deleted file mode 100644 index d5902dd31..000000000 --- a/python/examples/17-stateful-communication/dialogues/agentverse_chitchat.py +++ /dev/null @@ -1,134 +0,0 @@ -""" -Specific dialogue class for the Agentverse chit-chat dialogue. - -See python/examples/17-stateful-communication/agent5.py for a usage example. -""" - -from typing import Type - -from uagents import Model -from uagents.experimental.dialogues import Dialogue, Edge, Node - - -# This is the definition of the dialogue messages that can be exchanged -# each transition needs a separate message and since digests are calculated -# based on the message structure, the message classes should not be changed. -class InitiateChitChatDialogue(Model): - pass - - -class ChitChatDialogueMessage(Model): - text: str - - -class ConcludeChitChatDialogue(Model): - pass - - -class AgentverseChitChat(Dialogue): - """ - NOTE: Do not change this class as it is used in Agentverse and to be compatible - both parties need to use the same dialogue implementation. - - The Agentverse compatible ChitChatDialogue is the first version of the generic - Dialogue implementation that can be used by any agent to exchange messages - with a hosted agent in Agentverse. - - It provides a simple pattern that allows 2 parties to exchange an arbitrary - number of "ChitChat" messages. - Messages are started with an initial message, followed by any number of - "ChitChat" messages, and finally concluded with an end message. - """ - - # Node definition for the dialogue states - chatting_state_node = Node( - name="Chit Chatting", - description="While in this state, more messages can be exchanged.", - ) - end_state_node = Node( - name="Concluded", - description="This is the state after the dialogue has been concluded and " - "no more messages will be accepted.", - ) - - # Edge definition for the dialogue transitions - start_dialogue_edge = Edge( - name="Start Dialogue", - description=( - "A message that initiates a ChitChat conversation and provides " - "any information needed to set the context and let the receiver " - "decide whether to accept or directly end this conversation." - ), - parent=None, - child=chatting_state_node, - ) - cont_dialogue_edge = Edge( - name="Continue Dialogue", - description=( - "A general message structure to exchange information without " - "annotating further states or limiting the message flow in any way." - ), - parent=chatting_state_node, - child=chatting_state_node, - ) - end_dialogue_edge = Edge( - name="End Dialogue", - description=( - "A final message that can be sent at any time by either party " - "to finish this dialogue." - ), - parent=chatting_state_node, - child=end_state_node, - ) - - def __init__( - self, - storage=None, - ) -> None: - """ - Initialize the simple ChitChatDialogue class. - - Args: - storage (Optional[StorageAPI], optional): Storage to use. - None will generate a new KeyValueStore based on the dialogue name. - Defaults to None. - """ - super().__init__( - name="ChitChatDialogue_simple", - version="0.1.0", - storage=storage, - nodes=[ - self.chatting_state_node, - self.end_state_node, - ], - edges=[ - self.start_dialogue_edge, - self.cont_dialogue_edge, - self.end_dialogue_edge, - ], - ) - - def on_start_dialogue(self, model: Type[Model]): - """ - This handler is triggered when the initial message of the - dialogue is received. - It automatically transitions into the chit-chatting state for - the next message exchange or directly into the end state. - """ - return super()._on_state_transition(self.start_dialogue_edge.name, model) - - def on_continue_dialogue(self, model: Type[Model]): - """ - This handler is triggered for every incoming "chitchat" message - once the session has been accepted. - Any additional stateful information within a dialogue needs to be - persisted explicitly to access it at a later point in the dialogue. - """ - return super()._on_state_transition(self.cont_dialogue_edge.name, model) - - def on_end_session(self, model: Type[Model]): - """ - This handler is triggered once the other party has ended the dialogue. - Any final conclusion or cleanup goes here. - """ - return super()._on_state_transition(self.end_dialogue_edge.name, model) diff --git a/python/examples/17-stateful-communication/dialogues/chitchat.py b/python/examples/17-stateful-communication/dialogues/chitchat.py deleted file mode 100644 index 8b0de063e..000000000 --- a/python/examples/17-stateful-communication/dialogues/chitchat.py +++ /dev/null @@ -1,155 +0,0 @@ -""" -Specific dialogue class for the chit-chat dialogue. - -The contents of this file are to be shared between the agents that want to -use this dialogue. This defines the structure of the specific dialogue and -the messages that are expected to be exchanged. -""" - -from typing import Optional, Type - -from uagents import Model -from uagents.experimental.dialogues import Dialogue, Edge, Node -from uagents.storage import StorageAPI - -# Node definition for the dialogue states -default_state = Node( - name="Default State", - description=( - "This is the default state of the dialogue. Every session starts in " - "this state and is automatically updated once the dialogue starts." - ), - initial=True, -) -init_state = Node( - name="Initiated", - description=( - "This is the initial state of the dialogue that is only available at " - "the receiving agent." - ), -) -chatting_state = Node( - name="Chit Chatting", - description="This is the state in which messages are exchanged.", -) -end_state = Node( - name="Concluded", - description="This is the state after the dialogue has been concluded.", -) - -# Edge definition for the dialogue transitions -init_session = Edge( - name="Initiate session", - description="Every dialogue starts with this transition.", - parent=default_state, - child=init_state, -) -reject_session = Edge( - name="Reject session", - description=("This is the transition for when the dialogue is rejected"), - parent=init_state, - child=end_state, -) -start_dialogue = Edge( - name="Start dialogue", - description="This is the transition from initiated to chit chatting.", - parent=init_state, - child=chatting_state, -) -cont_dialogue = Edge( - name="Continue dialogue", - description=( - "This is the transition from one dialogue message to the next, " - "i.e. for when the dialogue continues." - ), - parent=chatting_state, - child=chatting_state, -) -end_session = Edge( - name="End session", - description="This is the transition for when the session is ended.", - parent=chatting_state, - child=end_state, -) - - -class ChitChatDialogue(Dialogue): - """ - This is the specific definition of the rules for the chit-chat dialogue - The rules will be predefined and the actual messages will be passed into it - """ - - def __init__( - self, - version: Optional[str] = None, - storage: Optional[StorageAPI] = None, - ) -> None: - """ - Initialize the ChitChatDialogue class. - - Args: - version (Optional[str], optional): Version of the dialogue. Defaults to None. - storage (Optional[StorageAPI], optional): Storage to use. - None will generate a new KeyValueStore based on the dialogue name. - Defaults to None. - """ - super().__init__( - name="ChitChatDialogue", - version=version, - storage=storage, - nodes=[ - default_state, - init_state, - chatting_state, - end_state, - ], - edges=[ - init_session, - reject_session, - start_dialogue, - cont_dialogue, - end_session, - ], - ) - - def on_initiate_session(self, model: Type[Model]): - """ - This handler is triggered when the initial message of the - dialogue is received. From here you can either accept or reject. - Logic that is needed to complete any kind of handshake or considers - global agent state should go here. - """ - return super()._on_state_transition(init_session.name, model) - - def on_reject_session(self, model: Type[Model]): - """ - This handler is triggered when a reject message is returned on - the initial message. - Implement this if you need to clean up session data. - """ - return super()._on_state_transition(reject_session.name, model) - - def on_start_dialogue(self, model: Type[Model]): - """ - This handler is triggered when an accept message is returned on - the initial message. - Include logic to complete any handshake on the sender side and - prepare the actual message exchange. - """ - return super()._on_state_transition(start_dialogue.name, model) - - def on_continue_dialogue(self, model: Type[Model]): - """ - This handler is triggered for every incoming "chitchat" message - once the session has been accepted. - Any additional stateful information within a dialogue needs to be - persisted explicitly to access it at a later point in the dialogue. - """ - return super()._on_state_transition(cont_dialogue.name, model) - - def on_end_session(self, model: Type[Model]): - """ - This handler is triggered once the other party has ended the dialogue. - Any final conclusion or cleanup goes here. - """ - return super()._on_state_transition(end_session.name, model) diff --git a/python/examples/17-stateful-communication/dialogues/hardcoded_chitchat.py b/python/examples/17-stateful-communication/dialogues/hardcoded_chitchat.py deleted file mode 100644 index b06987296..000000000 --- a/python/examples/17-stateful-communication/dialogues/hardcoded_chitchat.py +++ /dev/null @@ -1,208 +0,0 @@ -""" -Specific dialogue class for the chit-chat dialogue. - -The contents of this file are to be shared between the agents that want to -use this dialogue. This defines the structure of the specific dialogue and -the messages that are expected to be exchanged. -""" - -from typing import Optional, Type -from warnings import warn - -from uagents import Model -from uagents.context import Context -from uagents.experimental.dialogues import Dialogue, Edge, Node -from uagents.storage import StorageAPI - - -# define dialogue messages; each transition needs a separate message -class InitiateChitChatDialogue(Model): - pass - - -class AcceptChitChatDialogue(Model): - pass - - -class ChitChatDialogueMessage(Model): - text: str - - -class ConcludeChitChatDialogue(Model): - pass - - -class RejectChitChatDialogue(Model): - pass - - -# Node definition for the dialogue states -default_state = Node( - name="Default State", - description=( - "This is the default state of the dialogue. Every session starts in " - "this state and is automatically updated once ." - ), - initial=True, -) # currently not used as states are measured by the edges -init_state = Node( - name="Initiated", - description=( - "This is the initial state of the dialogue that is only available at " - "the receiving agent." - ), -) -chatting_state = Node( - name="Chit Chatting", - description="This is the state in which messages are exchanged.", -) -end_state = Node( - name="Concluded", - description="This is the state after the dialogue has been concluded.", -) - -# Edge definition for the dialogue transitions -init_session = Edge( - name="initiate_session", - description="Every dialogue starts with this transition.", - parent=None, - child=init_state, -) -start_dialogue = Edge( - name="start_dialogue", - description="This is the transition from initiated to chit chatting.", - parent=init_state, - child=chatting_state, -) -cont_dialogue = Edge( - name="continue_dialogue", - description=( - "This is the transition from one dialogue message to the next, " - "i.e. for when the dialogue continues." - ), - parent=chatting_state, - child=chatting_state, -) -end_session = Edge( - name="end_session", - description="This is the transition for when the session is ended.", - parent=chatting_state, - child=end_state, -) - - -# define default behaviour for individual dialogue edges -# only the interaction that requires input from the user is exposed, making the -# other parts of the dialogue more robust and easier to maintain -async def start_chitchat( - ctx: Context, - sender: str, - _msg: Type[Model], -): - ctx.logger.info(f"Received init message from {sender}. Accepting Dialogue.") - await ctx.send(sender, AcceptChitChatDialogue()) - - -async def accept_chitchat( - ctx: Context, - sender: str, - _msg: Type[Model], -): - ctx.logger.info( - f"Dialogue session with {sender} was accepted. " - "I'll say 'Hello!' to start the ChitChat" - ) - await ctx.send(sender, ChitChatDialogueMessage(text="Hello!")) - - -async def conclude_chitchat( - ctx: Context, - sender: str, - _msg: Type[Model], -): - ctx.logger.info(f"Received conclude message from: {sender}") - - -async def default( - _ctx: Context, - _sender: str, - _msg: Type[Model], -): - warn( - "There is no handler for this message, please add your own logic by " - "using the `on_continue_dialogue` decorator.", - RuntimeWarning, - stacklevel=2, - ) - - -async def persisting_function( - ctx: Context, - _sender: str, - _msg: Type[Model], -): - ctx.logger.info("I was not overwritten, hehe.") - - -init_session.set_message_handler(InitiateChitChatDialogue, start_chitchat) -start_dialogue.set_message_handler(AcceptChitChatDialogue, accept_chitchat) - -cont_dialogue.set_message_handler(ChitChatDialogueMessage, default) -cont_dialogue.set_edge_handler(ChitChatDialogueMessage, persisting_function) - -end_session.set_message_handler(ConcludeChitChatDialogue, conclude_chitchat) - - -class ChitChatDialogue(Dialogue): - """ - This is the specific definition of the rules for the chit-chat dialogue - The rules will be predefined and the actual messages will be passed into it. - - In this specific instance of the ChitChatDialogue, some parts of the dialogue - are hardcoded, such as the initial message and the response to it. - This is done to demonstrate that the dialogue can be defined in a way for - developers to only focus on the parts that are relevant to them. - """ - - def __init__( - self, - version: Optional[str] = None, - storage: Optional[StorageAPI] = None, - ) -> None: - """ - Initialize the ChitChatDialogue class where certain behaviour is hardcoded / predefined. - - Args: - version (Optional[str], optional): Version of the dialogue. Defaults to None. - storage (Optional[StorageAPI], optional): Storage to use. - None will generate a new KeyValueStore based on the dialogue name. - Defaults to None. - """ - super().__init__( - name="ChitChatDialogue", - version=version, - storage=storage, - nodes=[ - init_state, - chatting_state, - end_state, - ], - edges=[ - init_session, - start_dialogue, - cont_dialogue, - end_session, - ], - ) - - def on_continue_dialogue(self): - """ - This handler is triggered for every incoming "chitchat" message - once the session has been accepted. - Any additional stateful information within a dialogue needs to be - persisted explicitly to access it at a later point in the dialogue. - """ - return super()._on_state_transition( - cont_dialogue.name, - ChitChatDialogueMessage, - ) diff --git a/python/examples/17-stateful-communication/dialogues/simple_chitchat.py b/python/examples/17-stateful-communication/dialogues/simple_chitchat.py deleted file mode 100644 index 8b83ce229..000000000 --- a/python/examples/17-stateful-communication/dialogues/simple_chitchat.py +++ /dev/null @@ -1,109 +0,0 @@ -"""Specific dialogue class for the chit-chat dialogue.""" - -from typing import Optional, Type - -from uagents import Model -from uagents.experimental.dialogues import Dialogue, Edge, Node -from uagents.storage import StorageAPI - -# Node definition for the dialogue states -chatting_state = Node( - name="Chit Chatting", - description="While in this state, more messages can be exchanged.", -) -end_state = Node( - name="Concluded", - description="This is the state after the dialogue has been concluded and " - "no more messages will be accepted.", -) - -# Edge definition for the dialogue transitions -start_dialogue = Edge( - name="Start Dialogue", - description=( - "A message that initiates a ChitChat conversation and provides " - "any information needed to set the context and let the receiver " - "decide whether to accept or directly end this conversation." - ), - parent=None, - child=chatting_state, -) -cont_dialogue = Edge( - name="Continue Dialogue", - description=( - "A general message structure to exchange information without " - "annotating further states or limiting the message flow in any way." - ), - parent=chatting_state, - child=chatting_state, -) -end_dialogue = Edge( - name="End Dialogue", - description=( - "A final message that can be sent at any time by either party " - "to finish this dialogue." - ), - parent=chatting_state, - child=end_state, -) - - -class SimpleChitChatDialogue(Dialogue): - """ - The SimpleChitChatDialogue provides a simple pattern that allows 2 parties - to exchange an arbitrary number of "ChitChat" messages - """ - - def __init__( - self, - version: Optional[str] = None, - storage: Optional[StorageAPI] = None, - ) -> None: - """ - Initialize the simple ChitChatDialogue class. - - Args: - version (Optional[str], optional): Version of the dialogue. Defaults to None. - storage (Optional[StorageAPI], optional): Storage to use. - None will generate a new KeyValueStore based on the dialogue name. - Defaults to None. - """ - super().__init__( - name="ChitChatDialogue_simple", - version=version, - storage=storage, - nodes=[ - chatting_state, - end_state, - ], - edges=[ - start_dialogue, - cont_dialogue, - end_dialogue, - ], - ) - - def on_start_dialogue(self, model: Type[Model]): - """ - This handler is triggered when the initial message of the - dialogue is received. - It automatically transitions into the chit-chatting state for - the next message exchange or directly into the end state. - """ - return super()._on_state_transition(start_dialogue.name, model) - - def on_continue_dialogue(self, model: Type[Model]): - """ - This handler is triggered for every incoming "chitchat" message - once the session has been accepted. - Any additional stateful information within a dialogue needs to be - persisted explicitly to access it at a later point in the dialogue. - """ - return super()._on_state_transition(cont_dialogue.name, model) - - def on_end_session(self, model: Type[Model]): - """ - This handler is triggered once the other party has ended the dialogue. - Any final conclusion or cleanup goes here. - """ - return super()._on_state_transition(end_dialogue.name, model) diff --git a/python/examples/18-async-loops/external_loop_attach.py b/python/examples/18-async-loops/external_loop_attach.py deleted file mode 100644 index a425d74d8..000000000 --- a/python/examples/18-async-loops/external_loop_attach.py +++ /dev/null @@ -1,46 +0,0 @@ -import asyncio -import contextlib - -from uagents import Agent, Bureau, Context - -loop = asyncio.get_event_loop() - - -agent = Agent( - name="looper", - seed="", -) - -bureau = Bureau( - agents=[agent], -) - - -@agent.on_event("startup") -async def startup(ctx: Context): - ctx.logger.info(">>> Looper is starting up.") - - -@agent.on_event("shutdown") -async def shutdown(ctx: Context): - ctx.logger.info(">>> Looper is shutting down.") - - -async def coro(): - while True: - print("doing hard work...") - await asyncio.sleep(1) - - -if __name__ == "__main__": - print("Attaching the agent or bureau to the external loop...") - loop.create_task(coro()) - - # > when attaching the agent to the external loop - loop.create_task(agent.run_async()) - - # > when attaching a bureau to the external loop - # loop.create_task(bureau.run_async()) - - with contextlib.suppress(KeyboardInterrupt): - loop.run_forever() diff --git a/python/examples/18-async-loops/external_loop_run.py b/python/examples/18-async-loops/external_loop_run.py deleted file mode 100644 index 203f0c73f..000000000 --- a/python/examples/18-async-loops/external_loop_run.py +++ /dev/null @@ -1,44 +0,0 @@ -import asyncio - -from uagents import Agent, Bureau, Context - -loop = asyncio.get_event_loop() - - -agent = Agent( - name="looper", - seed="", - loop=loop, -) - -bureau = Bureau( - agents=[agent], - loop=loop, -) - - -@agent.on_event("startup") -async def startup(ctx: Context): - ctx.logger.info(">>> Looper is starting up.") - - -@agent.on_event("shutdown") -async def shutdown(ctx: Context): - ctx.logger.info(">>> Looper is shutting down.") - - -async def coro(): - while True: - print("doing hard work...") - await asyncio.sleep(1) - - -if __name__ == "__main__": - print("Starting the external loop from the agent or bureau...") - loop.create_task(coro()) - - # > when starting the external loop from the agent - agent.run() - - # > when starting the external loop from the bureau - # bureau.run() diff --git a/python/examples/19-rest-api/README.md b/python/examples/19-rest-api/README.md deleted file mode 100644 index 25e9635ef..000000000 --- a/python/examples/19-rest-api/README.md +++ /dev/null @@ -1,50 +0,0 @@ -## Example of how to add custom REST endpoints to your agent - -By using one of the two new decorators: `on_rest_get()` and `on_rest_post()` you are able to define custom endpoints that your agent can act upon. -Please note that this feature is only available at the "agent-level" meaning that you cannot add rest endpoints to uagents `Protocols`. - -The usage is similar to a message handler in that you define: - -- a custom endpoint in string format, e.g. `"/my_rest_endpoint"`, -- a Request Model (inheriting from uagents.models) for `POST` endpoints, and -- a Response Model for `GET` endpoints - -The difference to a message handler is that you actually have to invoke `return` for the value to be returned to the REST client. The format can either be `Dict[str, Any]` or the `Model` itself but either way the output will be validated against the predefined response model. - -**GET request example** - -```python -@agent.on_rest_get("/custom_get_route", Response) -async def handle_get(ctx: Context) -> Dict[str, Any]: - return { - "field": , - } -``` - -**POST request example** - -```python -@agent.on_rest_post("/custom_post_route", Request, Response) -async def handle_post(ctx: Context, req: Request) -> Response: - ctx.logger.info(req) # access to the request - return Response(...) -``` - -For querying the agent you have to make sure that: - -1. You use the correct REST method ("GET" or "POST"), and -2. You address the agent endpoint together with its route (`http://localhost:8000/custom_route`) - -### Run the example - -1. Run the agent: - -```bash -python agent.py -``` - -2. Query the agent directly through your predefined interfaces: - -```bash -curl -d '{"text": "test"}' -H "Content-Type: application/json" -X POST http://localhost:8000/rest/post -``` diff --git a/python/examples/19-rest-api/agent.py b/python/examples/19-rest-api/agent.py deleted file mode 100644 index 958ddc0a6..000000000 --- a/python/examples/19-rest-api/agent.py +++ /dev/null @@ -1,46 +0,0 @@ -import time -from typing import Any, Dict - -from uagents import Agent, Context, Model - - -class Request(Model): - text: str - - -class Response(Model): - timestamp: int - text: str - agent_address: str - - -# You can also use empty models to represent empty request/response bodies -class EmptyMessage(Model): - pass - - -agent = Agent(name="Rest API") - - -@agent.on_rest_get("/rest/get", Response) -async def handle_get(ctx: Context) -> Dict[str, Any]: - ctx.logger.info("Received GET request") - return { - "timestamp": int(time.time()), - "text": "Hello from the GET handler!", - "agent_address": ctx.agent.address, - } - - -@agent.on_rest_post("/rest/post", Request, Response) -async def handle_post(ctx: Context, req: Request) -> Response: - ctx.logger.info("Received POST request") - return Response( - text=f"Received: {req.text}", - agent_address=ctx.agent.address, - timestamp=int(time.time()), - ) - - -if __name__ == "__main__": - agent.run()