agentstr 0.1.12__py3-none-any.whl → 0.1.14__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
agentstr/__init__.py CHANGED
@@ -2,8 +2,14 @@
2
2
  AgentStr: Nostr extension for Agno AI agents
3
3
  """
4
4
 
5
- from nostr_sdk import ShippingCost, ShippingMethod # type: ignore
5
+ import importlib.metadata
6
+ import logging
6
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
7
13
  from .merchant import MerchantTools
8
14
 
9
15
  # Import main classes to make them available at package level
@@ -11,27 +17,33 @@ from .models import AgentProfile, MerchantProduct, MerchantStall, NostrProfile
11
17
 
12
18
  # Import version from pyproject.toml at runtime
13
19
  try:
14
- from importlib.metadata import version
15
-
16
- __version__ = version("agentstr")
17
- except Exception:
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'.")
18
26
  __version__ = "unknown"
19
27
 
28
+ # Define What is Exposed at the Package Level
20
29
  __all__ = [
30
+ # Merchant Tools
21
31
  "MerchantTools",
22
32
  "MerchantProduct",
23
33
  "MerchantStall",
34
+ # Buyer Tools
35
+ "BuyerTools",
36
+ # Shipping
24
37
  "ShippingCost",
25
38
  "ShippingMethod",
26
- ]
27
-
28
- from agentstr.nostr import EventId, Keys, NostrClient, ProductData, StallData
29
-
30
- __all__ = [
39
+ # Nostr-related utils
31
40
  "EventId",
32
41
  "Keys",
42
+ "Kind",
33
43
  "NostrClient",
34
- "ProductData",
35
- "StallData",
44
+ "generate_and_save_keys",
45
+ "Timestamp",
46
+ # Models
36
47
  "AgentProfile",
48
+ "NostrProfile",
37
49
  ]
agentstr/buyer.py CHANGED
@@ -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
 
agentstr/buyer.pyi CHANGED
@@ -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: ...
agentstr/merchant.py CHANGED
@@ -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)
agentstr/merchant.pyi CHANGED
@@ -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: ...
agentstr/models.py CHANGED
@@ -1,6 +1,6 @@
1
1
  import json
2
2
  import logging
3
- from typing import List, Optional
3
+ from typing import List
4
4
 
5
5
  from nostr_sdk import (
6
6
  Keys,
@@ -131,9 +131,6 @@ class NostrProfile(Profile):
131
131
  def get_profile_url(self) -> str:
132
132
  return self.profile_url
133
133
 
134
- def get_zip_codes(self) -> List[str]:
135
- return self.zip_codes
136
-
137
134
  def to_json(self) -> str:
138
135
  # Ensure super().to_json() returns a dictionary
139
136
  parent_json = super().to_json()
@@ -287,14 +284,15 @@ class MerchantProduct(BaseModel):
287
284
  specs=self.specs,
288
285
  )
289
286
  except Exception as e:
290
- logging.error(f"Failed to convert to ProductData: {e}")
291
- logging.error(f"Shipping data: {self.shipping}")
287
+ logging.error("Failed to convert to ProductData: %s", e)
288
+ logging.error("Shipping data: %s", self.shipping)
292
289
  raise
293
290
 
294
291
  def to_dict(self) -> dict:
295
292
  """
296
293
  Returns a dictionary representation of the MerchantProduct.
297
- ShippingCost class is not serializable, so we need to convert it to a dictionary.
294
+ ShippingCost class is not serializable, so we need to convert it
295
+ to a dictionary.
298
296
 
299
297
  Returns:
300
298
  dict: dictionary representation of the MerchantProduct
@@ -347,8 +345,9 @@ class MerchantStall(BaseModel):
347
345
  def to_dict(self) -> dict:
348
346
  """
349
347
  Returns a dictionary representation of the MerchantStall.
350
- ShippingMethod class is not serializable, so we need to convert it to a dictionary.
351
- We can only access cost and id from the ShippingMethod class. We can't access name or regions.
348
+ ShippingMethod class is not serializable, so we need to convert
349
+ it to a dictionary. We can only access cost and id from the
350
+ ShippingMethod class. We can't access name or regions.
352
351
 
353
352
  Returns:
354
353
  dict: dictionary representation of the MerchantStall
agentstr/models.pyi CHANGED
@@ -1,5 +1,5 @@
1
1
  from logging import Logger
2
- from typing import ClassVar, List, Optional, Set
2
+ from typing import ClassVar, List, Set
3
3
 
4
4
  from nostr_sdk import (
5
5
  Keys,
agentstr/nostr.py CHANGED
@@ -1,19 +1,16 @@
1
+ """
2
+ Core Nostr utilities for agentstr.
3
+ """
4
+
5
+ import asyncio
1
6
  import json
2
7
  import logging
3
- import traceback
4
8
  from datetime import timedelta
5
9
  from pathlib import Path
6
- from typing import Dict, List, Optional, Tuple
10
+ from typing import Dict, List, Optional
7
11
 
8
12
  from agentstr.models import MerchantProduct, MerchantStall, NostrProfile
9
13
 
10
- try:
11
- import asyncio
12
- except ImportError:
13
- raise ImportError(
14
- "`asyncio` not installed. Please install using `pip install asyncio`"
15
- )
16
-
17
14
  try:
18
15
  from nostr_sdk import (
19
16
  Alphabet,
@@ -29,29 +26,26 @@ try:
29
26
  NostrSigner,
30
27
  ProductData,
31
28
  PublicKey,
32
- ShippingCost,
33
- ShippingMethod,
34
29
  SingleLetterTag,
35
30
  StallData,
36
31
  Tag,
37
32
  TagKind,
38
33
  TagStandard,
39
- Timestamp,
40
34
  )
41
-
42
- except ImportError:
35
+ except ImportError as exc:
43
36
  raise ImportError(
44
37
  "`nostr_sdk` not installed. Please install using `pip install nostr_sdk`"
45
- )
38
+ ) from exc
46
39
 
47
40
 
48
41
  class NostrClient:
49
42
  """
50
- NostrClient implements the set of Nostr utilities required for higher level functions implementing
51
- like the Marketplace.
43
+ NostrClient implements the set of Nostr utilities required for
44
+ higher level functions implementations like the Marketplace.
52
45
 
53
- Nostr is an asynchronous communication protocol. To hide this, NostrClient exposes synchronous functions.
54
- Users of the NostrClient should ignore `_async_` functions which are for internal purposes only.
46
+ Nostr is an asynchronous communication protocol. To hide this,
47
+ NostrClient exposes synchronous functions. Users of the NostrClient
48
+ should ignore `_async_` functions which are for internal purposes only.
55
49
  """
56
50
 
57
51
  logger = logging.getLogger("NostrClient")
@@ -148,7 +142,7 @@ class NostrClient:
148
142
  try:
149
143
  return asyncio.run(self._async_publish_product(product))
150
144
  except Exception as e:
151
- raise RuntimeError(f"Failed to publish product: {e}")
145
+ raise RuntimeError(f"Failed to publish product: {e}") from e
152
146
 
153
147
  def publish_profile(self, name: str, about: str, picture: str) -> EventId:
154
148
  """
@@ -183,7 +177,7 @@ class NostrClient:
183
177
  try:
184
178
  return asyncio.run(self._async_publish_stall(stall))
185
179
  except Exception as e:
186
- raise RuntimeError(f"Failed to publish stall: {e}")
180
+ raise RuntimeError(f"Failed to publish stall: {e}") from e
187
181
 
188
182
  def retrieve_products_from_seller(self, seller: PublicKey) -> List[MerchantProduct]:
189
183
  """
@@ -217,7 +211,7 @@ class NostrClient:
217
211
  products.append(MerchantProduct.from_product_data(product_data))
218
212
  return products
219
213
  except Exception as e:
220
- raise RuntimeError(f"Failed to retrieve products: {e}")
214
+ raise RuntimeError(f"Failed to retrieve products: {e}") from e
221
215
 
222
216
  def retrieve_profile(self, public_key: PublicKey) -> NostrProfile:
223
217
  """
@@ -235,7 +229,7 @@ class NostrClient:
235
229
  try:
236
230
  return asyncio.run(self._async_retrieve_profile(public_key))
237
231
  except Exception as e:
238
- raise RuntimeError(f"Failed to retrieve profile: {e}")
232
+ raise RuntimeError(f"Failed to retrieve profile: {e}") from e
239
233
 
240
234
  def retrieve_sellers(self) -> set[NostrProfile]:
241
235
  """
@@ -244,17 +238,18 @@ class NostrClient:
244
238
  Return set may be empty if metadata can't be retrieved for any author.
245
239
 
246
240
  Returns:
247
- set[NostrProfile]: set of seller profiles (skips authors with missing metadata)
241
+ set[NostrProfile]: set of seller profiles
242
+ (skips authors with missing metadata)
248
243
  """
249
244
 
250
- sellers: set[NostrProfile] = set()
245
+ # sellers: set[NostrProfile] = set()
251
246
 
252
247
  # First we retrieve all stalls from the relay
253
248
 
254
249
  try:
255
250
  events = asyncio.run(self._async_retrieve_all_stalls())
256
251
  except Exception as e:
257
- raise RuntimeError(f"Failed to retrieve stalls: {e}")
252
+ raise RuntimeError(f"Failed to retrieve stalls: {e}") from e
258
253
 
259
254
  # Now we search for unique npubs from the list of stalls
260
255
 
@@ -265,17 +260,16 @@ class NostrClient:
265
260
  if event.kind() == Kind(30017):
266
261
  # Is this event the first time we see this author?
267
262
  if event.author() not in authors:
268
- # First time we see this author. Let's add the profile to the dictionary
263
+ # First time we see this author.
264
+ # Let's add the profile to the dictionary
269
265
  try:
270
266
  profile = asyncio.run(
271
267
  self._async_retrieve_profile(event.author())
272
268
  )
273
- # Add the profile to the dictionary, associating it with the author's PublicKey
269
+ # Add profile to the dictionary
270
+ # associated with the author's PublicKey
274
271
  authors[event.author()] = profile
275
- except Exception as e:
276
- # print(
277
- # f"Failed to retrieve profile for {event.author().to_bech32()}: {e}"
278
- # )
272
+ except RuntimeError:
279
273
  continue
280
274
 
281
275
  # Now we add locations from the event locations to the profile
@@ -287,15 +281,10 @@ class NostrClient:
287
281
  extracted_geohash = string_repr.split("=")[1].rstrip(
288
282
  ")"
289
283
  ) # Splitting and removing the closing parenthesis
290
- # print(
291
- # f"Adding location {extracted_geohash} to profile {authors[event.author()].get_name()}"
292
- # )
284
+
293
285
  profile = authors[event.author()]
294
286
  profile.add_location(extracted_geohash)
295
287
  authors[event.author()] = profile
296
- # print(
297
- # f"New locations for {authors[event.author()].get_name()}: {authors[event.author()].get_locations()}"
298
- # )
299
288
  # else:
300
289
  # print(f"Unknown tag: {standardized_tag}")
301
290
 
@@ -311,6 +300,9 @@ class NostrClient:
311
300
 
312
301
  Returns:
313
302
  List[StallData]: list of stalls from the seller
303
+
304
+ Raises:
305
+ RuntimeError: if the stalls can't be retrieved
314
306
  """
315
307
  stalls = []
316
308
  try:
@@ -322,12 +314,12 @@ class NostrClient:
322
314
  content = event.content()
323
315
  stall = StallData.from_json(content)
324
316
  stalls.append(stall)
325
- except Exception as e:
326
- self.logger.warning(f"Failed to parse stall data: {e}")
317
+ except RuntimeError as e:
318
+ self.logger.warning("Failed to parse stall data: %s", e)
327
319
  continue
328
320
  return stalls
329
321
  except Exception as e:
330
- raise RuntimeError(f"Failed to retrieve stalls: {e}")
322
+ raise RuntimeError(f"Failed to retrieve stalls: {e}") from e
331
323
 
332
324
  @classmethod
333
325
  def set_logging_level(cls, logging_level: int) -> None:
@@ -339,15 +331,18 @@ class NostrClient:
339
331
  cls.logger.setLevel(logging_level)
340
332
  for handler in cls.logger.handlers:
341
333
  handler.setLevel(logging_level)
342
- cls.logger.info(f"Logging level set to {logging.getLevelName(logging_level)}")
334
+ cls.logger.info("Logging level set to %s", logging.getLevelName(logging_level))
343
335
 
344
- # ----------------------------------------------------------------------------------------------
345
- # --*-- async functions for internal use only. Developers should use synchronous functions above
346
- # ----------------------------------------------------------------------------------------------
336
+ # ----------------------------------------------------------------
337
+ # internal async functions.
338
+ # Developers should use synchronous functions above
339
+ # ----------------------------------------------------------------
347
340
 
348
341
  async def _async_connect(self) -> None:
349
- """Asynchronous function to add relay to the NostrClient instance and connect to it.
350
- TBD: refactor to not return anything if successful and raise an exception if not
342
+ """
343
+ Asynchronous function to add relay to the NostrClient
344
+ instance and connect to it.
345
+
351
346
 
352
347
  Raises:
353
348
  RuntimeError: if the relay can't be connected to
@@ -356,7 +351,7 @@ class NostrClient:
356
351
  if not self.connected:
357
352
  try:
358
353
  await self.client.add_relay(self.relay)
359
- NostrClient.logger.info(f"Relay {self.relay} succesfully added.")
354
+ NostrClient.logger.info("Relay %s successfully added.", self.relay)
360
355
  await self.client.connect()
361
356
  await asyncio.sleep(2) # give time for slower connections
362
357
  NostrClient.logger.info("Connected to relay.")
@@ -364,7 +359,7 @@ class NostrClient:
364
359
  except Exception as e:
365
360
  raise RuntimeError(
366
361
  f"Unable to connect to relay {self.relay}. Exception: {e}."
367
- )
362
+ ) from e
368
363
 
369
364
  async def _async_publish_event(self, event_builder: EventBuilder) -> EventId:
370
365
  """
@@ -380,9 +375,9 @@ class NostrClient:
380
375
  await self._async_connect()
381
376
 
382
377
  # Add debug logging
383
- NostrClient.logger.debug(f"Attempting to publish event: {event_builder}")
378
+ NostrClient.logger.debug("Attempting to publish event: %s", event_builder)
384
379
  NostrClient.logger.debug(
385
- f"Using keys: {self.keys.public_key().to_bech32()}"
380
+ "Using keys: %s", self.keys.public_key().to_bech32()
386
381
  )
387
382
 
388
383
  # Wait for connection and try to publish
@@ -392,19 +387,18 @@ class NostrClient:
392
387
  if not output:
393
388
  raise RuntimeError("No output received from send_event_builder")
394
389
  if len(output.success) == 0:
395
- raise RuntimeError(
396
- f"Event rejected by relay. Reason: {output.message if hasattr(output, 'message') else 'unknown'}"
397
- )
390
+ reason = getattr(output, "message", "unknown")
391
+ raise RuntimeError(f"Event rejected by relay. Reason: {reason}")
398
392
 
399
393
  NostrClient.logger.info(
400
- f"Event published with event id: {output.id.to_bech32()}"
394
+ "Event published with event id: %s", output.id.to_bech32()
401
395
  )
402
396
  return output.id
403
397
 
404
398
  except Exception as e:
405
- NostrClient.logger.error(f"Failed to publish event: {str(e)}")
399
+ NostrClient.logger.error("Failed to publish event: %s", str(e))
406
400
  NostrClient.logger.debug("Event details:", exc_info=True)
407
- raise RuntimeError(f"Unable to publish event: {str(e)}")
401
+ raise RuntimeError(f"Unable to publish event: {str(e)}") from e
408
402
 
409
403
  async def _async_publish_note(self, text: str) -> EventId:
410
404
  """
@@ -424,7 +418,8 @@ class NostrClient:
424
418
 
425
419
  async def _async_publish_product(self, product: MerchantProduct) -> EventId:
426
420
  """
427
- Asynchronous function to create or update a NIP-15 Marketplace product with event kind 30018
421
+ Asynchronous function to create or update a NIP-15
422
+ Marketplace product with event kind 30018
428
423
 
429
424
  Args:
430
425
  product: product to publish
@@ -443,7 +438,8 @@ class NostrClient:
443
438
  # We use the function to create the content field and discard the eventbuilder
444
439
  bad_event_builder = EventBuilder.product_data(product.to_product_data())
445
440
 
446
- # create an event from bad_event_builder to extract the content - not broadcasted
441
+ # create an event from bad_event_builder to extract the content -
442
+ # not broadcasted
447
443
  bad_event = await self.client.sign_event_builder(bad_event_builder)
448
444
  content = bad_event.content()
449
445
 
@@ -451,7 +447,7 @@ class NostrClient:
451
447
  good_event_builder = EventBuilder(Kind(30018), content).tags(
452
448
  [Tag.identifier(product.id), Tag.coordinate(coordinate_tag)]
453
449
  )
454
- self.logger.info("Product event: " + str(good_event_builder))
450
+ NostrClient.logger.info("Product event: %s", good_event_builder)
455
451
  return await self._async_publish_event(good_event_builder)
456
452
 
457
453
  async def _async_publish_profile(
@@ -480,7 +476,8 @@ class NostrClient:
480
476
 
481
477
  async def _async_publish_stall(self, stall: MerchantStall) -> EventId:
482
478
  """
483
- Asynchronous function to create or update a NIP-15 Marketplace stall with event kind 30017
479
+ Asynchronous function to create or update a NIP-15
480
+ Marketplace stall with event kind 30017
484
481
 
485
482
  Args:
486
483
  stall: stall to be published
@@ -496,7 +493,7 @@ class NostrClient:
496
493
  # [Tag.identifier(product.id), Tag.coordinate(coordinate_tag)]
497
494
  # )
498
495
 
499
- self.logger.info(f" Merchant Stall: {stall}")
496
+ NostrClient.logger.info("Merchant Stall: %s", stall)
500
497
  event_builder = EventBuilder.stall_data(stall.to_stall_data()).tags(
501
498
  [
502
499
  Tag.custom(
@@ -521,16 +518,16 @@ class NostrClient:
521
518
  try:
522
519
  await self._async_connect()
523
520
  except Exception as e:
524
- raise RuntimeError("Unable to connect to the relay")
521
+ raise RuntimeError("Unable to connect to the relay") from e
525
522
 
526
523
  try:
527
- filter = Filter().kind(Kind(30017))
524
+ events_filter = Filter().kind(Kind(30017))
528
525
  events = await self.client.fetch_events_from(
529
- urls=[self.relay], filter=filter, timeout=timedelta(seconds=2)
526
+ urls=[self.relay], filter=events_filter, timeout=timedelta(seconds=2)
530
527
  )
531
528
  return events
532
529
  except Exception as e:
533
- raise RuntimeError(f"Unable to retrieve stalls: {e}")
530
+ raise RuntimeError(f"Unable to retrieve stalls: {e}") from e
534
531
 
535
532
  async def _async_retrieve_products_from_seller(self, seller: PublicKey) -> Events:
536
533
  """
@@ -548,17 +545,17 @@ class NostrClient:
548
545
  try:
549
546
  await self._async_connect()
550
547
  except Exception as e:
551
- raise RuntimeError("Unable to connect to the relay")
548
+ raise RuntimeError("Unable to connect to the relay") from e
552
549
 
553
550
  try:
554
551
  # print(f"Retrieving products from seller: {seller}")
555
- filter = Filter().kind(Kind(30018)).authors([seller])
552
+ events_filter = Filter().kind(Kind(30018)).authors([seller])
556
553
  events = await self.client.fetch_events_from(
557
- urls=[self.relay], filter=filter, timeout=timedelta(seconds=2)
554
+ urls=[self.relay], filter=events_filter, timeout=timedelta(seconds=2)
558
555
  )
559
556
  return events
560
557
  except Exception as e:
561
- raise RuntimeError(f"Unable to retrieve stalls: {e}")
558
+ raise RuntimeError(f"Unable to retrieve stalls: {e}") from e
562
559
 
563
560
  async def _async_retrieve_profile(self, author: PublicKey) -> NostrProfile:
564
561
  """
@@ -576,7 +573,7 @@ class NostrClient:
576
573
  try:
577
574
  await self._async_connect()
578
575
  except Exception as e:
579
- raise RuntimeError("Unable to connect to the relay")
576
+ raise RuntimeError("Unable to connect to the relay") from e
580
577
 
581
578
  try:
582
579
  metadata = await self.client.fetch_metadata(
@@ -584,7 +581,7 @@ class NostrClient:
584
581
  )
585
582
  return NostrProfile.from_metadata(metadata, author)
586
583
  except Exception as e:
587
- raise RuntimeError(f"Unable to retrieve metadata: {e}")
584
+ raise RuntimeError(f"Unable to retrieve metadata: {e}") from e
588
585
 
589
586
  async def _async_retrieve_stalls_from_seller(self, seller: PublicKey) -> Events:
590
587
  """
@@ -602,16 +599,16 @@ class NostrClient:
602
599
  try:
603
600
  await self._async_connect()
604
601
  except Exception as e:
605
- raise RuntimeError("Unable to connect to the relay")
602
+ raise RuntimeError("Unable to connect to the relay") from e
606
603
 
607
604
  try:
608
- filter = Filter().kind(Kind(30017)).authors([seller])
605
+ events_filter = Filter().kind(Kind(30017)).authors([seller])
609
606
  events = await self.client.fetch_events_from(
610
- urls=[self.relay], filter=filter, timeout=timedelta(seconds=2)
607
+ urls=[self.relay], filter=events_filter, timeout=timedelta(seconds=2)
611
608
  )
612
609
  return events
613
610
  except Exception as e:
614
- raise RuntimeError(f"Unable to retrieve stalls: {e}")
611
+ raise RuntimeError(f"Unable to retrieve stalls: {e}") from e
615
612
 
616
613
 
617
614
  def generate_and_save_keys(env_var: str, env_path: Path) -> Keys:
@@ -635,7 +632,7 @@ def generate_and_save_keys(env_var: str, env_path: Path) -> Keys:
635
632
  # Read existing .env content
636
633
  env_content = ""
637
634
  if env_path.exists():
638
- with open(env_path, "r") as f:
635
+ with open(env_path, "r", encoding="utf-8") as f:
639
636
  env_content = f.read()
640
637
 
641
638
  # Check if the env var already exists
@@ -655,7 +652,7 @@ def generate_and_save_keys(env_var: str, env_path: Path) -> Keys:
655
652
  new_lines.append(f"{env_var}={nsec}")
656
653
 
657
654
  # Write back to .env
658
- with open(env_path, "w") as f:
655
+ with open(env_path, "w", encoding="utf-8") as f:
659
656
  f.write("\n".join(new_lines))
660
657
  if new_lines: # Add final newline if there's content
661
658
  f.write("\n")
agentstr/nostr.pyi CHANGED
@@ -1,3 +1,13 @@
1
+ """
2
+ Type stubs for the Nostr module.
3
+
4
+ This file provides type annotations for the Nostr module, enabling better
5
+ type checking and autocompletion in IDEs. It defines the expected types
6
+ for classes, functions, and variables used within the Nostr module.
7
+
8
+ Note: This is a type stub file and does not contain any executable code.
9
+ """
10
+
1
11
  from logging import Logger
2
12
  from pathlib import Path
3
13
  from typing import ClassVar, List, Optional
@@ -17,7 +27,6 @@ from nostr_sdk import ( # type: ignore
17
27
  ShippingCost,
18
28
  ShippingMethod,
19
29
  StallData,
20
- Tag,
21
30
  Timestamp,
22
31
  )
23
32
 
@@ -41,6 +50,11 @@ __all__ = [
41
50
  ]
42
51
 
43
52
  class NostrClient:
53
+ """
54
+ NostrClient implements the set of Nostr utilities required for higher level functions
55
+ implementations like the Marketplace.
56
+ """
57
+
44
58
  logger: ClassVar[Logger]
45
59
  relay: str
46
60
  keys: Keys
@@ -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
 
@@ -0,0 +1,15 @@
1
+ agentstr/__init__.py,sha256=rbFtFOldBSbeDX_8Xva3YHRC9c3nPFA9mJXS3UZhJ4s,1294
2
+ agentstr/buyer.py,sha256=I1tApMjlq0Bg5Qk7twcAbF2rf_L5I2L9qzQO5sKv2RE,9912
3
+ agentstr/buyer.pyi,sha256=AqdKa7nQWohxHaoKHjCNpOpDB6rsWGWAlASNhwEw45g,1219
4
+ agentstr/merchant.py,sha256=3Fz4hrxyb5m7Kk47RodNC-Vyjm9iV7bI4ncPF8EMkhw,35078
5
+ agentstr/merchant.pyi,sha256=mqak--H7D_b7o8JNQlQRmov2An-defyBGRJNhMNchXQ,1437
6
+ agentstr/models.py,sha256=lEoopEQYIqI0egVxoXhBLlBh35C0WQmR_oBKm5Sexwk,11423
7
+ agentstr/models.pyi,sha256=k1_D-afE17gybHhtQMqA6cx7H5lYh7Fg0gbXclxyP4A,2857
8
+ agentstr/nostr.py,sha256=u2jDwQDmg01UCThDy1WW9lOPvAHyI1WMbtxFFi_2q08,22703
9
+ agentstr/nostr.pyi,sha256=nlBdOOI6vPboACjBvrQZKHy3BtCjboaClG9ZVD2X8XQ,3118
10
+ agentstr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ agentstr-0.1.14.dist-info/LICENSE,sha256=20H0yoEDN5XO1xPXyZCyJjvSTP0YiarRMKWPfiaBhQY,1063
12
+ agentstr-0.1.14.dist-info/METADATA,sha256=FV-_lJhW2H7K66AinZpTUB0o_kIjh0mBXS0Y7khKtkE,4667
13
+ agentstr-0.1.14.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
14
+ agentstr-0.1.14.dist-info/top_level.txt,sha256=KZObFRHppZvKUGYB_m9w5HhLwps7jj7w6Xrw73dH2ss,9
15
+ agentstr-0.1.14.dist-info/RECORD,,
@@ -1,15 +0,0 @@
1
- agentstr/__init__.py,sha256=t2nKJcQ0cq3uYc5sadpJGXSO08UViAEfifCQE9pa068,800
2
- agentstr/buyer.py,sha256=rifdCC4FSmbLimA94AhSp3UaCA5vxtH61j_KgVJwkVo,9848
3
- agentstr/buyer.pyi,sha256=Zs2SbItabztcGg_cJF8_5Mf-p7Me34hxoFHEkC1inDU,1168
4
- agentstr/merchant.py,sha256=Uf3LkV5HlyuOZBA-covkUnDYaxI91yHvmVYOD8kk4cM,35225
5
- agentstr/merchant.pyi,sha256=bAYP8qz9GTGNeyFEkZsc4CqdCsC2HV4nrV0dWyYu1VY,1440
6
- agentstr/models.py,sha256=-anXkTmOANTKw-elxKG-RZCarCGBIWlUHiUoMBrE3FQ,11488
7
- agentstr/models.pyi,sha256=QVnjv01cuMynOjzuEcct72yO-LUWpdxEeiEzcf7QTl4,2867
8
- agentstr/nostr.py,sha256=VSC5gDN8XM54BSlfQ0yYv2N3DdTVVviWg5VJkJOD5R0,23199
9
- agentstr/nostr.pyi,sha256=A-Dq0ZVuZuXEXnMfgVhKRym4ubFnsSjFrgH1Db1JMCI,2647
10
- agentstr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- agentstr-0.1.12.dist-info/LICENSE,sha256=20H0yoEDN5XO1xPXyZCyJjvSTP0YiarRMKWPfiaBhQY,1063
12
- agentstr-0.1.12.dist-info/METADATA,sha256=idnYX1pNr1qsbQXDOTdHmmPBEy3pa9RLdinZn6PeIsE,4475
13
- agentstr-0.1.12.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
14
- agentstr-0.1.12.dist-info/top_level.txt,sha256=KZObFRHppZvKUGYB_m9w5HhLwps7jj7w6Xrw73dH2ss,9
15
- agentstr-0.1.12.dist-info/RECORD,,