Skip to content

Commit 5f3a88f

Browse files
authored
Add files via upload
1 parent 81c1784 commit 5f3a88f

1 file changed

Lines changed: 327 additions & 0 deletions

File tree

Lines changed: 327 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,327 @@
1+
{
2+
"nbformat": 4,
3+
"nbformat_minor": 0,
4+
"metadata": {
5+
"colab": {
6+
"provenance": []
7+
},
8+
"kernelspec": {
9+
"name": "python3",
10+
"display_name": "Python 3"
11+
},
12+
"language_info": {
13+
"name": "python"
14+
}
15+
},
16+
"cells": [
17+
{
18+
"cell_type": "code",
19+
"source": [
20+
"!pip -q install \"uagents>=0.11.2\"\n",
21+
"\n",
22+
"import asyncio, random\n",
23+
"from typing import List, Dict, Optional\n",
24+
"from uagents import Agent, Context, Bureau, Model, Protocol\n",
25+
"\n",
26+
"class ServiceAnnounce(Model):\n",
27+
" category: str\n",
28+
" endpoint: str\n",
29+
"\n",
30+
"class ServiceQuery(Model):\n",
31+
" category: str\n",
32+
"\n",
33+
"class ServiceList(Model):\n",
34+
" addresses: List[str]\n",
35+
"\n",
36+
"class OfferRequest(Model):\n",
37+
" item: str\n",
38+
" max_price: int\n",
39+
"\n",
40+
"class Offer(Model):\n",
41+
" item: str\n",
42+
" price: int\n",
43+
" qty: int\n",
44+
"\n",
45+
"class Order(Model):\n",
46+
" item: str\n",
47+
" qty: int\n",
48+
"\n",
49+
"class Receipt(Model):\n",
50+
" item: str\n",
51+
" qty: int\n",
52+
" total: int\n",
53+
" ok: bool\n",
54+
" note: Optional[str] = None"
55+
],
56+
"metadata": {
57+
"id": "JLZCduHv7Mc0"
58+
},
59+
"execution_count": null,
60+
"outputs": []
61+
},
62+
{
63+
"cell_type": "code",
64+
"source": [
65+
"registry_proto = Protocol(name=\"registry\", version=\"1.0\")\n",
66+
"trade_proto = Protocol(name=\"trade\", version=\"1.0\")\n",
67+
"\n",
68+
"directory = Agent(name=\"directory\", seed=\"dir-seed-001\")\n",
69+
"seller = Agent(name=\"seller\", seed=\"seller-seed-001\")\n",
70+
"buyer = Agent(name=\"buyer\", seed=\"buyer-seed-001\")\n",
71+
"\n",
72+
"directory.include(registry_proto)\n",
73+
"seller.include(trade_proto)\n",
74+
"buyer.include(registry_proto)\n",
75+
"buyer.include(trade_proto)\n",
76+
"\n",
77+
"@registry_proto.on_message(model=ServiceAnnounce)\n",
78+
"async def on_announce(ctx: Context, sender: str, msg: ServiceAnnounce):\n",
79+
" reg = await ctx.storage.get(\"reg\") or {}\n",
80+
" reg.setdefault(msg.category, set()).add(sender)\n",
81+
" await ctx.storage.set(\"reg\", reg)\n",
82+
" ctx.logger.info(f\"Registered {sender} under '{msg.category}'\")\n",
83+
"\n",
84+
"@registry_proto.on_message(model=ServiceQuery)\n",
85+
"async def on_query(ctx: Context, sender: str, msg: ServiceQuery):\n",
86+
" reg = await ctx.storage.get(\"reg\") or {}\n",
87+
" addrs = sorted(list(reg.get(msg.category, set())))\n",
88+
" await ctx.send(sender, ServiceList(addresses=addrs))\n",
89+
" ctx.logger.info(f\"Returned {len(addrs)} providers for '{msg.category}'\")"
90+
],
91+
"metadata": {
92+
"id": "S_Z5bjy27MZ9"
93+
},
94+
"execution_count": null,
95+
"outputs": []
96+
},
97+
{
98+
"cell_type": "code",
99+
"source": [
100+
"CATALOG: Dict[str, Dict[str, int]] = {\n",
101+
" \"camera\": {\"price\": 120, \"qty\": 3},\n",
102+
" \"laptop\": {\"price\": 650, \"qty\": 2},\n",
103+
" \"headphones\": {\"price\": 60, \"qty\": 5},\n",
104+
"}\n",
105+
"\n",
106+
"@seller.on_event(\"startup\")\n",
107+
"async def seller_start(ctx: Context):\n",
108+
" await ctx.send(directory.address, ServiceAnnounce(category=\"electronics\", endpoint=seller.address))\n",
109+
" ctx.logger.info(\"Seller announced to directory\")\n",
110+
"\n",
111+
"@trade_proto.on_message(model=OfferRequest)\n",
112+
"async def on_offer_request(ctx: Context, sender: str, req: OfferRequest):\n",
113+
" item = CATALOG.get(req.item)\n",
114+
" if not item:\n",
115+
" await ctx.send(sender, Offer(item=req.item, price=0, qty=0))\n",
116+
" return\n",
117+
" price = max(1, int(item[\"price\"] * (0.9 + 0.2 * random.random())))\n",
118+
" if price > req.max_price or item[\"qty\"] <= 0:\n",
119+
" await ctx.send(sender, Offer(item=req.item, price=0, qty=0))\n",
120+
" return\n",
121+
" await ctx.send(sender, Offer(item=req.item, price=price, qty=item[\"qty\"]))\n",
122+
" ctx.logger.info(f\"Offered {req.item} at {price} with qty {item['qty']}\")\n",
123+
"\n",
124+
"@trade_proto.on_message(model=Order)\n",
125+
"async def on_order(ctx: Context, sender: str, order: Order):\n",
126+
" item = CATALOG.get(order.item)\n",
127+
" if not item or item[\"qty\"] < order.qty:\n",
128+
" await ctx.send(sender, Receipt(item=order.item, qty=0, total=0, ok=False, note=\"Not enough stock\"))\n",
129+
" return\n",
130+
" total = item[\"price\"] * order.qty\n",
131+
" item[\"qty\"] -= order.qty\n",
132+
" await ctx.send(sender, Receipt(item=order.item, qty=order.qty, total=total, ok=True, note=\"Thanks!\"))"
133+
],
134+
"metadata": {
135+
"id": "PGQUQi347MXJ"
136+
},
137+
"execution_count": null,
138+
"outputs": []
139+
},
140+
{
141+
"cell_type": "code",
142+
"source": [
143+
"@buyer.on_event(\"startup\")\n",
144+
"async def buyer_start(ctx: Context):\n",
145+
" ctx.logger.info(\"Buyer querying directory for electronics...\")\n",
146+
" resp = await ctx.ask(directory.address, ServiceQuery(category=\"electronics\"), expects=ServiceList, timeout=5.0)\n",
147+
" sellers = resp.addresses if resp else []\n",
148+
" if not sellers:\n",
149+
" return\n",
150+
" target = sellers[0]\n",
151+
" desired = \"laptop\"\n",
152+
" budget = 700\n",
153+
" ctx.logger.info(f\"Requesting offer for '{desired}' within budget {budget} from {target}\")\n",
154+
" offer = await ctx.ask(target, OfferRequest(item=desired, max_price=budget), expects=Offer, timeout=5.0)\n",
155+
" if not offer or offer.price <= 0:\n",
156+
" return\n",
157+
" qty = 1 if offer.qty >= 1 else 0\n",
158+
" if qty == 0:\n",
159+
" return\n",
160+
" ctx.logger.info(f\"Placing order for {qty} x {offer.item} at {offer.price}\")\n",
161+
" receipt = await ctx.ask(target, Order(item=offer.item, qty=qty), expects=Receipt, timeout=5.0)\n",
162+
" if receipt and receipt.ok:\n",
163+
" ctx.logger.info(f\"ORDER SUCCESS: {receipt.qty} x {receipt.item} | total={receipt.total}\")"
164+
],
165+
"metadata": {
166+
"id": "cwxrJFiW7MUF"
167+
},
168+
"execution_count": null,
169+
"outputs": []
170+
},
171+
{
172+
"cell_type": "code",
173+
"execution_count": 2,
174+
"metadata": {
175+
"colab": {
176+
"base_uri": "https://localhost:8080/"
177+
},
178+
"id": "qXW2QFCw6VK9",
179+
"outputId": "0dc7b642-1248-455f-c006-dcba3af77945"
180+
},
181+
"outputs": [
182+
{
183+
"output_type": "stream",
184+
"name": "stdout",
185+
"text": [
186+
"INFO: [directory]: Starting agent with address: agent1qvgxjeed0mvkhfmvnywkpyyeadkkj07v9gvcfz40v8p5738c55x2vn86mez\n",
187+
"INFO: [directory]: Starting agent with address: agent1qvgxjeed0mvkhfmvnywkpyyeadkkj07v9gvcfz40v8p5738c55x2vn86mez\n",
188+
"WARNING: [directory]: No endpoints provided. Skipping registration: Agent won't be reachable.\n",
189+
"WARNING: [directory]: No endpoints provided. Skipping registration: Agent won't be reachable.\n",
190+
"INFO: [seller]: Starting agent with address: agent1q0wv76nvkr60wvfvmf0jjf2qygl2csarhf09dglcd3xh2an2yxv9uvar5la\n",
191+
"INFO: [seller]: Starting agent with address: agent1q0wv76nvkr60wvfvmf0jjf2qygl2csarhf09dglcd3xh2an2yxv9uvar5la\n",
192+
"WARNING: [seller]: No endpoints provided. Skipping registration: Agent won't be reachable.\n",
193+
"WARNING: [seller]: No endpoints provided. Skipping registration: Agent won't be reachable.\n",
194+
"INFO: [buyer]: Starting agent with address: agent1qwk5eey3t36pswtv4f7w5euf4myycnqar8lsq3dd2hq4gcmk6y2vxenlqwq\n",
195+
"INFO: [buyer]: Starting agent with address: agent1qwk5eey3t36pswtv4f7w5euf4myycnqar8lsq3dd2hq4gcmk6y2vxenlqwq\n",
196+
"WARNING: [buyer]: No endpoints provided. Skipping registration: Agent won't be reachable.\n",
197+
"WARNING: [buyer]: No endpoints provided. Skipping registration: Agent won't be reachable.\n",
198+
"INFO: [seller]: Seller announced to directory\n",
199+
"INFO: [seller]: Seller announced to directory\n",
200+
"INFO: [buyer]: Buyer querying directory for electronics...\n",
201+
"INFO: [buyer]: Buyer querying directory for electronics...\n",
202+
"ERROR: [buyer]: Exception in startup handler: 'InternalContext' object has no attribute 'ask'\n",
203+
"Traceback (most recent call last):\n",
204+
" File \"/usr/local/lib/python3.12/dist-packages/uagents/agent.py\", line 1169, in run_startup_tasks\n",
205+
" await handler(ctx)\n",
206+
" File \"/tmp/ipython-input-3257487494.py\", line 111, in buyer_start\n",
207+
" resp = await ctx.ask(directory.address, ServiceQuery(category=\"electronics\"), expects=ServiceList, timeout=5.0)\n",
208+
" ^^^^^^^\n",
209+
"AttributeError: 'InternalContext' object has no attribute 'ask'\n",
210+
"ERROR: [buyer]: Exception in startup handler: 'InternalContext' object has no attribute 'ask'\n",
211+
"Traceback (most recent call last):\n",
212+
" File \"/usr/local/lib/python3.12/dist-packages/uagents/agent.py\", line 1169, in run_startup_tasks\n",
213+
" await handler(ctx)\n",
214+
" File \"/tmp/ipython-input-3257487494.py\", line 111, in buyer_start\n",
215+
" resp = await ctx.ask(directory.address, ServiceQuery(category=\"electronics\"), expects=ServiceList, timeout=5.0)\n",
216+
" ^^^^^^^\n",
217+
"AttributeError: 'InternalContext' object has no attribute 'ask'\n",
218+
"INFO: [bureau]: Starting server on http://0.0.0.0:8000 (Press CTRL+C to quit)\n",
219+
"INFO: [bureau]: Starting server on http://0.0.0.0:8000 (Press CTRL+C to quit)\n",
220+
"WARNING: [directory]: Received message with unrecognized schema digest: model:b87a4c644ee706f817dfffc3097f23cbdc491c7ea359d4e7fac6d53a6f4aef59\n",
221+
"WARNING: [directory]: Received message with unrecognized schema digest: model:b87a4c644ee706f817dfffc3097f23cbdc491c7ea359d4e7fac6d53a6f4aef59\n",
222+
"ERROR: [buyer]: Exception in interval handler: object NoneType can't be used in 'await' expression\n",
223+
"Traceback (most recent call last):\n",
224+
" File \"/usr/local/lib/python3.12/dist-packages/uagents/agent.py\", line 109, in _run_interval\n",
225+
" await func(ctx)\n",
226+
" File \"/tmp/ipython-input-3257487494.py\", line 138, in periodic_discovery\n",
227+
" seen = await ctx.storage.get(\"seen\") or 0\n",
228+
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
229+
"TypeError: object NoneType can't be used in 'await' expression\n",
230+
"ERROR: [buyer]: Exception in interval handler: object NoneType can't be used in 'await' expression\n",
231+
"Traceback (most recent call last):\n",
232+
" File \"/usr/local/lib/python3.12/dist-packages/uagents/agent.py\", line 109, in _run_interval\n",
233+
" await func(ctx)\n",
234+
" File \"/tmp/ipython-input-3257487494.py\", line 138, in periodic_discovery\n",
235+
" seen = await ctx.storage.get(\"seen\") or 0\n",
236+
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
237+
"TypeError: object NoneType can't be used in 'await' expression\n",
238+
"ERROR: [buyer]: Exception in interval handler: object NoneType can't be used in 'await' expression\n",
239+
"Traceback (most recent call last):\n",
240+
" File \"/usr/local/lib/python3.12/dist-packages/uagents/agent.py\", line 109, in _run_interval\n",
241+
" await func(ctx)\n",
242+
" File \"/tmp/ipython-input-3257487494.py\", line 138, in periodic_discovery\n",
243+
" seen = await ctx.storage.get(\"seen\") or 0\n",
244+
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
245+
"TypeError: object NoneType can't be used in 'await' expression\n",
246+
"ERROR: [buyer]: Exception in interval handler: object NoneType can't be used in 'await' expression\n",
247+
"Traceback (most recent call last):\n",
248+
" File \"/usr/local/lib/python3.12/dist-packages/uagents/agent.py\", line 109, in _run_interval\n",
249+
" await func(ctx)\n",
250+
" File \"/tmp/ipython-input-3257487494.py\", line 138, in periodic_discovery\n",
251+
" seen = await ctx.storage.get(\"seen\") or 0\n",
252+
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
253+
"TypeError: object NoneType can't be used in 'await' expression\n",
254+
"INFO: [bureau]: Shutting down server...\n",
255+
"INFO: [bureau]: Shutting down server...\n"
256+
]
257+
},
258+
{
259+
"output_type": "stream",
260+
"name": "stderr",
261+
"text": [
262+
"ERROR:asyncio:Exception in callback Task.task_wakeup(<Task finishe...> result=None>)\n",
263+
"handle: <Handle Task.task_wakeup(<Task finishe...> result=None>)>\n",
264+
"Traceback (most recent call last):\n",
265+
" File \"/usr/lib/python3.12/asyncio/events.py\", line 88, in _run\n",
266+
" self._context.run(self._callback, *self._args)\n",
267+
" File \"/usr/lib/python3.12/asyncio/tasks.py\", line 721, in cancel\n",
268+
" if child.cancel(msg=msg):\n",
269+
" ^^^^^^^^^^^^^^^^^^^^^\n",
270+
" File \"/usr/lib/python3.12/asyncio/tasks.py\", line 721, in cancel\n",
271+
" if child.cancel(msg=msg):\n",
272+
" ^^^^^^^^^^^^^^^^^^^^^\n",
273+
" File \"/usr/lib/python3.12/asyncio/tasks.py\", line 721, in cancel\n",
274+
" if child.cancel(msg=msg):\n",
275+
" ^^^^^^^^^^^^^^^^^^^^^\n",
276+
" [Previous line repeated 988 more times]\n",
277+
"RecursionError: maximum recursion depth exceeded\n"
278+
]
279+
},
280+
{
281+
"output_type": "stream",
282+
"name": "stdout",
283+
"text": [
284+
"\n",
285+
"✅ Demo run complete.\n",
286+
"\n"
287+
]
288+
}
289+
],
290+
"source": [
291+
"@buyer.on_interval(period=6.0)\n",
292+
"async def periodic_discovery(ctx: Context):\n",
293+
" seen = await ctx.storage.get(\"seen\") or 0\n",
294+
" if seen >= 1:\n",
295+
" return\n",
296+
" await ctx.storage.set(\"seen\", seen + 1)\n",
297+
" ctx.logger.info(\"Periodic discovery tick -> re-query directory\")\n",
298+
" resp = await ctx.ask(directory.address, ServiceQuery(category=\"electronics\"), expects=ServiceList, timeout=3.0)\n",
299+
" n = len(resp.addresses) if resp else 0\n",
300+
" ctx.logger.info(f\"Periodic: directory reports {n} seller(s)\")\n",
301+
"\n",
302+
"bureau = Bureau()\n",
303+
"bureau.add(directory)\n",
304+
"bureau.add(seller)\n",
305+
"bureau.add(buyer)\n",
306+
"\n",
307+
"async def run_demo(seconds=10):\n",
308+
" task = asyncio.create_task(bureau.run_async())\n",
309+
" try:\n",
310+
" await asyncio.sleep(seconds)\n",
311+
" finally:\n",
312+
" task.cancel()\n",
313+
" try:\n",
314+
" await task\n",
315+
" except asyncio.CancelledError:\n",
316+
" pass\n",
317+
" print(\"\\n✅ Demo run complete.\\n\")\n",
318+
"\n",
319+
"try:\n",
320+
" loop = asyncio.get_running_loop()\n",
321+
" await run_demo(10)\n",
322+
"except RuntimeError:\n",
323+
" asyncio.run(run_demo(10))"
324+
]
325+
}
326+
]
327+
}

0 commit comments

Comments
 (0)