basedagents 0.1.2__tar.gz → 0.3.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.3.0}/PKG-INFO +56 -22
- basedagents-0.3.0/README.md +106 -0
- basedagents-0.3.0/basedagents/__init__.py +51 -0
- {basedagents-0.1.2 → basedagents-0.3.0}/basedagents/cli.py +1 -1
- basedagents-0.3.0/basedagents/easy.py +142 -0
- basedagents-0.3.0/basedagents/integrations/__init__.py +8 -0
- basedagents-0.3.0/basedagents/integrations/autogen.py +224 -0
- basedagents-0.3.0/basedagents/integrations/crewai.py +251 -0
- basedagents-0.3.0/basedagents/integrations/langchain.py +224 -0
- {basedagents-0.1.2 → basedagents-0.3.0}/basedagents/keypair.py +8 -1
- basedagents-0.3.0/basedagents/middleware.py +316 -0
- {basedagents-0.1.2 → basedagents-0.3.0}/basedagents.egg-info/PKG-INFO +56 -22
- {basedagents-0.1.2 → basedagents-0.3.0}/basedagents.egg-info/SOURCES.txt +7 -1
- {basedagents-0.1.2 → basedagents-0.3.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.3.0}/basedagents/auth.py +0 -0
- {basedagents-0.1.2 → basedagents-0.3.0}/basedagents/client.py +0 -0
- {basedagents-0.1.2 → basedagents-0.3.0}/basedagents/pow.py +0 -0
- {basedagents-0.1.2 → basedagents-0.3.0}/basedagents.egg-info/dependency_links.txt +0 -0
- {basedagents-0.1.2 → basedagents-0.3.0}/basedagents.egg-info/entry_points.txt +0 -0
- {basedagents-0.1.2 → basedagents-0.3.0}/basedagents.egg-info/requires.txt +0 -0
- {basedagents-0.1.2 → basedagents-0.3.0}/basedagents.egg-info/top_level.txt +0 -0
- {basedagents-0.1.2 → basedagents-0.3.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.3.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,51 @@
|
|
|
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
|
+
from .middleware import require_agent, verify_request, fetch_attestation, VerifiedAgent
|
|
37
|
+
|
|
38
|
+
__version__ = "0.3.0"
|
|
39
|
+
__all__ = [
|
|
40
|
+
"register_or_load",
|
|
41
|
+
"require_agent",
|
|
42
|
+
"verify_request",
|
|
43
|
+
"fetch_attestation",
|
|
44
|
+
"VerifiedAgent",
|
|
45
|
+
"AgentKeypair",
|
|
46
|
+
"RegistryClient",
|
|
47
|
+
"BasedAgentsError",
|
|
48
|
+
"generate_keypair",
|
|
49
|
+
"from_private_key_hex",
|
|
50
|
+
"build_auth_headers",
|
|
51
|
+
]
|
|
@@ -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
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AutoGen integration for basedagents.
|
|
3
|
+
|
|
4
|
+
Introspects an AutoGen ConversableAgent, AssistantAgent, or GroupChat and
|
|
5
|
+
auto-populates capabilities and skills for registration.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
from autogen import AssistantAgent, GroupChat
|
|
9
|
+
from basedagents.integrations.autogen import register_autogen_agent
|
|
10
|
+
|
|
11
|
+
# Single agent
|
|
12
|
+
agent_id = register_autogen_agent(
|
|
13
|
+
assistant,
|
|
14
|
+
name="my-autogen-assistant",
|
|
15
|
+
description="Writes and executes Python code.",
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
# GroupChat (multi-agent)
|
|
19
|
+
agent_id = register_autogen_agent(
|
|
20
|
+
groupchat,
|
|
21
|
+
name="my-autogen-group",
|
|
22
|
+
description="Research + coding multi-agent system.",
|
|
23
|
+
)
|
|
24
|
+
"""
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
from typing import Any
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# ── Known AutoGen agent class names → capabilities ────────────────────────────
|
|
31
|
+
_AGENT_CLASS_CAPABILITIES: dict[str, list[str]] = {
|
|
32
|
+
"AssistantAgent": ["reasoning", "code"],
|
|
33
|
+
"UserProxyAgent": ["code"], # executes code by default
|
|
34
|
+
"GPTAssistantAgent": ["reasoning", "code"],
|
|
35
|
+
"RetrieveAssistantAgent": ["reasoning", "knowledge"],
|
|
36
|
+
"RetrieveUserProxyAgent": ["knowledge"],
|
|
37
|
+
"MathUserProxyAgent": ["reasoning"],
|
|
38
|
+
"TeachableAgent": ["reasoning", "knowledge"],
|
|
39
|
+
"CompressibleAgent": ["reasoning"],
|
|
40
|
+
"TransformMessages": ["reasoning"],
|
|
41
|
+
"WebSurferAgent": ["web-search", "web-scraping"],
|
|
42
|
+
"MultimodalConversableAgent": ["vision", "reasoning"],
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# ── Code execution detection ──────────────────────────────────────────────────
|
|
46
|
+
_CODE_EXEC_CLASSES = {"UserProxyAgent", "GPTAssistantAgent"}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _collect_agents(agent_or_group: Any) -> list[Any]:
|
|
50
|
+
"""Extract agents from GroupChat, GroupChatManager, or return single agent."""
|
|
51
|
+
# GroupChat has .agents
|
|
52
|
+
agents = getattr(agent_or_group, "agents", None)
|
|
53
|
+
if agents:
|
|
54
|
+
return list(agents)
|
|
55
|
+
# GroupChatManager has .groupchat.agents
|
|
56
|
+
gc = getattr(agent_or_group, "groupchat", None)
|
|
57
|
+
if gc:
|
|
58
|
+
return list(getattr(gc, "agents", []) or [])
|
|
59
|
+
# Single agent
|
|
60
|
+
return [agent_or_group]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _is_multi_agent(agent_or_group: Any) -> bool:
|
|
64
|
+
return len(_collect_agents(agent_or_group)) > 1
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _agent_executes_code(agent: Any) -> bool:
|
|
68
|
+
"""Check if an agent is configured to execute code."""
|
|
69
|
+
cls = type(agent).__name__
|
|
70
|
+
if cls in _CODE_EXEC_CLASSES:
|
|
71
|
+
return True
|
|
72
|
+
# human_input_mode="NEVER" + code_execution_config set = code executor
|
|
73
|
+
code_cfg = getattr(agent, "code_execution_config", None)
|
|
74
|
+
if code_cfg and code_cfg is not False:
|
|
75
|
+
return True
|
|
76
|
+
return False
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _detect_llm_skill(agent: Any) -> str | None:
|
|
80
|
+
"""Try to detect LLM provider package from llm_config."""
|
|
81
|
+
llm_config = getattr(agent, "llm_config", None) or {}
|
|
82
|
+
if not isinstance(llm_config, dict):
|
|
83
|
+
return None
|
|
84
|
+
model = llm_config.get("model", "") or ""
|
|
85
|
+
config_list = llm_config.get("config_list", [{}])
|
|
86
|
+
if config_list:
|
|
87
|
+
model = model or config_list[0].get("model", "")
|
|
88
|
+
model = model.lower()
|
|
89
|
+
if "gpt" in model or "o1" in model or "o3" in model:
|
|
90
|
+
return "pyautogen"
|
|
91
|
+
if "claude" in model:
|
|
92
|
+
return "pyautogen"
|
|
93
|
+
if "gemini" in model:
|
|
94
|
+
return "pyautogen"
|
|
95
|
+
return "pyautogen" # always include base pyautogen
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def extract_profile(
|
|
99
|
+
agent_or_group: Any,
|
|
100
|
+
extra_capabilities: list[str] | None = None,
|
|
101
|
+
extra_skills: list[dict[str, str]] | None = None,
|
|
102
|
+
) -> dict[str, Any]:
|
|
103
|
+
"""
|
|
104
|
+
Introspect an AutoGen agent or GroupChat and return a partial profile dict
|
|
105
|
+
with auto-detected capabilities and skills.
|
|
106
|
+
"""
|
|
107
|
+
agents = _collect_agents(agent_or_group)
|
|
108
|
+
capabilities: set[str] = set(extra_capabilities or [])
|
|
109
|
+
skills_seen: set[str] = set()
|
|
110
|
+
skills: list[dict[str, str]] = list(extra_skills or [])
|
|
111
|
+
|
|
112
|
+
# Base skill
|
|
113
|
+
skills.append({"name": "pyautogen", "registry": "pypi"})
|
|
114
|
+
skills_seen.add("pyautogen")
|
|
115
|
+
|
|
116
|
+
if _is_multi_agent(agent_or_group):
|
|
117
|
+
capabilities.add("multi-agent")
|
|
118
|
+
|
|
119
|
+
for agent in agents:
|
|
120
|
+
cls_name = type(agent).__name__
|
|
121
|
+
|
|
122
|
+
for cap in _AGENT_CLASS_CAPABILITIES.get(cls_name, []):
|
|
123
|
+
capabilities.add(cap)
|
|
124
|
+
|
|
125
|
+
if _agent_executes_code(agent):
|
|
126
|
+
capabilities.add("code")
|
|
127
|
+
|
|
128
|
+
# Detect function/tool calling
|
|
129
|
+
fn_map = getattr(agent, "function_map", None) or {}
|
|
130
|
+
if fn_map:
|
|
131
|
+
capabilities.add("tool-use")
|
|
132
|
+
|
|
133
|
+
# Detect retrieval augmentation
|
|
134
|
+
retrieve_config = getattr(agent, "retrieve_config", None)
|
|
135
|
+
if retrieve_config:
|
|
136
|
+
capabilities.add("knowledge")
|
|
137
|
+
|
|
138
|
+
# Extra skill from LLM config
|
|
139
|
+
pkg = _detect_llm_skill(agent)
|
|
140
|
+
if pkg and pkg not in skills_seen:
|
|
141
|
+
skills.append({"name": pkg, "registry": "pypi"})
|
|
142
|
+
skills_seen.add(pkg)
|
|
143
|
+
|
|
144
|
+
# Fallback capability
|
|
145
|
+
if not capabilities:
|
|
146
|
+
capabilities.add("reasoning")
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
"capabilities": sorted(capabilities),
|
|
150
|
+
"protocols": ["https"],
|
|
151
|
+
"skills": skills,
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def register_autogen_agent(
|
|
156
|
+
agent_or_group: Any,
|
|
157
|
+
name: str,
|
|
158
|
+
description: str = "",
|
|
159
|
+
contact_endpoint: str | None = None,
|
|
160
|
+
organization: str | None = None,
|
|
161
|
+
version: str | None = None,
|
|
162
|
+
tags: list[str] | None = None,
|
|
163
|
+
extra_capabilities: list[str] | None = None,
|
|
164
|
+
extra_skills: list[dict[str, str]] | None = None,
|
|
165
|
+
keypair_path: str | None = None,
|
|
166
|
+
api_url: str | None = None,
|
|
167
|
+
verbose: bool = True,
|
|
168
|
+
) -> str:
|
|
169
|
+
"""
|
|
170
|
+
Register an AutoGen agent or GroupChat with basedagents.ai.
|
|
171
|
+
|
|
172
|
+
Introspects the agent to auto-detect capabilities and skills.
|
|
173
|
+
Idempotent — safe to call on every startup.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
agent_or_group: AutoGen ConversableAgent, AssistantAgent, UserProxyAgent,
|
|
177
|
+
GroupChat, or GroupChatManager
|
|
178
|
+
name: Unique agent name (globally unique on registry)
|
|
179
|
+
description: What this agent/group does
|
|
180
|
+
contact_endpoint: URL where the agent can be reached for verification
|
|
181
|
+
organization: Optional org name
|
|
182
|
+
version: Optional version string
|
|
183
|
+
tags: Optional extra tags
|
|
184
|
+
extra_capabilities: Additional capabilities beyond auto-detected ones
|
|
185
|
+
extra_skills: Additional skills beyond auto-detected ones
|
|
186
|
+
keypair_path: Override keypair file location
|
|
187
|
+
api_url: Override API URL (defaults to BASEDAGENTS_API env or prod)
|
|
188
|
+
verbose: Print progress (default True)
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
agent_id string
|
|
192
|
+
|
|
193
|
+
Example:
|
|
194
|
+
import autogen
|
|
195
|
+
from basedagents.integrations.autogen import register_autogen_agent
|
|
196
|
+
|
|
197
|
+
assistant = autogen.AssistantAgent("assistant", llm_config={"model": "gpt-4o"})
|
|
198
|
+
user_proxy = autogen.UserProxyAgent("user_proxy", code_execution_config={"work_dir": "."})
|
|
199
|
+
|
|
200
|
+
agent_id = register_autogen_agent(
|
|
201
|
+
assistant,
|
|
202
|
+
name="my-autogen-assistant",
|
|
203
|
+
description="Writes and debugs Python code via GPT-4o.",
|
|
204
|
+
)
|
|
205
|
+
"""
|
|
206
|
+
from ..easy import register_or_load
|
|
207
|
+
|
|
208
|
+
profile = extract_profile(agent_or_group, extra_capabilities, extra_skills)
|
|
209
|
+
merged_tags = list(set(["autogen"] + (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
|
+
)
|