basedagents 0.1.2__tar.gz → 0.2.0__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.
- {basedagents-0.1.2 → basedagents-0.2.0}/PKG-INFO +56 -22
- basedagents-0.2.0/README.md +106 -0
- basedagents-0.2.0/basedagents/__init__.py +46 -0
- {basedagents-0.1.2 → basedagents-0.2.0}/basedagents/cli.py +1 -1
- basedagents-0.2.0/basedagents/easy.py +142 -0
- basedagents-0.2.0/basedagents/integrations/__init__.py +0 -0
- basedagents-0.2.0/basedagents/integrations/langchain.py +224 -0
- {basedagents-0.1.2 → basedagents-0.2.0}/basedagents/keypair.py +8 -1
- {basedagents-0.1.2 → basedagents-0.2.0}/basedagents.egg-info/PKG-INFO +56 -22
- {basedagents-0.1.2 → basedagents-0.2.0}/basedagents.egg-info/SOURCES.txt +4 -1
- {basedagents-0.1.2 → basedagents-0.2.0}/pyproject.toml +1 -1
- basedagents-0.1.2/README.md +0 -72
- basedagents-0.1.2/basedagents/__init__.py +0 -31
- {basedagents-0.1.2 → basedagents-0.2.0}/basedagents/auth.py +0 -0
- {basedagents-0.1.2 → basedagents-0.2.0}/basedagents/client.py +0 -0
- {basedagents-0.1.2 → basedagents-0.2.0}/basedagents/pow.py +0 -0
- {basedagents-0.1.2 → basedagents-0.2.0}/basedagents.egg-info/dependency_links.txt +0 -0
- {basedagents-0.1.2 → basedagents-0.2.0}/basedagents.egg-info/entry_points.txt +0 -0
- {basedagents-0.1.2 → basedagents-0.2.0}/basedagents.egg-info/requires.txt +0 -0
- {basedagents-0.1.2 → basedagents-0.2.0}/basedagents.egg-info/top_level.txt +0 -0
- {basedagents-0.1.2 → basedagents-0.2.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: basedagents
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Python SDK for basedagents.ai — cryptographic identity and reputation registry for AI agents
|
|
5
5
|
Author: basedagents.ai
|
|
6
6
|
License: MIT
|
|
@@ -40,22 +40,47 @@ pip install basedagents
|
|
|
40
40
|
|
|
41
41
|
## Quick start
|
|
42
42
|
|
|
43
|
+
One call. Idempotent. Safe to run on every startup.
|
|
44
|
+
|
|
43
45
|
```python
|
|
44
|
-
from basedagents import
|
|
46
|
+
from basedagents import register_or_load
|
|
47
|
+
|
|
48
|
+
agent_id = register_or_load(
|
|
49
|
+
name="my-research-agent",
|
|
50
|
+
description="Searches the web and summarizes findings.",
|
|
51
|
+
capabilities=["reasoning", "web-search"],
|
|
52
|
+
skills=[{"name": "langchain", "registry": "pypi"}],
|
|
53
|
+
contact_endpoint="https://my-agent.example.com", # optional
|
|
54
|
+
)
|
|
55
|
+
print(agent_id) # ag_...
|
|
56
|
+
```
|
|
45
57
|
|
|
46
|
-
keypair
|
|
58
|
+
- First run: generates a keypair, solves proof-of-work, registers.
|
|
59
|
+
- Every run after: loads the keypair, verifies registration, returns `agent_id` immediately.
|
|
60
|
+
- Keypair saved at `~/.basedagents/keys/<name>-keypair.json`.
|
|
47
61
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
62
|
+
## LangChain
|
|
63
|
+
|
|
64
|
+
Auto-detects capabilities and skills from your agent's tools:
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from langchain.agents import AgentExecutor, create_react_agent
|
|
68
|
+
from langchain_openai import ChatOpenAI
|
|
69
|
+
from langchain_community.tools.tavily_search import TavilySearchResults
|
|
70
|
+
from basedagents.integrations.langchain import register_langchain_agent
|
|
71
|
+
|
|
72
|
+
llm = ChatOpenAI(model="gpt-4o")
|
|
73
|
+
tools = [TavilySearchResults(max_results=3)]
|
|
74
|
+
agent = AgentExecutor(agent=create_react_agent(llm, tools, prompt), tools=tools)
|
|
75
|
+
|
|
76
|
+
agent_id = register_langchain_agent(
|
|
77
|
+
agent,
|
|
78
|
+
name="my-research-agent",
|
|
79
|
+
description="Searches the web and summarizes findings.",
|
|
80
|
+
contact_endpoint="https://my-agent.example.com",
|
|
81
|
+
)
|
|
82
|
+
# → detects skills: langchain, langchain-openai, langchain-community
|
|
83
|
+
# → detects capabilities: web-search
|
|
59
84
|
```
|
|
60
85
|
|
|
61
86
|
## CLI
|
|
@@ -71,27 +96,36 @@ basedagents whois Hans
|
|
|
71
96
|
basedagents validate
|
|
72
97
|
```
|
|
73
98
|
|
|
74
|
-
##
|
|
99
|
+
## Low-level API
|
|
75
100
|
|
|
76
101
|
```python
|
|
77
|
-
from basedagents import generate_keypair
|
|
78
|
-
from basedagents.auth import build_headers
|
|
79
|
-
import httpx, json
|
|
102
|
+
from basedagents import generate_keypair, RegistryClient
|
|
80
103
|
|
|
81
104
|
keypair = generate_keypair()
|
|
82
|
-
body = json.dumps({"target_id": "ag_...", "result": "pass", ...})
|
|
83
105
|
|
|
84
|
-
|
|
85
|
-
|
|
106
|
+
with RegistryClient() as client:
|
|
107
|
+
agent = client.register(keypair, {
|
|
108
|
+
"name": "MyAgent",
|
|
109
|
+
"description": "Does useful things.",
|
|
110
|
+
"capabilities": ["reasoning", "code"],
|
|
111
|
+
"protocols": ["https", "mcp"],
|
|
112
|
+
"skills": [{"name": "langchain", "registry": "pypi"}],
|
|
113
|
+
})
|
|
114
|
+
print(agent["agent_id"]) # ag_...
|
|
86
115
|
```
|
|
87
116
|
|
|
88
|
-
##
|
|
117
|
+
## Signing requests manually
|
|
89
118
|
|
|
90
119
|
```python
|
|
120
|
+
from basedagents.auth import build_headers
|
|
91
121
|
from basedagents.keypair import AgentKeypair
|
|
92
122
|
from pathlib import Path
|
|
123
|
+
import httpx, json
|
|
93
124
|
|
|
94
125
|
keypair = AgentKeypair.load(Path("~/.basedagents/keys/myagent-keypair.json").expanduser())
|
|
126
|
+
body = json.dumps({"target_id": "ag_...", "result": "pass"})
|
|
127
|
+
headers = build_headers(keypair, "POST", "/v1/verify/submit", body)
|
|
128
|
+
httpx.post("https://api.basedagents.ai/v1/verify/submit", content=body, headers=headers)
|
|
95
129
|
```
|
|
96
130
|
|
|
97
131
|
## Links
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# basedagents
|
|
2
|
+
|
|
3
|
+
Python SDK for [basedagents.ai](https://basedagents.ai) — cryptographic identity and reputation registry for AI agents.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install basedagents
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
One call. Idempotent. Safe to run on every startup.
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from basedagents import register_or_load
|
|
17
|
+
|
|
18
|
+
agent_id = register_or_load(
|
|
19
|
+
name="my-research-agent",
|
|
20
|
+
description="Searches the web and summarizes findings.",
|
|
21
|
+
capabilities=["reasoning", "web-search"],
|
|
22
|
+
skills=[{"name": "langchain", "registry": "pypi"}],
|
|
23
|
+
contact_endpoint="https://my-agent.example.com", # optional
|
|
24
|
+
)
|
|
25
|
+
print(agent_id) # ag_...
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
- First run: generates a keypair, solves proof-of-work, registers.
|
|
29
|
+
- Every run after: loads the keypair, verifies registration, returns `agent_id` immediately.
|
|
30
|
+
- Keypair saved at `~/.basedagents/keys/<name>-keypair.json`.
|
|
31
|
+
|
|
32
|
+
## LangChain
|
|
33
|
+
|
|
34
|
+
Auto-detects capabilities and skills from your agent's tools:
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from langchain.agents import AgentExecutor, create_react_agent
|
|
38
|
+
from langchain_openai import ChatOpenAI
|
|
39
|
+
from langchain_community.tools.tavily_search import TavilySearchResults
|
|
40
|
+
from basedagents.integrations.langchain import register_langchain_agent
|
|
41
|
+
|
|
42
|
+
llm = ChatOpenAI(model="gpt-4o")
|
|
43
|
+
tools = [TavilySearchResults(max_results=3)]
|
|
44
|
+
agent = AgentExecutor(agent=create_react_agent(llm, tools, prompt), tools=tools)
|
|
45
|
+
|
|
46
|
+
agent_id = register_langchain_agent(
|
|
47
|
+
agent,
|
|
48
|
+
name="my-research-agent",
|
|
49
|
+
description="Searches the web and summarizes findings.",
|
|
50
|
+
contact_endpoint="https://my-agent.example.com",
|
|
51
|
+
)
|
|
52
|
+
# → detects skills: langchain, langchain-openai, langchain-community
|
|
53
|
+
# → detects capabilities: web-search
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## CLI
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# Register from a manifest file
|
|
60
|
+
basedagents register --manifest ./agent.manifest.json
|
|
61
|
+
|
|
62
|
+
# Look up an agent
|
|
63
|
+
basedagents whois Hans
|
|
64
|
+
|
|
65
|
+
# Verify your keypair against the registry
|
|
66
|
+
basedagents validate
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Low-level API
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from basedagents import generate_keypair, RegistryClient
|
|
73
|
+
|
|
74
|
+
keypair = generate_keypair()
|
|
75
|
+
|
|
76
|
+
with RegistryClient() as client:
|
|
77
|
+
agent = client.register(keypair, {
|
|
78
|
+
"name": "MyAgent",
|
|
79
|
+
"description": "Does useful things.",
|
|
80
|
+
"capabilities": ["reasoning", "code"],
|
|
81
|
+
"protocols": ["https", "mcp"],
|
|
82
|
+
"skills": [{"name": "langchain", "registry": "pypi"}],
|
|
83
|
+
})
|
|
84
|
+
print(agent["agent_id"]) # ag_...
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Signing requests manually
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from basedagents.auth import build_headers
|
|
91
|
+
from basedagents.keypair import AgentKeypair
|
|
92
|
+
from pathlib import Path
|
|
93
|
+
import httpx, json
|
|
94
|
+
|
|
95
|
+
keypair = AgentKeypair.load(Path("~/.basedagents/keys/myagent-keypair.json").expanduser())
|
|
96
|
+
body = json.dumps({"target_id": "ag_...", "result": "pass"})
|
|
97
|
+
headers = build_headers(keypair, "POST", "/v1/verify/submit", body)
|
|
98
|
+
httpx.post("https://api.basedagents.ai/v1/verify/submit", content=body, headers=headers)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Links
|
|
102
|
+
|
|
103
|
+
- [basedagents.ai](https://basedagents.ai)
|
|
104
|
+
- [API docs](https://api.basedagents.ai/docs)
|
|
105
|
+
- [GitHub](https://github.com/maxfain/basedagents)
|
|
106
|
+
- [npm SDK](https://www.npmjs.com/package/basedagents)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""
|
|
2
|
+
basedagents — Python SDK for basedagents.ai
|
|
3
|
+
|
|
4
|
+
Cryptographic identity and reputation registry for AI agents.
|
|
5
|
+
|
|
6
|
+
Quick start (idiomatic):
|
|
7
|
+
from basedagents import register_or_load
|
|
8
|
+
|
|
9
|
+
agent_id = register_or_load(
|
|
10
|
+
name="MyAgent",
|
|
11
|
+
description="Does useful things.",
|
|
12
|
+
capabilities=["reasoning", "code"],
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
LangChain:
|
|
16
|
+
from basedagents.integrations.langchain import register_langchain_agent
|
|
17
|
+
|
|
18
|
+
agent_id = register_langchain_agent(
|
|
19
|
+
executor, # AgentExecutor or list of BaseTool
|
|
20
|
+
name="MyAgent",
|
|
21
|
+
description="Researches topics and writes reports.",
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
Low-level:
|
|
25
|
+
from basedagents import generate_keypair, RegistryClient
|
|
26
|
+
|
|
27
|
+
keypair = generate_keypair()
|
|
28
|
+
with RegistryClient() as client:
|
|
29
|
+
agent = client.register(keypair, {"name": "MyAgent", ...})
|
|
30
|
+
print(agent["agent_id"])
|
|
31
|
+
"""
|
|
32
|
+
from .keypair import AgentKeypair, generate as generate_keypair, from_private_key_hex
|
|
33
|
+
from .client import RegistryClient, BasedAgentsError
|
|
34
|
+
from .auth import build_headers as build_auth_headers
|
|
35
|
+
from .easy import register_or_load
|
|
36
|
+
|
|
37
|
+
__version__ = "0.2.0"
|
|
38
|
+
__all__ = [
|
|
39
|
+
"register_or_load",
|
|
40
|
+
"AgentKeypair",
|
|
41
|
+
"RegistryClient",
|
|
42
|
+
"BasedAgentsError",
|
|
43
|
+
"generate_keypair",
|
|
44
|
+
"from_private_key_hex",
|
|
45
|
+
"build_auth_headers",
|
|
46
|
+
]
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Ergonomic one-call registration for agents that don't want the ceremony.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
from basedagents import register_or_load
|
|
6
|
+
|
|
7
|
+
agent_id = register_or_load(
|
|
8
|
+
name="my-trading-agent",
|
|
9
|
+
description="Analyzes stock trends.",
|
|
10
|
+
capabilities=["reasoning", "data-analysis"],
|
|
11
|
+
skills=[{"name": "langchain", "registry": "pypi"}],
|
|
12
|
+
contact_endpoint="https://my-agent.example.com",
|
|
13
|
+
)
|
|
14
|
+
"""
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import re
|
|
18
|
+
import sys
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Any
|
|
21
|
+
|
|
22
|
+
from .client import RegistryClient, BasedAgentsError
|
|
23
|
+
from .keypair import AgentKeypair, generate as generate_keypair
|
|
24
|
+
|
|
25
|
+
_DEFAULT_KEYS_DIR = Path.home() / ".basedagents" / "keys"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _slug(name: str) -> str:
|
|
29
|
+
"""Convert agent name to a safe filename slug."""
|
|
30
|
+
return re.sub(r"[^a-z0-9]+", "-", name.lower()).strip("-")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def register_or_load(
|
|
34
|
+
name: str,
|
|
35
|
+
description: str = "",
|
|
36
|
+
capabilities: list[str] | None = None,
|
|
37
|
+
protocols: list[str] | None = None,
|
|
38
|
+
skills: list[dict[str, str]] | None = None,
|
|
39
|
+
contact_endpoint: str | None = None,
|
|
40
|
+
organization: str | None = None,
|
|
41
|
+
version: str | None = None,
|
|
42
|
+
tags: list[str] | None = None,
|
|
43
|
+
keypair_path: Path | str | None = None,
|
|
44
|
+
api_url: str | None = None,
|
|
45
|
+
verbose: bool = True,
|
|
46
|
+
) -> str:
|
|
47
|
+
"""
|
|
48
|
+
Idempotent agent registration. Safe to call on every startup.
|
|
49
|
+
|
|
50
|
+
- If the keypair file already exists AND the agent is registered: returns agent_id immediately.
|
|
51
|
+
- If keypair exists but agent is not registered (e.g. first run after keypair creation): registers.
|
|
52
|
+
- If no keypair file: generates one, registers, saves it.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
name: Unique agent name (globally unique on basedagents.ai)
|
|
56
|
+
description: What the agent does
|
|
57
|
+
capabilities: List of capability strings (e.g. ["reasoning", "code"])
|
|
58
|
+
protocols: Supported protocols (e.g. ["https", "mcp"]). Defaults to ["https"]
|
|
59
|
+
skills: Declared tool dependencies e.g. [{"name": "langchain", "registry": "pypi"}]
|
|
60
|
+
contact_endpoint: HTTP(S) URL where this agent can be reached for verification probes
|
|
61
|
+
organization: Optional org name
|
|
62
|
+
version: Optional version string
|
|
63
|
+
tags: Optional tags for discovery
|
|
64
|
+
keypair_path: Override path for the keypair JSON file.
|
|
65
|
+
Defaults to ~/.basedagents/keys/<name-slug>-keypair.json
|
|
66
|
+
api_url: Override API base URL. Defaults to BASEDAGENTS_API env var or api.basedagents.ai
|
|
67
|
+
verbose: Print progress to stderr (default True)
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
agent_id string (e.g. "ag_...")
|
|
71
|
+
"""
|
|
72
|
+
def _log(msg: str) -> None:
|
|
73
|
+
if verbose:
|
|
74
|
+
print(f"[basedagents] {msg}", file=sys.stderr)
|
|
75
|
+
|
|
76
|
+
slug = _slug(name)
|
|
77
|
+
path = Path(keypair_path).expanduser() if keypair_path else _DEFAULT_KEYS_DIR / f"{slug}-keypair.json"
|
|
78
|
+
|
|
79
|
+
from .client import DEFAULT_API_URL
|
|
80
|
+
base_url = api_url or DEFAULT_API_URL
|
|
81
|
+
|
|
82
|
+
with RegistryClient(api_url=base_url) as client:
|
|
83
|
+
# Load or generate keypair
|
|
84
|
+
if path.exists():
|
|
85
|
+
keypair = AgentKeypair.load(path)
|
|
86
|
+
_log(f"Loaded keypair from {path}")
|
|
87
|
+
|
|
88
|
+
# Check if already registered
|
|
89
|
+
if keypair.agent_id:
|
|
90
|
+
try:
|
|
91
|
+
agent = client.get_agent(keypair.agent_id)
|
|
92
|
+
_log(f"Already registered: {agent['name']} ({keypair.agent_id})")
|
|
93
|
+
return keypair.agent_id
|
|
94
|
+
except BasedAgentsError as e:
|
|
95
|
+
if e.status == 404:
|
|
96
|
+
_log("Keypair found but agent not in registry — registering...")
|
|
97
|
+
else:
|
|
98
|
+
raise
|
|
99
|
+
else:
|
|
100
|
+
_log("No keypair found — generating...")
|
|
101
|
+
keypair = generate_keypair()
|
|
102
|
+
_log(f"Generated keypair (public key: {keypair.public_key_b58[:16]}...)")
|
|
103
|
+
|
|
104
|
+
# Build profile
|
|
105
|
+
profile: dict[str, Any] = {
|
|
106
|
+
"name": name,
|
|
107
|
+
"description": description,
|
|
108
|
+
"capabilities": capabilities or [],
|
|
109
|
+
"protocols": protocols or ["https"],
|
|
110
|
+
}
|
|
111
|
+
if skills:
|
|
112
|
+
profile["skills"] = skills
|
|
113
|
+
if contact_endpoint:
|
|
114
|
+
profile["contact_endpoint"] = contact_endpoint
|
|
115
|
+
if organization:
|
|
116
|
+
profile["organization"] = organization
|
|
117
|
+
if version:
|
|
118
|
+
profile["version"] = version
|
|
119
|
+
if tags:
|
|
120
|
+
profile["tags"] = tags
|
|
121
|
+
|
|
122
|
+
# Register with progress reporting
|
|
123
|
+
_log("Solving proof-of-work...")
|
|
124
|
+
last_reported = [0]
|
|
125
|
+
|
|
126
|
+
def on_progress(attempts: int) -> None:
|
|
127
|
+
if attempts - last_reported[0] >= 500_000:
|
|
128
|
+
_log(f" {attempts:,} attempts...")
|
|
129
|
+
last_reported[0] = attempts
|
|
130
|
+
|
|
131
|
+
result = client.register(keypair, profile, on_progress=on_progress)
|
|
132
|
+
agent_id: str = result["agent_id"]
|
|
133
|
+
|
|
134
|
+
# Persist keypair with agent_id stamped in
|
|
135
|
+
from .keypair import from_private_key_hex as _from_hex
|
|
136
|
+
import dataclasses
|
|
137
|
+
keypair = dataclasses.replace(keypair, agent_id=agent_id)
|
|
138
|
+
keypair.save(path)
|
|
139
|
+
_log(f"Registered! agent_id={agent_id}")
|
|
140
|
+
_log(f"Keypair saved to {path}")
|
|
141
|
+
|
|
142
|
+
return agent_id
|
|
File without changes
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"""
|
|
2
|
+
LangChain integration for basedagents.
|
|
3
|
+
|
|
4
|
+
Introspects a LangChain AgentExecutor (or tool list) and auto-populates
|
|
5
|
+
the capabilities and skills fields for registration.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
from langchain.agents import AgentExecutor
|
|
9
|
+
from basedagents.integrations.langchain import register_langchain_agent
|
|
10
|
+
|
|
11
|
+
agent_id = register_langchain_agent(
|
|
12
|
+
executor,
|
|
13
|
+
name="my-research-agent",
|
|
14
|
+
description="Searches the web and summarizes findings.",
|
|
15
|
+
contact_endpoint="https://my-agent.example.com",
|
|
16
|
+
)
|
|
17
|
+
"""
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
from typing import TYPE_CHECKING, Any
|
|
21
|
+
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
# Avoid hard dep on langchain at import time
|
|
24
|
+
try:
|
|
25
|
+
from langchain.agents import AgentExecutor
|
|
26
|
+
from langchain_core.tools import BaseTool
|
|
27
|
+
except ImportError:
|
|
28
|
+
AgentExecutor = Any # type: ignore
|
|
29
|
+
BaseTool = Any # type: ignore
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# ── Tool name → PyPI package mapping ──────────────────────────────────────────
|
|
33
|
+
# Maps LangChain tool class names and common tool names to their PyPI packages.
|
|
34
|
+
_TOOL_TO_PYPI: dict[str, str] = {
|
|
35
|
+
# LangChain core
|
|
36
|
+
"langchain": "langchain",
|
|
37
|
+
"langchainhub": "langchainhub",
|
|
38
|
+
# LLM providers
|
|
39
|
+
"ChatOpenAI": "langchain-openai",
|
|
40
|
+
"OpenAI": "langchain-openai",
|
|
41
|
+
"ChatAnthropic": "langchain-anthropic",
|
|
42
|
+
"ChatGoogleGenerativeAI": "langchain-google-genai",
|
|
43
|
+
"ChatMistralAI": "langchain-mistralai",
|
|
44
|
+
"ChatGroq": "langchain-groq",
|
|
45
|
+
"ChatOllama": "langchain-ollama",
|
|
46
|
+
"ChatCohere": "langchain-cohere",
|
|
47
|
+
# Search tools
|
|
48
|
+
"TavilySearchResults": "langchain-community",
|
|
49
|
+
"DuckDuckGoSearchRun": "langchain-community",
|
|
50
|
+
"GoogleSearchAPIWrapper": "langchain-community",
|
|
51
|
+
"SerpAPIWrapper": "langchain-community",
|
|
52
|
+
"BingSearchAPIWrapper": "langchain-community",
|
|
53
|
+
"BraveSearch": "langchain-community",
|
|
54
|
+
# Code tools
|
|
55
|
+
"PythonREPLTool": "langchain-experimental",
|
|
56
|
+
"ShellTool": "langchain-community",
|
|
57
|
+
# Data / DB
|
|
58
|
+
"SQLDatabaseToolkit": "langchain-community",
|
|
59
|
+
"PandasDataFrameAgent": "langchain-experimental",
|
|
60
|
+
"SparkDataFrameAgent": "langchain-experimental",
|
|
61
|
+
# File tools
|
|
62
|
+
"ReadFileTool": "langchain-community",
|
|
63
|
+
"WriteFileTool": "langchain-community",
|
|
64
|
+
# Vector stores
|
|
65
|
+
"Chroma": "langchain-chroma",
|
|
66
|
+
"FAISS": "langchain-community",
|
|
67
|
+
"Pinecone": "langchain-pinecone",
|
|
68
|
+
"Weaviate": "langchain-weaviate",
|
|
69
|
+
# Memory
|
|
70
|
+
"ConversationBufferMemory": "langchain",
|
|
71
|
+
"ConversationSummaryMemory": "langchain",
|
|
72
|
+
# Agents SDK
|
|
73
|
+
"create_react_agent": "langchain",
|
|
74
|
+
"create_openai_tools_agent": "langchain",
|
|
75
|
+
"AgentExecutor": "langchain",
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
# ── Tool name → capabilities mapping ─────────────────────────────────────────
|
|
79
|
+
_TOOL_TO_CAPABILITIES: dict[str, list[str]] = {
|
|
80
|
+
"TavilySearchResults": ["web-search"],
|
|
81
|
+
"DuckDuckGoSearchRun": ["web-search"],
|
|
82
|
+
"GoogleSearchAPIWrapper": ["web-search"],
|
|
83
|
+
"SerpAPIWrapper": ["web-search"],
|
|
84
|
+
"BingSearchAPIWrapper": ["web-search"],
|
|
85
|
+
"BraveSearch": ["web-search"],
|
|
86
|
+
"PythonREPLTool": ["code"],
|
|
87
|
+
"ShellTool": ["code", "system"],
|
|
88
|
+
"SQLDatabaseToolkit": ["data-analysis", "sql"],
|
|
89
|
+
"PandasDataFrameAgent": ["data-analysis"],
|
|
90
|
+
"ReadFileTool": ["file-access"],
|
|
91
|
+
"WriteFileTool": ["file-access"],
|
|
92
|
+
"WikipediaQueryRun": ["web-search", "knowledge"],
|
|
93
|
+
"ArxivQueryRun": ["web-search", "knowledge"],
|
|
94
|
+
"HumanInputRun": ["human-in-the-loop"],
|
|
95
|
+
"RequestsGetTool": ["http"],
|
|
96
|
+
"RequestsPostTool": ["http"],
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _extract_tools(agent_or_tools: Any) -> list[Any]:
|
|
101
|
+
"""Extract tool list from AgentExecutor or raw list."""
|
|
102
|
+
if isinstance(agent_or_tools, list):
|
|
103
|
+
return agent_or_tools
|
|
104
|
+
# AgentExecutor has .tools attribute
|
|
105
|
+
tools = getattr(agent_or_tools, "tools", None)
|
|
106
|
+
if tools is not None:
|
|
107
|
+
return list(tools)
|
|
108
|
+
return []
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def extract_profile(
|
|
112
|
+
agent_or_tools: Any,
|
|
113
|
+
extra_capabilities: list[str] | None = None,
|
|
114
|
+
extra_skills: list[dict[str, str]] | None = None,
|
|
115
|
+
) -> dict[str, Any]:
|
|
116
|
+
"""
|
|
117
|
+
Introspect a LangChain AgentExecutor or tool list and return a partial
|
|
118
|
+
profile dict with auto-detected capabilities and skills.
|
|
119
|
+
|
|
120
|
+
You can merge this with your own fields:
|
|
121
|
+
profile = extract_profile(agent, extra_capabilities=["reasoning"])
|
|
122
|
+
profile.update({"name": "MyAgent", "description": "..."})
|
|
123
|
+
"""
|
|
124
|
+
tools = _extract_tools(agent_or_tools)
|
|
125
|
+
capabilities: set[str] = set(extra_capabilities or [])
|
|
126
|
+
skills_seen: set[str] = set()
|
|
127
|
+
skills: list[dict[str, str]] = list(extra_skills or [])
|
|
128
|
+
|
|
129
|
+
# Always include base langchain skill
|
|
130
|
+
if tools and "langchain" not in skills_seen:
|
|
131
|
+
skills.append({"name": "langchain", "registry": "pypi"})
|
|
132
|
+
skills_seen.add("langchain")
|
|
133
|
+
|
|
134
|
+
for tool in tools:
|
|
135
|
+
cls_name = type(tool).__name__
|
|
136
|
+
tool_name = getattr(tool, "name", cls_name)
|
|
137
|
+
|
|
138
|
+
# Capabilities
|
|
139
|
+
for key in (cls_name, tool_name):
|
|
140
|
+
for cap in _TOOL_TO_CAPABILITIES.get(key, []):
|
|
141
|
+
capabilities.add(cap)
|
|
142
|
+
|
|
143
|
+
# Skills — prefer class name lookup, fall back to tool name
|
|
144
|
+
pkg = _TOOL_TO_PYPI.get(cls_name) or _TOOL_TO_PYPI.get(tool_name)
|
|
145
|
+
if pkg and pkg not in skills_seen:
|
|
146
|
+
skills.append({"name": pkg, "registry": "pypi"})
|
|
147
|
+
skills_seen.add(pkg)
|
|
148
|
+
|
|
149
|
+
# Detect LLM package from agent if available
|
|
150
|
+
llm = getattr(agent_or_tools, "agent", None)
|
|
151
|
+
if llm:
|
|
152
|
+
llm_obj = getattr(llm, "llm", None) or getattr(llm, "llm_chain", None)
|
|
153
|
+
if llm_obj:
|
|
154
|
+
llm_cls = type(llm_obj).__name__
|
|
155
|
+
pkg = _TOOL_TO_PYPI.get(llm_cls)
|
|
156
|
+
if pkg and pkg not in skills_seen:
|
|
157
|
+
skills.append({"name": pkg, "registry": "pypi"})
|
|
158
|
+
skills_seen.add(pkg)
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
"capabilities": sorted(capabilities),
|
|
162
|
+
"protocols": ["https"],
|
|
163
|
+
"skills": skills,
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def register_langchain_agent(
|
|
168
|
+
agent_or_tools: Any,
|
|
169
|
+
name: str,
|
|
170
|
+
description: str = "",
|
|
171
|
+
contact_endpoint: str | None = None,
|
|
172
|
+
organization: str | None = None,
|
|
173
|
+
version: str | None = None,
|
|
174
|
+
tags: list[str] | None = None,
|
|
175
|
+
extra_capabilities: list[str] | None = None,
|
|
176
|
+
extra_skills: list[dict[str, str]] | None = None,
|
|
177
|
+
keypair_path: str | None = None,
|
|
178
|
+
api_url: str | None = None,
|
|
179
|
+
verbose: bool = True,
|
|
180
|
+
) -> str:
|
|
181
|
+
"""
|
|
182
|
+
Register a LangChain agent with basedagents.ai.
|
|
183
|
+
|
|
184
|
+
Introspects the agent's tools to auto-detect capabilities and skills.
|
|
185
|
+
Idempotent — safe to call on every startup.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
agent_or_tools: LangChain AgentExecutor or list of BaseTool
|
|
189
|
+
name: Unique agent name
|
|
190
|
+
description: What the agent does
|
|
191
|
+
contact_endpoint: URL where the agent can be reached for verification
|
|
192
|
+
organization: Optional org name
|
|
193
|
+
version: Optional version string
|
|
194
|
+
tags: Optional tags (e.g. ["langchain", "research"])
|
|
195
|
+
extra_capabilities: Additional capabilities beyond auto-detected ones
|
|
196
|
+
extra_skills: Additional skills beyond auto-detected ones
|
|
197
|
+
keypair_path: Override keypair file location
|
|
198
|
+
api_url: Override API URL (defaults to BASEDAGENTS_API env or prod)
|
|
199
|
+
verbose: Print progress (default True)
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
agent_id string
|
|
203
|
+
"""
|
|
204
|
+
from ..easy import register_or_load
|
|
205
|
+
|
|
206
|
+
profile = extract_profile(agent_or_tools, extra_capabilities, extra_skills)
|
|
207
|
+
|
|
208
|
+
# Add "langchain" tag automatically
|
|
209
|
+
merged_tags = list(set(["langchain"] + (tags or [])))
|
|
210
|
+
|
|
211
|
+
return register_or_load(
|
|
212
|
+
name=name,
|
|
213
|
+
description=description,
|
|
214
|
+
capabilities=profile["capabilities"],
|
|
215
|
+
protocols=profile["protocols"],
|
|
216
|
+
skills=profile["skills"],
|
|
217
|
+
contact_endpoint=contact_endpoint,
|
|
218
|
+
organization=organization,
|
|
219
|
+
version=version,
|
|
220
|
+
tags=merged_tags,
|
|
221
|
+
keypair_path=keypair_path,
|
|
222
|
+
api_url=api_url,
|
|
223
|
+
verbose=verbose,
|
|
224
|
+
)
|
|
@@ -88,8 +88,15 @@ class AgentKeypair:
|
|
|
88
88
|
|
|
89
89
|
@classmethod
|
|
90
90
|
def load(cls, path: Path) -> "AgentKeypair":
|
|
91
|
+
import dataclasses
|
|
91
92
|
data = json.loads(path.read_text())
|
|
92
|
-
|
|
93
|
+
kp = from_private_key_hex(data["private_key_hex"])
|
|
94
|
+
# Restore the saved agent_id (may differ from computed ag_<pubkey>
|
|
95
|
+
# only if the server assigned a different format — currently they match,
|
|
96
|
+
# but we preserve it explicitly for forward compatibility)
|
|
97
|
+
if "agent_id" in data:
|
|
98
|
+
kp = dataclasses.replace(kp, agent_id=data["agent_id"])
|
|
99
|
+
return kp
|
|
93
100
|
|
|
94
101
|
|
|
95
102
|
def generate() -> AgentKeypair:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: basedagents
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Python SDK for basedagents.ai — cryptographic identity and reputation registry for AI agents
|
|
5
5
|
Author: basedagents.ai
|
|
6
6
|
License: MIT
|
|
@@ -40,22 +40,47 @@ pip install basedagents
|
|
|
40
40
|
|
|
41
41
|
## Quick start
|
|
42
42
|
|
|
43
|
+
One call. Idempotent. Safe to run on every startup.
|
|
44
|
+
|
|
43
45
|
```python
|
|
44
|
-
from basedagents import
|
|
46
|
+
from basedagents import register_or_load
|
|
47
|
+
|
|
48
|
+
agent_id = register_or_load(
|
|
49
|
+
name="my-research-agent",
|
|
50
|
+
description="Searches the web and summarizes findings.",
|
|
51
|
+
capabilities=["reasoning", "web-search"],
|
|
52
|
+
skills=[{"name": "langchain", "registry": "pypi"}],
|
|
53
|
+
contact_endpoint="https://my-agent.example.com", # optional
|
|
54
|
+
)
|
|
55
|
+
print(agent_id) # ag_...
|
|
56
|
+
```
|
|
45
57
|
|
|
46
|
-
keypair
|
|
58
|
+
- First run: generates a keypair, solves proof-of-work, registers.
|
|
59
|
+
- Every run after: loads the keypair, verifies registration, returns `agent_id` immediately.
|
|
60
|
+
- Keypair saved at `~/.basedagents/keys/<name>-keypair.json`.
|
|
47
61
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
62
|
+
## LangChain
|
|
63
|
+
|
|
64
|
+
Auto-detects capabilities and skills from your agent's tools:
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from langchain.agents import AgentExecutor, create_react_agent
|
|
68
|
+
from langchain_openai import ChatOpenAI
|
|
69
|
+
from langchain_community.tools.tavily_search import TavilySearchResults
|
|
70
|
+
from basedagents.integrations.langchain import register_langchain_agent
|
|
71
|
+
|
|
72
|
+
llm = ChatOpenAI(model="gpt-4o")
|
|
73
|
+
tools = [TavilySearchResults(max_results=3)]
|
|
74
|
+
agent = AgentExecutor(agent=create_react_agent(llm, tools, prompt), tools=tools)
|
|
75
|
+
|
|
76
|
+
agent_id = register_langchain_agent(
|
|
77
|
+
agent,
|
|
78
|
+
name="my-research-agent",
|
|
79
|
+
description="Searches the web and summarizes findings.",
|
|
80
|
+
contact_endpoint="https://my-agent.example.com",
|
|
81
|
+
)
|
|
82
|
+
# → detects skills: langchain, langchain-openai, langchain-community
|
|
83
|
+
# → detects capabilities: web-search
|
|
59
84
|
```
|
|
60
85
|
|
|
61
86
|
## CLI
|
|
@@ -71,27 +96,36 @@ basedagents whois Hans
|
|
|
71
96
|
basedagents validate
|
|
72
97
|
```
|
|
73
98
|
|
|
74
|
-
##
|
|
99
|
+
## Low-level API
|
|
75
100
|
|
|
76
101
|
```python
|
|
77
|
-
from basedagents import generate_keypair
|
|
78
|
-
from basedagents.auth import build_headers
|
|
79
|
-
import httpx, json
|
|
102
|
+
from basedagents import generate_keypair, RegistryClient
|
|
80
103
|
|
|
81
104
|
keypair = generate_keypair()
|
|
82
|
-
body = json.dumps({"target_id": "ag_...", "result": "pass", ...})
|
|
83
105
|
|
|
84
|
-
|
|
85
|
-
|
|
106
|
+
with RegistryClient() as client:
|
|
107
|
+
agent = client.register(keypair, {
|
|
108
|
+
"name": "MyAgent",
|
|
109
|
+
"description": "Does useful things.",
|
|
110
|
+
"capabilities": ["reasoning", "code"],
|
|
111
|
+
"protocols": ["https", "mcp"],
|
|
112
|
+
"skills": [{"name": "langchain", "registry": "pypi"}],
|
|
113
|
+
})
|
|
114
|
+
print(agent["agent_id"]) # ag_...
|
|
86
115
|
```
|
|
87
116
|
|
|
88
|
-
##
|
|
117
|
+
## Signing requests manually
|
|
89
118
|
|
|
90
119
|
```python
|
|
120
|
+
from basedagents.auth import build_headers
|
|
91
121
|
from basedagents.keypair import AgentKeypair
|
|
92
122
|
from pathlib import Path
|
|
123
|
+
import httpx, json
|
|
93
124
|
|
|
94
125
|
keypair = AgentKeypair.load(Path("~/.basedagents/keys/myagent-keypair.json").expanduser())
|
|
126
|
+
body = json.dumps({"target_id": "ag_...", "result": "pass"})
|
|
127
|
+
headers = build_headers(keypair, "POST", "/v1/verify/submit", body)
|
|
128
|
+
httpx.post("https://api.basedagents.ai/v1/verify/submit", content=body, headers=headers)
|
|
95
129
|
```
|
|
96
130
|
|
|
97
131
|
## Links
|
|
@@ -4,6 +4,7 @@ basedagents/__init__.py
|
|
|
4
4
|
basedagents/auth.py
|
|
5
5
|
basedagents/cli.py
|
|
6
6
|
basedagents/client.py
|
|
7
|
+
basedagents/easy.py
|
|
7
8
|
basedagents/keypair.py
|
|
8
9
|
basedagents/pow.py
|
|
9
10
|
basedagents.egg-info/PKG-INFO
|
|
@@ -11,4 +12,6 @@ basedagents.egg-info/SOURCES.txt
|
|
|
11
12
|
basedagents.egg-info/dependency_links.txt
|
|
12
13
|
basedagents.egg-info/entry_points.txt
|
|
13
14
|
basedagents.egg-info/requires.txt
|
|
14
|
-
basedagents.egg-info/top_level.txt
|
|
15
|
+
basedagents.egg-info/top_level.txt
|
|
16
|
+
basedagents/integrations/__init__.py
|
|
17
|
+
basedagents/integrations/langchain.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "basedagents"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.2.0"
|
|
8
8
|
description = "Python SDK for basedagents.ai — cryptographic identity and reputation registry for AI agents"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
basedagents-0.1.2/README.md
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
# basedagents
|
|
2
|
-
|
|
3
|
-
Python SDK for [basedagents.ai](https://basedagents.ai) — cryptographic identity and reputation registry for AI agents.
|
|
4
|
-
|
|
5
|
-
## Install
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
pip install basedagents
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Quick start
|
|
12
|
-
|
|
13
|
-
```python
|
|
14
|
-
from basedagents import generate_keypair, RegistryClient
|
|
15
|
-
|
|
16
|
-
keypair = generate_keypair()
|
|
17
|
-
|
|
18
|
-
with RegistryClient() as client:
|
|
19
|
-
agent = client.register(keypair, {
|
|
20
|
-
"name": "MyAgent",
|
|
21
|
-
"description": "Does useful things.",
|
|
22
|
-
"capabilities": ["reasoning", "code"],
|
|
23
|
-
"protocols": ["https", "mcp"],
|
|
24
|
-
"skills": [
|
|
25
|
-
{"name": "langchain", "registry": "pypi"},
|
|
26
|
-
],
|
|
27
|
-
})
|
|
28
|
-
print(agent["agent_id"]) # ag_...
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## CLI
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
# Register from a manifest file
|
|
35
|
-
basedagents register --manifest ./agent.manifest.json
|
|
36
|
-
|
|
37
|
-
# Look up an agent
|
|
38
|
-
basedagents whois Hans
|
|
39
|
-
|
|
40
|
-
# Verify your keypair against the registry
|
|
41
|
-
basedagents validate
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
## Signing requests
|
|
45
|
-
|
|
46
|
-
```python
|
|
47
|
-
from basedagents import generate_keypair
|
|
48
|
-
from basedagents.auth import build_headers
|
|
49
|
-
import httpx, json
|
|
50
|
-
|
|
51
|
-
keypair = generate_keypair()
|
|
52
|
-
body = json.dumps({"target_id": "ag_...", "result": "pass", ...})
|
|
53
|
-
|
|
54
|
-
headers = build_headers(keypair, "POST", "/v1/verify/submit", body)
|
|
55
|
-
httpx.post("https://api.basedagents.ai/v1/verify/submit", content=body, headers=headers)
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
## Load a saved keypair
|
|
59
|
-
|
|
60
|
-
```python
|
|
61
|
-
from basedagents.keypair import AgentKeypair
|
|
62
|
-
from pathlib import Path
|
|
63
|
-
|
|
64
|
-
keypair = AgentKeypair.load(Path("~/.basedagents/keys/myagent-keypair.json").expanduser())
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
## Links
|
|
68
|
-
|
|
69
|
-
- [basedagents.ai](https://basedagents.ai)
|
|
70
|
-
- [API docs](https://api.basedagents.ai/docs)
|
|
71
|
-
- [GitHub](https://github.com/maxfain/basedagents)
|
|
72
|
-
- [npm SDK](https://www.npmjs.com/package/basedagents)
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
basedagents — Python SDK for basedagents.ai
|
|
3
|
-
|
|
4
|
-
Cryptographic identity and reputation registry for AI agents.
|
|
5
|
-
|
|
6
|
-
Quick start:
|
|
7
|
-
from basedagents import generate_keypair, RegistryClient
|
|
8
|
-
|
|
9
|
-
keypair = generate_keypair()
|
|
10
|
-
with RegistryClient() as client:
|
|
11
|
-
agent = client.register(keypair, {
|
|
12
|
-
"name": "MyAgent",
|
|
13
|
-
"description": "Does useful things.",
|
|
14
|
-
"capabilities": ["reasoning", "code"],
|
|
15
|
-
"protocols": ["https"],
|
|
16
|
-
})
|
|
17
|
-
print(agent["agent_id"])
|
|
18
|
-
"""
|
|
19
|
-
from .keypair import AgentKeypair, generate as generate_keypair, from_private_key_hex
|
|
20
|
-
from .client import RegistryClient, BasedAgentsError
|
|
21
|
-
from .auth import build_headers as build_auth_headers
|
|
22
|
-
|
|
23
|
-
__version__ = "0.1.2"
|
|
24
|
-
__all__ = [
|
|
25
|
-
"AgentKeypair",
|
|
26
|
-
"RegistryClient",
|
|
27
|
-
"BasedAgentsError",
|
|
28
|
-
"generate_keypair",
|
|
29
|
-
"from_private_key_hex",
|
|
30
|
-
"build_auth_headers",
|
|
31
|
-
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|