agentstr 0.1.12__tar.gz → 0.1.14__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (26) hide show
  1. {agentstr-0.1.12/src/agentstr.egg-info → agentstr-0.1.14}/PKG-INFO +10 -7
  2. {agentstr-0.1.12 → agentstr-0.1.14}/pyproject.toml +11 -7
  3. agentstr-0.1.14/src/agentstr/__init__.py +49 -0
  4. {agentstr-0.1.12 → agentstr-0.1.14}/src/agentstr/buyer.py +37 -23
  5. {agentstr-0.1.12 → agentstr-0.1.14}/src/agentstr/buyer.pyi +1 -0
  6. {agentstr-0.1.12 → agentstr-0.1.14}/src/agentstr/merchant.py +68 -59
  7. {agentstr-0.1.12 → agentstr-0.1.14}/src/agentstr/merchant.pyi +1 -1
  8. {agentstr-0.1.12 → agentstr-0.1.14}/src/agentstr/models.py +8 -9
  9. {agentstr-0.1.12 → agentstr-0.1.14}/src/agentstr/models.pyi +1 -1
  10. {agentstr-0.1.12 → agentstr-0.1.14}/src/agentstr/nostr.py +75 -78
  11. {agentstr-0.1.12 → agentstr-0.1.14}/src/agentstr/nostr.pyi +15 -1
  12. {agentstr-0.1.12 → agentstr-0.1.14/src/agentstr.egg-info}/PKG-INFO +10 -7
  13. {agentstr-0.1.12 → agentstr-0.1.14}/src/agentstr.egg-info/requires.txt +7 -3
  14. {agentstr-0.1.12 → agentstr-0.1.14}/tests/test_buyer.py +24 -5
  15. {agentstr-0.1.12 → agentstr-0.1.14}/tests/test_merchant.py +22 -6
  16. {agentstr-0.1.12 → agentstr-0.1.14}/tests/test_nostr_integration.py +6 -2
  17. {agentstr-0.1.12 → agentstr-0.1.14}/tests/test_nostr_mocked.py +32 -26
  18. agentstr-0.1.12/src/agentstr/__init__.py +0 -37
  19. {agentstr-0.1.12 → agentstr-0.1.14}/LICENSE +0 -0
  20. {agentstr-0.1.12 → agentstr-0.1.14}/MANIFEST.in +0 -0
  21. {agentstr-0.1.12 → agentstr-0.1.14}/README.md +0 -0
  22. {agentstr-0.1.12 → agentstr-0.1.14}/setup.cfg +0 -0
  23. {agentstr-0.1.12 → agentstr-0.1.14}/src/agentstr/py.typed +0 -0
  24. {agentstr-0.1.12 → agentstr-0.1.14}/src/agentstr.egg-info/SOURCES.txt +0 -0
  25. {agentstr-0.1.12 → agentstr-0.1.14}/src/agentstr.egg-info/dependency_links.txt +0 -0
  26. {agentstr-0.1.12 → agentstr-0.1.14}/src/agentstr.egg-info/top_level.txt +0 -0
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: agentstr
3
- Version: 0.1.12
4
- Summary: Nostr extension for Agno AI agents
5
- Author-email: Synvya <info@synvya.com>
3
+ Version: 0.1.14
4
+ Summary: Tools for a Nostr agentic ecosystem
5
+ Author-email: Synvya <synvya@synvya.com>
6
6
  License: MIT
7
7
  Project-URL: Homepage, https://www.synvya.com
8
8
  Project-URL: Repository, https://github.com/synvya/agentstr
@@ -13,18 +13,21 @@ Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
14
  Requires-Dist: agno>=1.1.1
15
15
  Requires-Dist: openai>=1.50.0
16
- Requires-Dist: packaging>=24.0
17
16
  Requires-Dist: nostr_sdk>=0.39.0
18
17
  Requires-Dist: pydantic>=2.0.0
19
- Requires-Dist: cassandra-driver>=3.29.2
20
- Requires-Dist: cassio>=0.1.10
21
18
  Provides-Extra: dev
22
19
  Requires-Dist: pytest>=7.0; extra == "dev"
23
20
  Requires-Dist: pytest-asyncio>=0.23.5; extra == "dev"
24
21
  Requires-Dist: black>=23.0; extra == "dev"
25
22
  Requires-Dist: isort>=5.0; extra == "dev"
26
23
  Requires-Dist: mypy>=1.0; extra == "dev"
27
- Requires-Dist: python-dotenv>=1.0; extra == "dev"
24
+ Requires-Dist: pylint>=3.0; extra == "dev"
25
+ Provides-Extra: examples
26
+ Requires-Dist: python-dotenv>=1.0; extra == "examples"
27
+ Requires-Dist: cassandra-driver>=3.29.2; extra == "examples"
28
+ Requires-Dist: cassio>=0.1.10; extra == "examples"
29
+ Requires-Dist: fastapi>=0.110.0; extra == "examples"
30
+ Requires-Dist: uvicorn>=0.30.0; extra == "examples"
28
31
 
29
32
  # AgentStr
30
33
 
@@ -4,22 +4,19 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "agentstr"
7
- version = "0.1.12"
8
- description = "Nostr extension for Agno AI agents"
7
+ version = "0.1.14"
8
+ description = "Tools for a Nostr agentic ecosystem"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9, <3.13"
11
11
  license = { text = "MIT" }
12
12
  authors = [
13
- {name = "Synvya", email = "info@synvya.com"}
13
+ {name = "Synvya", email = "synvya@synvya.com"}
14
14
  ]
15
15
  dependencies = [
16
16
  "agno>=1.1.1",
17
17
  "openai>=1.50.0",
18
- "packaging>=24.0",
19
18
  "nostr_sdk>=0.39.0",
20
- "pydantic>=2.0.0",
21
- "cassandra-driver>=3.29.2",
22
- "cassio>=0.1.10",
19
+ "pydantic>=2.0.0",
23
20
  ]
24
21
 
25
22
  [project.optional-dependencies]
@@ -29,7 +26,14 @@ dev = [
29
26
  "black>=23.0",
30
27
  "isort>=5.0",
31
28
  "mypy>=1.0",
29
+ "pylint>=3.0",
30
+ ]
31
+ examples = [
32
32
  "python-dotenv>=1.0",
33
+ "cassandra-driver>=3.29.2",
34
+ "cassio>=0.1.10",
35
+ "fastapi>=0.110.0",
36
+ "uvicorn>=0.30.0",
33
37
  ]
34
38
 
35
39
  [project.urls]
@@ -0,0 +1,49 @@
1
+ """
2
+ AgentStr: Nostr extension for Agno AI agents
3
+ """
4
+
5
+ import importlib.metadata
6
+ import logging
7
+
8
+ from nostr_sdk import ShippingCost, ShippingMethod, Timestamp # type: ignore
9
+
10
+ from agentstr.nostr import EventId, Keys, Kind, NostrClient, generate_and_save_keys
11
+
12
+ from .buyer import BuyerTools
13
+ from .merchant import MerchantTools
14
+
15
+ # Import main classes to make them available at package level
16
+ from .models import AgentProfile, MerchantProduct, MerchantStall, NostrProfile
17
+
18
+ # Import version from pyproject.toml at runtime
19
+ try:
20
+ __version__ = importlib.metadata.version("agentstr")
21
+ except importlib.metadata.PackageNotFoundError:
22
+ logging.warning("Package 'agentstr' not found. Falling back to 'unknown'.")
23
+ __version__ = "unknown"
24
+ except ImportError:
25
+ logging.warning("importlib.metadata is not available. Falling back to 'unknown'.")
26
+ __version__ = "unknown"
27
+
28
+ # Define What is Exposed at the Package Level
29
+ __all__ = [
30
+ # Merchant Tools
31
+ "MerchantTools",
32
+ "MerchantProduct",
33
+ "MerchantStall",
34
+ # Buyer Tools
35
+ "BuyerTools",
36
+ # Shipping
37
+ "ShippingCost",
38
+ "ShippingMethod",
39
+ # Nostr-related utils
40
+ "EventId",
41
+ "Keys",
42
+ "Kind",
43
+ "NostrClient",
44
+ "generate_and_save_keys",
45
+ "Timestamp",
46
+ # Models
47
+ "AgentProfile",
48
+ "NostrProfile",
49
+ ]
@@ -1,17 +1,24 @@
1
+ """
2
+ Module implementing the BuyerTools Toolkit for Agno agents.
3
+ """
4
+
1
5
  import json
2
6
  import logging
3
7
  from uuid import uuid4
4
8
 
5
- from agno.agent import AgentKnowledge # type: ignore
6
- from agno.document.base import Document
9
+ from pydantic import ConfigDict
7
10
 
8
11
  from agentstr.models import AgentProfile, NostrProfile
9
12
  from agentstr.nostr import NostrClient, PublicKey
10
13
 
11
14
  try:
15
+ from agno.agent import AgentKnowledge # type: ignore
16
+ from agno.document.base import Document
12
17
  from agno.tools import Toolkit
13
- except ImportError:
14
- raise ImportError("`agno` not installed. Please install using `pip install agno`")
18
+ except ImportError as exc:
19
+ raise ImportError(
20
+ "`agno` not installed. Please install using `pip install agno`"
21
+ ) from exc
15
22
 
16
23
 
17
24
  def _map_location_to_geohash(location: str) -> str:
@@ -21,20 +28,22 @@ def _map_location_to_geohash(location: str) -> str:
21
28
  TBD: Implement this function. Returning a fixed geohash for now.
22
29
 
23
30
  Args:
24
- location: location to map to a geohash. Can be a zip code, city, state, country, or latitude and longitude.
31
+ location: location to map to a geohash. Can be a zip code, city,
32
+ state, country, or latitude and longitude.
25
33
 
26
34
  Returns:
27
35
  str: geohash of the location or empty string if location is not found
28
36
  """
29
37
  if "snoqualmie" in location.lower():
30
38
  return "C23Q7U36W"
31
- else:
32
- return ""
39
+
40
+ return ""
33
41
 
34
42
 
35
43
  class BuyerTools(Toolkit):
36
44
  """
37
- BuyerTools is a toolkit that allows an agent to find sellers and transact with them over Nostr.
45
+ BuyerTools is a toolkit that allows an agent to find sellers and
46
+ transact with them over Nostr.
38
47
 
39
48
  Sellers are downloaded from the Nostr relay and cached.
40
49
  Sellers can be found by name or public key.
@@ -44,8 +53,6 @@ class BuyerTools(Toolkit):
44
53
  TBD: populate the sellers locations with info from stalls.
45
54
  """
46
55
 
47
- from pydantic import ConfigDict
48
-
49
56
  model_config = ConfigDict(
50
57
  arbitrary_types_allowed=True, extra="allow", validate_assignment=True
51
58
  )
@@ -92,8 +99,13 @@ class BuyerTools(Toolkit):
92
99
 
93
100
  Args:
94
101
  product: JSON string with product to purchase
102
+
103
+ Returns:
104
+ str: JSON string with status and message
95
105
  """
96
- return json.dumps({"status": "success", "message": "Product purchased"})
106
+ return json.dumps(
107
+ {"status": "success", "message": f"Product {product} purchased"}
108
+ )
97
109
 
98
110
  def find_seller_by_name(self, name: str) -> str:
99
111
  """Find a seller by name.
@@ -151,9 +163,6 @@ class BuyerTools(Toolkit):
151
163
  # Find sellers in the same geohash
152
164
  for seller in self.sellers:
153
165
  if geohash in seller.get_locations():
154
- # print(
155
- # f"geohash {geohash} found in seller {seller.get_name()} with locations {seller.get_locations()}"
156
- # )
157
166
  sellers.add(seller)
158
167
 
159
168
  if not sellers:
@@ -165,9 +174,17 @@ class BuyerTools(Toolkit):
165
174
  response = json.dumps([seller.to_dict() for seller in sellers])
166
175
  # print("find_sellers_by_location: storing response in knowledge base")
167
176
  self._store_response_in_knowledge_base(response)
168
- # print(f"Found {len(sellers)} sellers near {location}")
177
+ self.logger.info("Found %d sellers", len(sellers))
169
178
  return response
170
179
 
180
+ def get_nostr_client(self) -> NostrClient:
181
+ """Get the Nostr client.
182
+
183
+ Returns:
184
+ NostrClient: Nostr client
185
+ """
186
+ return self._nostr_client
187
+
171
188
  def get_profile(self) -> str:
172
189
  """Get the Nostr profile of the buyer agent.
173
190
 
@@ -204,7 +221,7 @@ class BuyerTools(Toolkit):
204
221
  response = json.dumps([stall.as_json() for stall in stalls])
205
222
  self._store_response_in_knowledge_base(response)
206
223
  return response
207
- except Exception as e:
224
+ except RuntimeError as e:
208
225
  response = json.dumps({"status": "error", "message": str(e)})
209
226
  return response
210
227
 
@@ -234,7 +251,7 @@ class BuyerTools(Toolkit):
234
251
  response = json.dumps([product.to_dict() for product in products])
235
252
  self._store_response_in_knowledge_base(response)
236
253
  return response
237
- except Exception as e:
254
+ except RuntimeError as e:
238
255
  response = json.dumps({"status": "error", "message": str(e)})
239
256
  return response
240
257
 
@@ -265,7 +282,8 @@ class BuyerTools(Toolkit):
265
282
  def _refresh_sellers(self) -> None:
266
283
  """
267
284
  Internal fucntion to retrieve a new list of sellers from the Nostr relay.
268
- The old list is discarded and the new list only contains unique sellers currently stored at the relay.
285
+ The old list is discarded and the new list only contains unique sellers
286
+ currently stored at the relay.
269
287
 
270
288
  Returns:
271
289
  List[NostrProfile]: List of Nostr profiles of all sellers.
@@ -274,11 +292,7 @@ class BuyerTools(Toolkit):
274
292
  if len(sellers) == 0:
275
293
  self.logger.info("No sellers found")
276
294
  else:
277
- self.logger.info(f"Found {len(sellers)} sellers")
278
-
279
- # Print the locations of the sellers
280
- # for seller in sellers:
281
- # print(f"Seller {seller.get_name()} has locations {seller.get_locations()}")
295
+ self.logger.info("Found %d sellers", len(sellers))
282
296
 
283
297
  self.sellers = sellers
284
298
 
@@ -19,6 +19,7 @@ class BuyerTools(Toolkit):
19
19
  def find_seller_by_name(self, name: str) -> str: ...
20
20
  def find_seller_by_public_key(self, public_key: str) -> str: ...
21
21
  def find_sellers_by_location(self, location: str) -> str: ...
22
+ def get_nostr_client(self) -> NostrClient: ...
22
23
  def get_profile(self) -> str: ...
23
24
  def get_relay(self) -> str: ...
24
25
  def get_seller_stalls(self, public_key: str) -> str: ...
@@ -1,30 +1,36 @@
1
+ """
2
+ Module implementing the MerchantTools Toolkit for Agno agents.
3
+ """
4
+
1
5
  import json
2
6
  import logging
3
7
  import time
4
8
  from typing import Any, List, Optional, Tuple, Union
5
9
 
10
+ from pydantic import ConfigDict
11
+
6
12
  from agentstr.models import AgentProfile, MerchantProduct, MerchantStall
7
13
  from agentstr.nostr import EventId, NostrClient
8
14
 
9
15
  try:
10
16
  from agno.tools import Toolkit
11
- except ImportError:
12
- raise ImportError("`agno` not installed. Please install using `pip install agno`")
13
-
14
- from pydantic import BaseModel, ConfigDict
17
+ except ImportError as exc:
18
+ raise ImportError(
19
+ "`agno` not installed. Please install using `pip install agno`"
20
+ ) from exc
15
21
 
16
22
 
17
23
  class MerchantTools(Toolkit):
18
24
  """
19
- MerchantTools is a toolkit that allows a merchant to publish products and stalls to Nostr.
25
+ MerchantTools is a toolkit that allows a merchant to publish
26
+ products and stalls to Nostr.
20
27
 
21
28
  TBD:
22
- - Better differentiation between products and stalls in the database and products and stalls published.
29
+ - Better differentiation between products and stalls in the database
30
+ and products and stalls published.
23
31
 
24
32
  """
25
33
 
26
- from pydantic import ConfigDict
27
-
28
34
  model_config = ConfigDict(
29
35
  arbitrary_types_allowed=True, extra="allow", validate_assignment=True
30
36
  )
@@ -77,6 +83,12 @@ class MerchantTools(Toolkit):
77
83
  self.register(self.remove_product_by_name)
78
84
  self.register(self.remove_stall_by_name)
79
85
 
86
+ def get_nostr_client(self) -> NostrClient:
87
+ """
88
+ Get the NostrClient instance
89
+ """
90
+ return self._nostr_client
91
+
80
92
  def get_profile(self) -> str:
81
93
  """
82
94
  Get the merchant profile in JSON format
@@ -147,8 +159,8 @@ class MerchantTools(Toolkit):
147
159
  )
148
160
  # Pause for 0.5 seconds to avoid rate limiting
149
161
  time.sleep(0.5)
150
- except Exception as e:
151
- logging.error(f"Unable to publish product {product}. Error {e}")
162
+ except RuntimeError as e:
163
+ logging.error("Unable to publish product %s. Error %s", product, e)
152
164
  results.append(
153
165
  {"status": "error", "message": str(e), "product_name": product.name}
154
166
  )
@@ -159,7 +171,8 @@ class MerchantTools(Toolkit):
159
171
  self,
160
172
  ) -> str:
161
173
  """
162
- Publishes or updates to Nostr all stalls managed by the merchant and adds the corresponding EventId to the Stall DB
174
+ Publishes or updates to Nostr all stalls managed by the merchant and adds
175
+ the corresponding EventId to the Stall DB
163
176
 
164
177
  Returns:
165
178
  str: JSON array with status of all stall publishing operations
@@ -186,8 +199,8 @@ class MerchantTools(Toolkit):
186
199
  )
187
200
  # Pause for 0.5 seconds to avoid rate limiting
188
201
  time.sleep(0.5)
189
- except Exception as e:
190
- logging.error(f"Unable to publish stall {stall}. Error {e}")
202
+ except RuntimeError as e:
203
+ logging.error("Unable to publish stall %s. Error %s", stall, e)
191
204
  results.append(
192
205
  {"status": "error", "message": str(e), "stall_name": stall.name}
193
206
  )
@@ -196,7 +209,8 @@ class MerchantTools(Toolkit):
196
209
 
197
210
  def publish_new_product(self, product: MerchantProduct) -> str:
198
211
  """
199
- Publishes to Nostra new product that is not currently in the Merchant's Product DB and adds it to the Product DB
212
+ Publishes to Nostra new product that is not currently in the Merchant's
213
+ Product DB and adds it to the Product DB
200
214
 
201
215
  Args:
202
216
  product: MerchantProduct to be published
@@ -224,7 +238,7 @@ class MerchantTools(Toolkit):
224
238
  "product_name": product.name,
225
239
  }
226
240
  )
227
- except Exception as e:
241
+ except RuntimeError as e:
228
242
  return json.dumps(
229
243
  {"status": "error", "message": str(e), "product_name": product.name}
230
244
  )
@@ -233,7 +247,8 @@ class MerchantTools(Toolkit):
233
247
  """
234
248
  Publishes or updates to Nostra given product from the Merchant's Product DB
235
249
  Args:
236
- arguments: JSON string that may contain {"name": "product_name"} or just "product_name"
250
+ arguments: JSON string that may contain
251
+ {"name": "product_name"} or just "product_name"
237
252
 
238
253
  Returns:
239
254
  str: JSON string with status of the operation
@@ -267,6 +282,8 @@ class MerchantTools(Toolkit):
267
282
  event_id = self._nostr_client.publish_product(product)
268
283
  # Update the product_db with the new event_id
269
284
  self.product_db[i] = (product, event_id)
285
+ # Pause for 0.5 seconds to avoid rate limiting
286
+ time.sleep(0.5)
270
287
  return json.dumps(
271
288
  {
272
289
  "status": "success",
@@ -274,9 +291,7 @@ class MerchantTools(Toolkit):
274
291
  "product_name": product.name,
275
292
  }
276
293
  )
277
- # Pause for 0.5 seconds to avoid rate limiting
278
- time.sleep(0.5)
279
- except Exception as e:
294
+ except RuntimeError as e:
280
295
  return json.dumps(
281
296
  {
282
297
  "status": "error",
@@ -376,7 +391,7 @@ class MerchantTools(Toolkit):
376
391
  )
377
392
  # Pause for 0.5 seconds to avoid rate limiting
378
393
  time.sleep(0.5)
379
- except Exception as e:
394
+ except RuntimeError as e:
380
395
  results.append(
381
396
  {
382
397
  "status": "error",
@@ -399,7 +414,7 @@ class MerchantTools(Toolkit):
399
414
 
400
415
  return json.dumps(results)
401
416
 
402
- except Exception as e:
417
+ except RuntimeError as e:
403
418
  return json.dumps(
404
419
  [{"status": "error", "message": str(e), "arguments": str(arguments)}]
405
420
  )
@@ -424,12 +439,13 @@ class MerchantTools(Toolkit):
424
439
  self.merchant_profile.get_picture(),
425
440
  )
426
441
  return json.dumps(event_id.__dict__)
427
- except Exception as e:
428
- raise RuntimeError(f"Unable to publish the profile: {e}")
442
+ except RuntimeError as e:
443
+ raise RuntimeError(f"Unable to publish the profile: {e}") from e
429
444
 
430
445
  def publish_new_stall(self, stall: MerchantStall) -> str:
431
446
  """
432
- Publishes to Nostr a new stall that is not currently in the Merchant's Stall DB and adds it to the Stall DB
447
+ Publishes to Nostr a new stall that is not currently in the Merchant's
448
+ Stall DB and adds it to the Stall DB
433
449
 
434
450
  Args:
435
451
  stall: MerchantStall to be published
@@ -444,7 +460,8 @@ class MerchantTools(Toolkit):
444
460
  raise ValueError("NostrClient not initialized")
445
461
 
446
462
  try:
447
- # We don't ned to convert to StallData. nostr_client.publish_stall() accepts a MerchantStall
463
+ # We don't ned to convert to StallData.
464
+ # nostr_client.publish_stall() accepts a MerchantStall
448
465
  # stall_data = stall.to_stall_data()
449
466
  # Publish using the synchronous method
450
467
  event_id = self._nostr_client.publish_stall(stall)
@@ -457,7 +474,7 @@ class MerchantTools(Toolkit):
457
474
  "stall_name": stall.name,
458
475
  }
459
476
  )
460
- except Exception as e:
477
+ except RuntimeError as e:
461
478
  return json.dumps(
462
479
  {"status": "error", "message": str(e), "stall_name": stall.name}
463
480
  )
@@ -514,10 +531,10 @@ class MerchantTools(Toolkit):
514
531
  for i, (stall, _) in enumerate(self.stall_db):
515
532
  if stall.name == stall_name:
516
533
  try:
517
- # We are not passing StallData to nostr_client.publish_stall() anymore
518
- # stall_data = stall.to_stall_data()
519
534
  event_id = self._nostr_client.publish_stall(stall)
520
535
  self.stall_db[i] = (stall, event_id)
536
+ # Pause for 0.5 seconds to avoid rate limiting
537
+ time.sleep(0.5)
521
538
  return json.dumps(
522
539
  {
523
540
  "status": "success",
@@ -525,9 +542,8 @@ class MerchantTools(Toolkit):
525
542
  "stall_name": stall.name,
526
543
  }
527
544
  )
528
- # Pause for 0.5 seconds to avoid rate limiting
529
- time.sleep(0.5)
530
- except Exception as e:
545
+
546
+ except RuntimeError as e:
531
547
  return json.dumps(
532
548
  [
533
549
  {
@@ -549,7 +565,7 @@ class MerchantTools(Toolkit):
549
565
  ]
550
566
  )
551
567
 
552
- except Exception as e:
568
+ except RuntimeError as e:
553
569
  return json.dumps(
554
570
  [{"status": "error", "message": str(e), "stall_name": "unknown"}]
555
571
  )
@@ -574,7 +590,9 @@ class MerchantTools(Toolkit):
574
590
  results.append(
575
591
  {
576
592
  "status": "skipped",
577
- "message": f"Product '{product.name}' has not been published yet",
593
+ "message": (
594
+ f"Product '{product.name}' has not been published yet"
595
+ ),
578
596
  "product_name": product.name,
579
597
  }
580
598
  )
@@ -597,7 +615,7 @@ class MerchantTools(Toolkit):
597
615
  )
598
616
  # Pause for 0.5 seconds to avoid rate limiting
599
617
  time.sleep(0.5)
600
- except Exception as e:
618
+ except RuntimeError as e:
601
619
  results.append(
602
620
  {"status": "error", "message": str(e), "product_name": product.name}
603
621
  )
@@ -606,7 +624,8 @@ class MerchantTools(Toolkit):
606
624
 
607
625
  def remove_all_stalls(self) -> str:
608
626
  """
609
- Removes from Nostr all stalls from the merchant and their corresponding products
627
+ Removes from Nostr all stalls from the merchant and their
628
+ corresponding products
610
629
 
611
630
  Returns:
612
631
  str: JSON array with status of all removal operations
@@ -631,7 +650,7 @@ class MerchantTools(Toolkit):
631
650
  results.append(
632
651
  {
633
652
  "status": "skipped",
634
- "message": f"Product '{product.name}' has not been published yet",
653
+ "message": "Unpublished product",
635
654
  "product_name": product.name,
636
655
  "stall_name": stall_name,
637
656
  }
@@ -653,7 +672,7 @@ class MerchantTools(Toolkit):
653
672
  "event_id": str(event_id),
654
673
  }
655
674
  )
656
- except Exception as e:
675
+ except RuntimeError as e:
657
676
  results.append(
658
677
  {
659
678
  "status": "error",
@@ -669,7 +688,7 @@ class MerchantTools(Toolkit):
669
688
  results.append(
670
689
  {
671
690
  "status": "skipped",
672
- "message": f"Stall '{stall_name}' has not been published yet",
691
+ "message": (f"Stall '{stall_name}' has not been published yet"),
673
692
  "stall_name": stall_name,
674
693
  }
675
694
  )
@@ -689,7 +708,7 @@ class MerchantTools(Toolkit):
689
708
  )
690
709
  # Pause for 0.5 seconds to avoid rate limiting
691
710
  time.sleep(0.5)
692
- except Exception as e:
711
+ except RuntimeError as e:
693
712
  results.append(
694
713
  {"status": "error", "message": str(e), "stall_name": stall_name}
695
714
  )
@@ -701,7 +720,8 @@ class MerchantTools(Toolkit):
701
720
  Removes from Nostr a product with the given name
702
721
 
703
722
  Args:
704
- arguments: JSON string that may contain {"name": "product_name"} or just "product_name"
723
+ arguments: JSON string that may contain {"name": "product_name"}
724
+ or just "product_name"
705
725
 
706
726
  Returns:
707
727
  str: JSON string with status of the operation
@@ -754,7 +774,7 @@ class MerchantTools(Toolkit):
754
774
  "event_id": str(event_id),
755
775
  }
756
776
  )
757
- except Exception as e:
777
+ except RuntimeError as e:
758
778
  return json.dumps(
759
779
  {"status": "error", "message": str(e), "product_name": name}
760
780
  )
@@ -843,7 +863,7 @@ class MerchantTools(Toolkit):
843
863
  results.append(
844
864
  {
845
865
  "status": "skipped",
846
- "message": f"Product '{product.name}' has not been published yet",
866
+ "message": "Unpublished product",
847
867
  "product_name": product.name,
848
868
  "stall_name": stall_name,
849
869
  }
@@ -866,7 +886,7 @@ class MerchantTools(Toolkit):
866
886
  )
867
887
  # Pause for 0.5 seconds to avoid rate limiting
868
888
  time.sleep(0.5)
869
- except Exception as e:
889
+ except RuntimeError as e:
870
890
  results.append(
871
891
  {
872
892
  "status": "error",
@@ -883,7 +903,9 @@ class MerchantTools(Toolkit):
883
903
  results.append(
884
904
  {
885
905
  "status": "skipped",
886
- "message": f"Stall '{stall_name}' has not been published yet",
906
+ "message": (
907
+ f"Stall '{stall_name}' has not been published yet"
908
+ ),
887
909
  "stall_name": stall_name,
888
910
  }
889
911
  )
@@ -904,7 +926,7 @@ class MerchantTools(Toolkit):
904
926
  "event_id": str(stall_event_id),
905
927
  }
906
928
  )
907
- except Exception as e:
929
+ except RuntimeError as e:
908
930
  results.append(
909
931
  {
910
932
  "status": "error",
@@ -915,20 +937,7 @@ class MerchantTools(Toolkit):
915
937
 
916
938
  return json.dumps(results)
917
939
 
918
- except Exception as e:
940
+ except RuntimeError as e:
919
941
  return json.dumps(
920
942
  [{"status": "error", "message": str(e), "stall_name": "unknown"}]
921
943
  )
922
-
923
- # def get_event_id(self, response: Any) -> str:
924
- # """Convert any response to a string event ID.
925
-
926
- # Args:
927
- # response: Response that might contain an event ID
928
-
929
- # Returns:
930
- # str: String representation of event ID or empty string if None
931
- # """
932
- # if response is None:
933
- # return ""
934
- # return str(response)
@@ -18,6 +18,7 @@ class MerchantTools(Toolkit):
18
18
  stalls: List[MerchantStall],
19
19
  products: List[MerchantProduct],
20
20
  ) -> None: ...
21
+ def get_nostr_client(self) -> NostrClient: ...
21
22
  def get_profile(self) -> str: ...
22
23
  def get_relay(self) -> str: ...
23
24
  def get_products(self) -> str: ...
@@ -34,4 +35,3 @@ class MerchantTools(Toolkit):
34
35
  def remove_all_stalls(self) -> str: ...
35
36
  def remove_product_by_name(self, product_name: str) -> str: ...
36
37
  def remove_stall_by_name(self, stall_name: str) -> str: ...
37
- def get_event_id(self, response: Any) -> str: ...