agentstr 0.1.11__tar.gz → 0.1.13__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
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