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.
- {agentstr-0.1.11/src/agentstr.egg-info → agentstr-0.1.13}/PKG-INFO +42 -56
- {agentstr-0.1.11 → agentstr-0.1.13}/README.md +29 -49
- {agentstr-0.1.11 → agentstr-0.1.13}/pyproject.toml +26 -8
- agentstr-0.1.13/src/agentstr/__init__.py +49 -0
- agentstr-0.1.13/src/agentstr/buyer.py +305 -0
- agentstr-0.1.13/src/agentstr/buyer.pyi +32 -0
- agentstr-0.1.11/src/agentstr/marketplace.py → agentstr-0.1.13/src/agentstr/merchant.py +167 -355
- agentstr-0.1.13/src/agentstr/merchant.pyi +37 -0
- agentstr-0.1.13/src/agentstr/models.py +380 -0
- agentstr-0.1.13/src/agentstr/models.pyi +103 -0
- agentstr-0.1.13/src/agentstr/nostr.py +660 -0
- agentstr-0.1.13/src/agentstr/nostr.pyi +96 -0
- agentstr-0.1.13/src/agentstr/py.typed +0 -0
- {agentstr-0.1.11 → agentstr-0.1.13/src/agentstr.egg-info}/PKG-INFO +42 -56
- {agentstr-0.1.11 → agentstr-0.1.13}/src/agentstr.egg-info/SOURCES.txt +11 -2
- agentstr-0.1.13/src/agentstr.egg-info/requires.txt +19 -0
- agentstr-0.1.13/tests/test_buyer.py +110 -0
- agentstr-0.1.13/tests/test_merchant.py +154 -0
- agentstr-0.1.13/tests/test_nostr_integration.py +98 -0
- agentstr-0.1.13/tests/test_nostr_mocked.py +107 -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/src/agentstr.egg-info/requires.txt +0 -12
- 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.13}/LICENSE +0 -0
- {agentstr-0.1.11 → agentstr-0.1.13}/MANIFEST.in +0 -0
- {agentstr-0.1.11 → agentstr-0.1.13}/setup.cfg +0 -0
- {agentstr-0.1.11 → agentstr-0.1.13}/src/agentstr.egg-info/dependency_links.txt +0 -0
- {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.
|
4
|
-
Summary:
|
5
|
-
Author-email: Synvya <
|
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:
|
14
|
+
Requires-Dist: agno>=1.1.1
|
15
15
|
Requires-Dist: openai>=1.50.0
|
16
|
-
Requires-Dist:
|
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:
|
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 [
|
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
|
-
│ ├──
|
45
|
-
│
|
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
|
-
- [ ]
|
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
|
-
|
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
|
-
|
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
|
-
- [
|
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 [
|
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,29 +4,36 @@ build-backend = "setuptools.build_meta"
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "agentstr"
|
7
|
-
version = "0.1.
|
8
|
-
description = "
|
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 = "
|
13
|
+
{name = "Synvya", email = "synvya@synvya.com"}
|
14
14
|
]
|
15
15
|
dependencies = [
|
16
|
-
"
|
16
|
+
"agno>=1.1.1",
|
17
17
|
"openai>=1.50.0",
|
18
|
-
"
|
19
|
-
"
|
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
|