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