agentstr 0.1.11__tar.gz → 0.1.12__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {agentstr-0.1.11/src/agentstr.egg-info → agentstr-0.1.12}/PKG-INFO +36 -53
- {agentstr-0.1.11 → agentstr-0.1.12}/README.md +29 -49
- {agentstr-0.1.11 → agentstr-0.1.12}/pyproject.toml +19 -5
- agentstr-0.1.12/src/agentstr/__init__.py +37 -0
- agentstr-0.1.12/src/agentstr/buyer.py +291 -0
- agentstr-0.1.12/src/agentstr/buyer.pyi +31 -0
- agentstr-0.1.11/src/agentstr/marketplace.py → agentstr-0.1.12/src/agentstr/merchant.py +126 -323
- agentstr-0.1.12/src/agentstr/merchant.pyi +37 -0
- agentstr-0.1.12/src/agentstr/models.py +381 -0
- agentstr-0.1.12/src/agentstr/models.pyi +103 -0
- agentstr-0.1.12/src/agentstr/nostr.py +663 -0
- agentstr-0.1.12/src/agentstr/nostr.pyi +82 -0
- agentstr-0.1.12/src/agentstr/py.typed +0 -0
- {agentstr-0.1.11 → agentstr-0.1.12/src/agentstr.egg-info}/PKG-INFO +36 -53
- {agentstr-0.1.11 → agentstr-0.1.12}/src/agentstr.egg-info/SOURCES.txt +11 -2
- {agentstr-0.1.11 → agentstr-0.1.12}/src/agentstr.egg-info/requires.txt +5 -2
- agentstr-0.1.12/tests/test_buyer.py +91 -0
- agentstr-0.1.12/tests/test_merchant.py +138 -0
- agentstr-0.1.12/tests/test_nostr_integration.py +94 -0
- agentstr-0.1.12/tests/test_nostr_mocked.py +101 -0
- agentstr-0.1.11/src/agentstr/__init__.py +0 -30
- agentstr-0.1.11/src/agentstr/nostr.py +0 -327
- agentstr-0.1.11/tests/test_merchant.py +0 -371
- agentstr-0.1.11/tests/test_nostr.py +0 -164
- {agentstr-0.1.11 → agentstr-0.1.12}/LICENSE +0 -0
- {agentstr-0.1.11 → agentstr-0.1.12}/MANIFEST.in +0 -0
- {agentstr-0.1.11 → agentstr-0.1.12}/setup.cfg +0 -0
- {agentstr-0.1.11 → agentstr-0.1.12}/src/agentstr.egg-info/dependency_links.txt +0 -0
- {agentstr-0.1.11 → agentstr-0.1.12}/src/agentstr.egg-info/top_level.txt +0 -0
@@ -1,7 +1,7 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: agentstr
|
3
|
-
Version: 0.1.
|
4
|
-
Summary: Nostr extension for
|
3
|
+
Version: 0.1.12
|
4
|
+
Summary: Nostr extension for Agno AI agents
|
5
5
|
Author-email: Synvya <info@synvya.com>
|
6
6
|
License: MIT
|
7
7
|
Project-URL: Homepage, https://www.synvya.com
|
@@ -11,13 +11,16 @@ 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:
|
14
|
+
Requires-Dist: agno>=1.1.1
|
15
15
|
Requires-Dist: openai>=1.50.0
|
16
16
|
Requires-Dist: packaging>=24.0
|
17
|
-
Requires-Dist:
|
17
|
+
Requires-Dist: nostr_sdk>=0.39.0
|
18
18
|
Requires-Dist: pydantic>=2.0.0
|
19
|
+
Requires-Dist: cassandra-driver>=3.29.2
|
20
|
+
Requires-Dist: cassio>=0.1.10
|
19
21
|
Provides-Extra: dev
|
20
22
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
23
|
+
Requires-Dist: pytest-asyncio>=0.23.5; extra == "dev"
|
21
24
|
Requires-Dist: black>=23.0; extra == "dev"
|
22
25
|
Requires-Dist: isort>=5.0; extra == "dev"
|
23
26
|
Requires-Dist: mypy>=1.0; extra == "dev"
|
@@ -25,7 +28,7 @@ Requires-Dist: python-dotenv>=1.0; extra == "dev"
|
|
25
28
|
|
26
29
|
# AgentStr
|
27
30
|
|
28
|
-
AgentStr is an extension of [
|
31
|
+
AgentStr is an extension of [Agno](https://www.agno.ai) AI agents that enables peer-to-peer agent communication using the Nostr protocol.
|
29
32
|
|
30
33
|
## Overview
|
31
34
|
|
@@ -41,8 +44,15 @@ agentstr/
|
|
41
44
|
├── src/ # Source code
|
42
45
|
│ └── agentstr/
|
43
46
|
│ ├── __init__.py
|
44
|
-
│ ├──
|
45
|
-
│
|
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
|
46
56
|
├── tests/ # Test files
|
47
57
|
├── docs/ # Documentation
|
48
58
|
├── examples/ # Example implementations
|
@@ -52,17 +62,19 @@ agentstr/
|
|
52
62
|
## Features
|
53
63
|
|
54
64
|
### 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
|
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
|
60
74
|
|
61
75
|
### Roadmap
|
62
76
|
- [ ] Create marketplace with stalls
|
63
|
-
- [ ]
|
64
|
-
- [ ] Enable merchants to define products
|
65
|
-
- [ ] Add customer toolkit for buyers
|
77
|
+
- [ ] Expand buyer agent to include more features
|
66
78
|
- [ ] Support additional Nostr NIPs
|
67
79
|
- [ ] Add more agent interaction patterns
|
68
80
|
|
@@ -82,46 +94,13 @@ pip install agentstr
|
|
82
94
|
|
83
95
|
You can find example code in the [examples](https://github.com/Synvya/agentstr/tree/main/examples/) directory.
|
84
96
|
|
85
|
-
|
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
|
-
```
|
97
|
+
To install the examples clone the repository and navigate to the examples directory:
|
108
98
|
|
109
|
-
2. ** Install dependencies**
|
110
99
|
```bash
|
111
|
-
|
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
|
100
|
+
git clone https://github.com/Synvya/agentstr.git
|
101
|
+
cd agentstr/examples/
|
123
102
|
```
|
124
|
-
|
103
|
+
Each example has its own README with instructions on how to run it.
|
125
104
|
|
126
105
|
## Documentation
|
127
106
|
|
@@ -140,7 +119,11 @@ This project is licensed under the MIT License - see the [LICENSE](https://githu
|
|
140
119
|
|
141
120
|
## Acknowledgments
|
142
121
|
|
143
|
-
- [
|
122
|
+
- [Agno](https://www.agno.ai) - For their AI agent framework
|
144
123
|
- [Rust-Nostr](https://rust-nostr.org) - For their Python Nostr SDK
|
145
124
|
- [Nostr Protocol](https://github.com/nostr-protocol/nips) - For the protocol specification
|
146
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
|
+
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# AgentStr
|
2
2
|
|
3
|
-
AgentStr is an extension of [
|
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
|
-
│ ├──
|
20
|
-
│
|
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
|
-
- [ ]
|
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
|
-
|
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
|
-
|
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
|
-
- [
|
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,8 +4,8 @@ build-backend = "setuptools.build_meta"
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "agentstr"
|
7
|
-
version = "0.1.
|
8
|
-
description = "Nostr extension for
|
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
11
|
license = { text = "MIT" }
|
@@ -13,16 +13,19 @@ authors = [
|
|
13
13
|
{name = "Synvya", email = "info@synvya.com"}
|
14
14
|
]
|
15
15
|
dependencies = [
|
16
|
-
"
|
16
|
+
"agno>=1.1.1",
|
17
17
|
"openai>=1.50.0",
|
18
18
|
"packaging>=24.0",
|
19
|
-
"
|
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",
|
@@ -55,7 +58,18 @@ python_version = "3.9"
|
|
55
58
|
warn_return_any = true
|
56
59
|
warn_unused_configs = true
|
57
60
|
mypy_path = "src"
|
61
|
+
check_untyped_defs = true
|
62
|
+
disallow_untyped_defs = true
|
58
63
|
|
59
64
|
[[tool.mypy.overrides]]
|
60
65
|
module = ["nostr_sdk.*"]
|
61
|
-
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: ...
|