agentstr 0.1.10__tar.gz → 0.1.12__tar.gz

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.
Files changed (36) hide show
  1. agentstr-0.1.12/MANIFEST.in +3 -0
  2. agentstr-0.1.12/PKG-INFO +129 -0
  3. agentstr-0.1.12/README.md +101 -0
  4. {agentstr-0.1.10 → agentstr-0.1.12}/pyproject.toml +24 -13
  5. agentstr-0.1.12/src/agentstr/__init__.py +37 -0
  6. agentstr-0.1.12/src/agentstr/buyer.py +291 -0
  7. agentstr-0.1.12/src/agentstr/buyer.pyi +31 -0
  8. agentstr-0.1.10/src/agentstr/marketplace.py → agentstr-0.1.12/src/agentstr/merchant.py +126 -323
  9. agentstr-0.1.12/src/agentstr/merchant.pyi +37 -0
  10. agentstr-0.1.12/src/agentstr/models.py +381 -0
  11. agentstr-0.1.12/src/agentstr/models.pyi +103 -0
  12. agentstr-0.1.12/src/agentstr/nostr.py +663 -0
  13. agentstr-0.1.12/src/agentstr/nostr.pyi +82 -0
  14. agentstr-0.1.12/src/agentstr/py.typed +0 -0
  15. agentstr-0.1.12/src/agentstr.egg-info/PKG-INFO +129 -0
  16. {agentstr-0.1.10 → agentstr-0.1.12}/src/agentstr.egg-info/SOURCES.txt +11 -5
  17. {agentstr-0.1.10 → agentstr-0.1.12}/src/agentstr.egg-info/requires.txt +5 -2
  18. agentstr-0.1.12/tests/test_buyer.py +91 -0
  19. agentstr-0.1.12/tests/test_merchant.py +138 -0
  20. agentstr-0.1.12/tests/test_nostr_integration.py +94 -0
  21. agentstr-0.1.12/tests/test_nostr_mocked.py +101 -0
  22. agentstr-0.1.10/MANIFEST.in +0 -5
  23. agentstr-0.1.10/PKG-INFO +0 -133
  24. agentstr-0.1.10/README.md +0 -88
  25. agentstr-0.1.10/src/agentstr/__init__.py +0 -30
  26. agentstr-0.1.10/src/agentstr/examples/basic_cli/.env.example +0 -2
  27. agentstr-0.1.10/src/agentstr/examples/basic_cli/README.md +0 -11
  28. agentstr-0.1.10/src/agentstr/examples/basic_cli/main.py +0 -193
  29. agentstr-0.1.10/src/agentstr/nostr.py +0 -327
  30. agentstr-0.1.10/src/agentstr.egg-info/PKG-INFO +0 -133
  31. agentstr-0.1.10/tests/test_merchant.py +0 -371
  32. agentstr-0.1.10/tests/test_nostr.py +0 -164
  33. {agentstr-0.1.10 → agentstr-0.1.12}/LICENSE +0 -0
  34. {agentstr-0.1.10 → agentstr-0.1.12}/setup.cfg +0 -0
  35. {agentstr-0.1.10 → agentstr-0.1.12}/src/agentstr.egg-info/dependency_links.txt +0 -0
  36. {agentstr-0.1.10 → agentstr-0.1.12}/src/agentstr.egg-info/top_level.txt +0 -0
@@ -0,0 +1,3 @@
1
+ include LICENSE README.md
2
+ global-exclude __pycache__
3
+ global-exclude *.py[cod]
@@ -0,0 +1,129 @@
1
+ Metadata-Version: 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>
6
+ License: MIT
7
+ Project-URL: Homepage, https://www.synvya.com
8
+ Project-URL: Repository, https://github.com/synvya/agentstr
9
+ Project-URL: Documentation, https://github.com/synvya/agentstr#readme
10
+ Project-URL: BugTracker, https://github.com/synvya/agentstr/issues
11
+ Requires-Python: <3.13,>=3.9
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: agno>=1.1.1
15
+ Requires-Dist: openai>=1.50.0
16
+ Requires-Dist: packaging>=24.0
17
+ Requires-Dist: nostr_sdk>=0.39.0
18
+ Requires-Dist: pydantic>=2.0.0
19
+ Requires-Dist: cassandra-driver>=3.29.2
20
+ Requires-Dist: cassio>=0.1.10
21
+ Provides-Extra: dev
22
+ Requires-Dist: pytest>=7.0; extra == "dev"
23
+ Requires-Dist: pytest-asyncio>=0.23.5; extra == "dev"
24
+ Requires-Dist: black>=23.0; extra == "dev"
25
+ Requires-Dist: isort>=5.0; extra == "dev"
26
+ Requires-Dist: mypy>=1.0; extra == "dev"
27
+ Requires-Dist: python-dotenv>=1.0; extra == "dev"
28
+
29
+ # AgentStr
30
+
31
+ AgentStr is an extension of [Agno](https://www.agno.ai) AI agents that enables peer-to-peer agent communication using the Nostr protocol.
32
+
33
+ ## Overview
34
+
35
+ AgentStr allows AI agents operated by different organizations to communicate and collaborate. For example:
36
+ - Agent A from Company A can coordinate with Agent B from Company B to execute a transaction
37
+ - Agents can discover and interact with each other through the decentralized Nostr network
38
+ - No central authority or intermediary required
39
+
40
+ ## Project Structure
41
+
42
+ ```
43
+ agentstr/
44
+ ├── src/ # Source code
45
+ │ └── agentstr/
46
+ │ ├── __init__.py
47
+ │ ├── buyer.py
48
+ │ ├── buyer.pyi
49
+ │ ├── merchant.py
50
+ │ ├── merchant.pyi
51
+ │ ├── models.py
52
+ │ ├── models.pyi
53
+ │ ├── nostr.py
54
+ │ ├── nostr.pyi
55
+ │ └── py.typed
56
+ ├── tests/ # Test files
57
+ ├── docs/ # Documentation
58
+ ├── examples/ # Example implementations
59
+ └── ...
60
+ ```
61
+
62
+ ## Features
63
+
64
+ ### Current Features
65
+ - Create Merchant agents with Nostr identities:
66
+ - Publish and manage merchant products using [NIP-15](https://github.com/nostr-protocol/nips/blob/master/15.md) marketplace protocol
67
+ - Create merchant stalls to organize products
68
+ - Handle shipping zones and costs
69
+ - Secure communication using Nostr keys
70
+ - Create Buyer agents:
71
+ - Retrieve a list of sellers from the relay using [NIP-15](https://github.com/nostr-protocol/nips/blob/master/15.md) marketplace protocol
72
+ - Find an specific seller by name or public key
73
+ - Refresh the list of sellers from the relay
74
+
75
+ ### Roadmap
76
+ - [ ] Create marketplace with stalls
77
+ - [ ] Expand buyer agent to include more features
78
+ - [ ] Support additional Nostr NIPs
79
+ - [ ] Add more agent interaction patterns
80
+
81
+ ## Installation
82
+
83
+ ```bash
84
+ # Create a new python environment
85
+ python3 -m venv ~/.venvs/aienv
86
+ source ~/.venvs/aienv/bin/activate
87
+
88
+ # Install agentstr
89
+ pip install --upgrade pip
90
+ pip install agentstr
91
+ ```
92
+
93
+ ## Examples
94
+
95
+ You can find example code in the [examples](https://github.com/Synvya/agentstr/tree/main/examples/) directory.
96
+
97
+ To install the examples clone the repository and navigate to the examples directory:
98
+
99
+ ```bash
100
+ git clone https://github.com/Synvya/agentstr.git
101
+ cd agentstr/examples/
102
+ ```
103
+ Each example has its own README with instructions on how to run it.
104
+
105
+ ## Documentation
106
+
107
+ For more detailed documentation and examples, see [Docs](https://github.com/Synvya/agentstr/tree/main/docs/docs.md)
108
+
109
+ ## Development
110
+
111
+ See [CONTRIBUTING.md](https://github.com/Synvya/agentstr/blob/main/CONTRIBUTING.md) for:
112
+ - Development setup
113
+ - Testing instructions
114
+ - Contribution guidelines
115
+
116
+ ## License
117
+
118
+ This project is licensed under the MIT License - see the [LICENSE](https://github.com/Synvya/agentstr/blob/main/LICENSE) file for details.
119
+
120
+ ## Acknowledgments
121
+
122
+ - [Agno](https://www.agno.ai) - For their AI agent framework
123
+ - [Rust-Nostr](https://rust-nostr.org) - For their Python Nostr SDK
124
+ - [Nostr Protocol](https://github.com/nostr-protocol/nips) - For the protocol specification
125
+
126
+ This software includes the following software licensed under the [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0):
127
+ - [DataStax Python Driver for Apache Cassandra](https://github.com/datastax/python-driver)
128
+ - [cassIO](https://github.com/CassioML/cassio). This library is not maintained anymore. We will need to replace it with a new library.
129
+
@@ -0,0 +1,101 @@
1
+ # AgentStr
2
+
3
+ AgentStr is an extension of [Agno](https://www.agno.ai) AI agents that enables peer-to-peer agent communication using the Nostr protocol.
4
+
5
+ ## Overview
6
+
7
+ AgentStr allows AI agents operated by different organizations to communicate and collaborate. For example:
8
+ - Agent A from Company A can coordinate with Agent B from Company B to execute a transaction
9
+ - Agents can discover and interact with each other through the decentralized Nostr network
10
+ - No central authority or intermediary required
11
+
12
+ ## Project Structure
13
+
14
+ ```
15
+ agentstr/
16
+ ├── src/ # Source code
17
+ │ └── agentstr/
18
+ │ ├── __init__.py
19
+ │ ├── buyer.py
20
+ │ ├── buyer.pyi
21
+ │ ├── merchant.py
22
+ │ ├── merchant.pyi
23
+ │ ├── models.py
24
+ │ ├── models.pyi
25
+ │ ├── nostr.py
26
+ │ ├── nostr.pyi
27
+ │ └── py.typed
28
+ ├── tests/ # Test files
29
+ ├── docs/ # Documentation
30
+ ├── examples/ # Example implementations
31
+ └── ...
32
+ ```
33
+
34
+ ## Features
35
+
36
+ ### Current Features
37
+ - Create Merchant agents with Nostr identities:
38
+ - Publish and manage merchant products using [NIP-15](https://github.com/nostr-protocol/nips/blob/master/15.md) marketplace protocol
39
+ - Create merchant stalls to organize products
40
+ - Handle shipping zones and costs
41
+ - Secure communication using Nostr keys
42
+ - Create Buyer agents:
43
+ - Retrieve a list of sellers from the relay using [NIP-15](https://github.com/nostr-protocol/nips/blob/master/15.md) marketplace protocol
44
+ - Find an specific seller by name or public key
45
+ - Refresh the list of sellers from the relay
46
+
47
+ ### Roadmap
48
+ - [ ] Create marketplace with stalls
49
+ - [ ] Expand buyer agent to include more features
50
+ - [ ] Support additional Nostr NIPs
51
+ - [ ] Add more agent interaction patterns
52
+
53
+ ## Installation
54
+
55
+ ```bash
56
+ # Create a new python environment
57
+ python3 -m venv ~/.venvs/aienv
58
+ source ~/.venvs/aienv/bin/activate
59
+
60
+ # Install agentstr
61
+ pip install --upgrade pip
62
+ pip install agentstr
63
+ ```
64
+
65
+ ## Examples
66
+
67
+ You can find example code in the [examples](https://github.com/Synvya/agentstr/tree/main/examples/) directory.
68
+
69
+ To install the examples clone the repository and navigate to the examples directory:
70
+
71
+ ```bash
72
+ git clone https://github.com/Synvya/agentstr.git
73
+ cd agentstr/examples/
74
+ ```
75
+ Each example has its own README with instructions on how to run it.
76
+
77
+ ## Documentation
78
+
79
+ For more detailed documentation and examples, see [Docs](https://github.com/Synvya/agentstr/tree/main/docs/docs.md)
80
+
81
+ ## Development
82
+
83
+ See [CONTRIBUTING.md](https://github.com/Synvya/agentstr/blob/main/CONTRIBUTING.md) for:
84
+ - Development setup
85
+ - Testing instructions
86
+ - Contribution guidelines
87
+
88
+ ## License
89
+
90
+ This project is licensed under the MIT License - see the [LICENSE](https://github.com/Synvya/agentstr/blob/main/LICENSE) file for details.
91
+
92
+ ## Acknowledgments
93
+
94
+ - [Agno](https://www.agno.ai) - For their AI agent framework
95
+ - [Rust-Nostr](https://rust-nostr.org) - For their Python Nostr SDK
96
+ - [Nostr Protocol](https://github.com/nostr-protocol/nips) - For the protocol specification
97
+
98
+ This software includes the following software licensed under the [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0):
99
+ - [DataStax Python Driver for Apache Cassandra](https://github.com/datastax/python-driver)
100
+ - [cassIO](https://github.com/CassioML/cassio). This library is not maintained anymore. We will need to replace it with a new library.
101
+
@@ -4,25 +4,28 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "agentstr"
7
- version = "0.1.10"
8
- description = "Nostr extension for Phidata AI agents"
7
+ version = "0.1.12"
8
+ description = "Nostr extension for Agno AI agents"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9, <3.13"
11
- license = {file = "LICENSE"}
11
+ license = { text = "MIT" }
12
12
  authors = [
13
- {name = "Synvya", email = "info@synvya.com"} # Add author info
13
+ {name = "Synvya", email = "info@synvya.com"}
14
14
  ]
15
15
  dependencies = [
16
- "phidata>=2.7.0",
16
+ "agno>=1.1.1",
17
17
  "openai>=1.50.0",
18
18
  "packaging>=24.0",
19
- "nostr-sdk>=0.38.0",
19
+ "nostr_sdk>=0.39.0",
20
20
  "pydantic>=2.0.0",
21
+ "cassandra-driver>=3.29.2",
22
+ "cassio>=0.1.10",
21
23
  ]
22
24
 
23
25
  [project.optional-dependencies]
24
26
  dev = [
25
27
  "pytest>=7.0",
28
+ "pytest-asyncio>=0.23.5",
26
29
  "black>=23.0",
27
30
  "isort>=5.0",
28
31
  "mypy>=1.0",
@@ -30,17 +33,14 @@ dev = [
30
33
  ]
31
34
 
32
35
  [project.urls]
33
- Homepage = "https://github.com/synvya/agentstr"
36
+ Homepage = "https://www.synvya.com"
37
+ Repository = "https://github.com/synvya/agentstr"
34
38
  Documentation = "https://github.com/synvya/agentstr#readme"
35
39
  BugTracker = "https://github.com/synvya/agentstr/issues"
36
40
 
37
41
  [tool.setuptools]
38
42
  package-dir = {"" = "src"}
39
- packages = [
40
- "agentstr",
41
- "agentstr.examples",
42
- "agentstr.examples.basic_cli"
43
- ]
43
+ packages = ["agentstr"]
44
44
 
45
45
  [tool.black]
46
46
  line-length = 88
@@ -58,7 +58,18 @@ python_version = "3.9"
58
58
  warn_return_any = true
59
59
  warn_unused_configs = true
60
60
  mypy_path = "src"
61
+ check_untyped_defs = true
62
+ disallow_untyped_defs = true
61
63
 
62
64
  [[tool.mypy.overrides]]
63
65
  module = ["nostr_sdk.*"]
64
- ignore_missing_imports = true
66
+ ignore_missing_imports = true
67
+
68
+ [tool.setuptools.package-data]
69
+ agentstr = ["py.typed"]
70
+
71
+ [tool.pytest.ini_options]
72
+ asyncio_mode = "auto"
73
+ markers = [
74
+ "asyncio: mark test as async",
75
+ ]
@@ -0,0 +1,37 @@
1
+ """
2
+ AgentStr: Nostr extension for Agno AI agents
3
+ """
4
+
5
+ from nostr_sdk import ShippingCost, ShippingMethod # type: ignore
6
+
7
+ from .merchant import MerchantTools
8
+
9
+ # Import main classes to make them available at package level
10
+ from .models import AgentProfile, MerchantProduct, MerchantStall, NostrProfile
11
+
12
+ # Import version from pyproject.toml at runtime
13
+ try:
14
+ from importlib.metadata import version
15
+
16
+ __version__ = version("agentstr")
17
+ except Exception:
18
+ __version__ = "unknown"
19
+
20
+ __all__ = [
21
+ "MerchantTools",
22
+ "MerchantProduct",
23
+ "MerchantStall",
24
+ "ShippingCost",
25
+ "ShippingMethod",
26
+ ]
27
+
28
+ from agentstr.nostr import EventId, Keys, NostrClient, ProductData, StallData
29
+
30
+ __all__ = [
31
+ "EventId",
32
+ "Keys",
33
+ "NostrClient",
34
+ "ProductData",
35
+ "StallData",
36
+ "AgentProfile",
37
+ ]
@@ -0,0 +1,291 @@
1
+ import json
2
+ import logging
3
+ from uuid import uuid4
4
+
5
+ from agno.agent import AgentKnowledge # type: ignore
6
+ from agno.document.base import Document
7
+
8
+ from agentstr.models import AgentProfile, NostrProfile
9
+ from agentstr.nostr import NostrClient, PublicKey
10
+
11
+ try:
12
+ from agno.tools import Toolkit
13
+ except ImportError:
14
+ raise ImportError("`agno` not installed. Please install using `pip install agno`")
15
+
16
+
17
+ def _map_location_to_geohash(location: str) -> str:
18
+ """
19
+ Map a location to a geohash.
20
+
21
+ TBD: Implement this function. Returning a fixed geohash for now.
22
+
23
+ Args:
24
+ location: location to map to a geohash. Can be a zip code, city, state, country, or latitude and longitude.
25
+
26
+ Returns:
27
+ str: geohash of the location or empty string if location is not found
28
+ """
29
+ if "snoqualmie" in location.lower():
30
+ return "C23Q7U36W"
31
+ else:
32
+ return ""
33
+
34
+
35
+ class BuyerTools(Toolkit):
36
+ """
37
+ BuyerTools is a toolkit that allows an agent to find sellers and transact with them over Nostr.
38
+
39
+ Sellers are downloaded from the Nostr relay and cached.
40
+ Sellers can be found by name or public key.
41
+ Sellers cache can be refreshed from the Nostr relay.
42
+ Sellers can be retrieved as a list of Nostr profiles.
43
+
44
+ TBD: populate the sellers locations with info from stalls.
45
+ """
46
+
47
+ from pydantic import ConfigDict
48
+
49
+ model_config = ConfigDict(
50
+ arbitrary_types_allowed=True, extra="allow", validate_assignment=True
51
+ )
52
+
53
+ logger = logging.getLogger("Buyer")
54
+ sellers: set[NostrProfile] = set()
55
+
56
+ def __init__(
57
+ self,
58
+ knowledge_base: AgentKnowledge,
59
+ buyer_profile: AgentProfile,
60
+ relay: str,
61
+ ) -> None:
62
+ """Initialize the Buyer toolkit.
63
+
64
+ Args:
65
+ knowledge_base: knowledge base of the buyer agent
66
+ buyer_profile: profile of the buyer using this agent
67
+ relay: Nostr relay to use for communications
68
+ """
69
+ super().__init__(name="Buyer")
70
+
71
+ self.relay = relay
72
+ self.buyer_profile = buyer_profile
73
+ self.knowledge_base = knowledge_base
74
+ # Initialize fields
75
+ self._nostr_client = NostrClient(relay, buyer_profile.get_private_key())
76
+
77
+ # Register methods
78
+ self.register(self.find_seller_by_name)
79
+ self.register(self.find_seller_by_public_key)
80
+ self.register(self.find_sellers_by_location)
81
+ self.register(self.get_profile)
82
+ self.register(self.get_relay)
83
+ self.register(self.get_seller_stalls)
84
+ self.register(self.get_seller_products)
85
+ self.register(self.get_seller_count)
86
+ self.register(self.get_sellers)
87
+ self.register(self.refresh_sellers)
88
+ self.register(self.purchase_product)
89
+
90
+ def purchase_product(self, product: str) -> str:
91
+ """Purchase a product.
92
+
93
+ Args:
94
+ product: JSON string with product to purchase
95
+ """
96
+ return json.dumps({"status": "success", "message": "Product purchased"})
97
+
98
+ def find_seller_by_name(self, name: str) -> str:
99
+ """Find a seller by name.
100
+
101
+ Args:
102
+ name: name of the seller to find
103
+
104
+ Returns:
105
+ str: JSON string with seller profile or error message
106
+ """
107
+ for seller in self.sellers:
108
+ if seller.get_name() == name:
109
+ response = seller.to_json()
110
+ # self._store_response_in_knowledge_base(response)
111
+ return response
112
+ response = json.dumps({"status": "error", "message": "Seller not found"})
113
+ self._store_response_in_knowledge_base(response)
114
+ return response
115
+
116
+ def find_seller_by_public_key(self, public_key: str) -> str:
117
+ """Find a seller by public key.
118
+
119
+ Args:
120
+ public_key: bech32 encoded public key of the seller to find
121
+
122
+ Returns:
123
+ str: seller profile json string or error message
124
+ """
125
+ for seller in self.sellers:
126
+ if seller.get_public_key() == public_key:
127
+ response = seller.to_json()
128
+ # self._store_response_in_knowledge_base(response)
129
+ return response
130
+ response = json.dumps({"status": "error", "message": "Seller not found"})
131
+ self._store_response_in_knowledge_base(response)
132
+ return response
133
+
134
+ def find_sellers_by_location(self, location: str) -> str:
135
+ """Find sellers by location.
136
+
137
+ Args:
138
+ location: location of the seller to find (e.g. "San Francisco, CA")
139
+
140
+ Returns:
141
+ str: list of seller profile json strings or error message
142
+ """
143
+ sellers: set[NostrProfile] = set()
144
+ geohash = _map_location_to_geohash(location)
145
+ # print(f"find_sellers_by_location: geohash: {geohash}")
146
+
147
+ if not geohash:
148
+ response = json.dumps({"status": "error", "message": "Invalid location"})
149
+ return response
150
+
151
+ # Find sellers in the same geohash
152
+ for seller in self.sellers:
153
+ 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
+ sellers.add(seller)
158
+
159
+ if not sellers:
160
+ response = json.dumps(
161
+ {"status": "error", "message": f"No sellers found near {location}"}
162
+ )
163
+ return response
164
+
165
+ response = json.dumps([seller.to_dict() for seller in sellers])
166
+ # print("find_sellers_by_location: storing response in knowledge base")
167
+ self._store_response_in_knowledge_base(response)
168
+ # print(f"Found {len(sellers)} sellers near {location}")
169
+ return response
170
+
171
+ def get_profile(self) -> str:
172
+ """Get the Nostr profile of the buyer agent.
173
+
174
+ Returns:
175
+ str: buyer profile json string
176
+ """
177
+ response = self.buyer_profile.to_json()
178
+ self._store_response_in_knowledge_base(response)
179
+ return response
180
+
181
+ def get_relay(self) -> str:
182
+ """Get the Nostr relay that the buyer agent is using.
183
+
184
+ Returns:
185
+ str: Nostr relay
186
+ """
187
+ response = self.relay
188
+ # self._store_response_in_knowledge_base(response)
189
+ return response
190
+
191
+ def get_seller_stalls(self, public_key: str) -> str:
192
+ """Get the stalls from a seller.
193
+
194
+ Args:
195
+ public_key: public key of the seller
196
+
197
+ Returns:
198
+ str: JSON string with seller collections
199
+ """
200
+ try:
201
+ stalls = self._nostr_client.retrieve_stalls_from_seller(
202
+ PublicKey.parse(public_key)
203
+ )
204
+ response = json.dumps([stall.as_json() for stall in stalls])
205
+ self._store_response_in_knowledge_base(response)
206
+ return response
207
+ except Exception as e:
208
+ response = json.dumps({"status": "error", "message": str(e)})
209
+ return response
210
+
211
+ def get_seller_count(self) -> str:
212
+ """Get the number of sellers.
213
+
214
+ Returns:
215
+ str: JSON string with status and count of sellers
216
+ """
217
+ response = json.dumps({"status": "success", "count": len(self.sellers)})
218
+ return response
219
+
220
+ def get_seller_products(self, public_key: str) -> str:
221
+ """Get the products from a seller
222
+
223
+ Args:
224
+ public_key: public key of the seller
225
+
226
+ Returns:
227
+ str: JSON string with seller products
228
+ """
229
+ try:
230
+ products = self._nostr_client.retrieve_products_from_seller(
231
+ PublicKey.parse(public_key)
232
+ )
233
+
234
+ response = json.dumps([product.to_dict() for product in products])
235
+ self._store_response_in_knowledge_base(response)
236
+ return response
237
+ except Exception as e:
238
+ response = json.dumps({"status": "error", "message": str(e)})
239
+ return response
240
+
241
+ def get_sellers(self) -> str:
242
+ """Get the list of sellers.
243
+ If no sellers are cached, the list is refreshed from the Nostr relay.
244
+ If sellers are cached, the list is returned from the cache.
245
+ To get a fresh list of sellers, call refresh_sellers() sellers first.
246
+
247
+ Returns:
248
+ str: list of sellers json strings
249
+ """
250
+ if not self.sellers:
251
+ self._refresh_sellers()
252
+ response = json.dumps([seller.to_json() for seller in self.sellers])
253
+ return response
254
+
255
+ def refresh_sellers(self) -> str:
256
+ """Refresh the list of sellers.
257
+
258
+ Returns:
259
+ str: JSON string with status and count of sellers refreshed
260
+ """
261
+ self._refresh_sellers()
262
+ response = json.dumps({"status": "success", "count": len(self.sellers)})
263
+ return response
264
+
265
+ def _refresh_sellers(self) -> None:
266
+ """
267
+ 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.
269
+
270
+ Returns:
271
+ List[NostrProfile]: List of Nostr profiles of all sellers.
272
+ """
273
+ sellers = self._nostr_client.retrieve_sellers()
274
+ if len(sellers) == 0:
275
+ self.logger.info("No sellers found")
276
+ 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()}")
282
+
283
+ self.sellers = sellers
284
+
285
+ def _store_response_in_knowledge_base(self, response: str) -> None:
286
+ doc = Document(
287
+ id=str(uuid4()),
288
+ content=response,
289
+ )
290
+ # print(f"Document length: {len(doc.content.split())} words")
291
+ self.knowledge_base.load_documents([doc]) # Store response in Cassandra
@@ -0,0 +1,31 @@
1
+ from logging import Logger
2
+ from typing import ClassVar
3
+
4
+ from agno.agent import AgentKnowledge
5
+ from agno.tools import Toolkit
6
+
7
+ from agentstr.models import AgentProfile, NostrProfile
8
+ from agentstr.nostr import NostrClient
9
+
10
+ class BuyerTools(Toolkit):
11
+ logger: ClassVar[Logger]
12
+ sellers: set[NostrProfile]
13
+ relay: str
14
+ _nostr_client: NostrClient
15
+
16
+ def __init__(
17
+ self, knowledge_base: AgentKnowledge, buyer_profile: AgentProfile, relay: str
18
+ ) -> None: ...
19
+ def find_seller_by_name(self, name: str) -> str: ...
20
+ def find_seller_by_public_key(self, public_key: str) -> str: ...
21
+ def find_sellers_by_location(self, location: str) -> str: ...
22
+ def get_profile(self) -> str: ...
23
+ def get_relay(self) -> str: ...
24
+ def get_seller_stalls(self, public_key: str) -> str: ...
25
+ def get_seller_count(self) -> str: ...
26
+ def get_seller_products(self, public_key: str) -> str: ...
27
+ def get_sellers(self) -> str: ...
28
+ def purchase_product(self, product: str) -> str: ...
29
+ def refresh_sellers(self) -> str: ...
30
+ def _refresh_sellers(self) -> None: ...
31
+ def _store_response_in_knowledge_base(self, response: str) -> None: ...