aip-agents 0.1.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.
- aip_agents-0.1.0/.gitignore +36 -0
- aip_agents-0.1.0/PKG-INFO +124 -0
- aip_agents-0.1.0/README.md +91 -0
- aip_agents-0.1.0/__init__.py +17 -0
- aip_agents-0.1.0/adapters/__init__.py +1 -0
- aip_agents-0.1.0/adapters/adk/__init__.py +3 -0
- aip_agents-0.1.0/adapters/adk/plugin.py +109 -0
- aip_agents-0.1.0/adapters/crewai/__init__.py +3 -0
- aip_agents-0.1.0/adapters/crewai/plugin.py +78 -0
- aip_agents-0.1.0/core/__init__.py +8 -0
- aip_agents-0.1.0/core/config.py +13 -0
- aip_agents-0.1.0/core/identity_manager.py +54 -0
- aip_agents-0.1.0/core/key_store.py +44 -0
- aip_agents-0.1.0/core/logger.py +27 -0
- aip_agents-0.1.0/core/token_manager.py +111 -0
- aip_agents-0.1.0/examples/__init__.py +0 -0
- aip_agents-0.1.0/examples/adk_example.py +57 -0
- aip_agents-0.1.0/examples/crewai_example.py +74 -0
- aip_agents-0.1.0/py.typed +0 -0
- aip_agents-0.1.0/pyproject.toml +46 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Rust
|
|
2
|
+
target/
|
|
3
|
+
Cargo.lock
|
|
4
|
+
|
|
5
|
+
# Python
|
|
6
|
+
__pycache__/
|
|
7
|
+
*.pyc
|
|
8
|
+
*.egg-info/
|
|
9
|
+
dist/
|
|
10
|
+
.venv/
|
|
11
|
+
*.so
|
|
12
|
+
**/.pytest_cache/
|
|
13
|
+
|
|
14
|
+
# Paper (local only)
|
|
15
|
+
paper/
|
|
16
|
+
texput.log
|
|
17
|
+
|
|
18
|
+
# IETF draft (local only)
|
|
19
|
+
ietf/
|
|
20
|
+
|
|
21
|
+
# Planning (local only)
|
|
22
|
+
planning/
|
|
23
|
+
|
|
24
|
+
# Archives
|
|
25
|
+
*.tar.gz
|
|
26
|
+
|
|
27
|
+
# IDE
|
|
28
|
+
.idea/
|
|
29
|
+
.vscode/
|
|
30
|
+
*.swp
|
|
31
|
+
|
|
32
|
+
# OS
|
|
33
|
+
.DS_Store
|
|
34
|
+
|
|
35
|
+
# Claude
|
|
36
|
+
.claude/
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aip-agents
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: AIP identity and delegation for AI agent frameworks
|
|
5
|
+
Project-URL: Homepage, https://github.com/sunilp/aip
|
|
6
|
+
Project-URL: Documentation, https://github.com/sunilp/aip#aip-agents
|
|
7
|
+
Project-URL: Repository, https://github.com/sunilp/aip
|
|
8
|
+
Author-email: Sunil Prakash <sunil@sunilprakash.com>
|
|
9
|
+
License-Expression: Apache-2.0
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Topic :: Security :: Cryptography
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Requires-Dist: base58>=2.1
|
|
18
|
+
Requires-Dist: biscuit-python>=0.4
|
|
19
|
+
Requires-Dist: cryptography>=43.0
|
|
20
|
+
Requires-Dist: pydantic>=2.0
|
|
21
|
+
Requires-Dist: pyjwt[crypto]>=2.9
|
|
22
|
+
Provides-Extra: adk
|
|
23
|
+
Requires-Dist: google-adk>=1.0; extra == 'adk'
|
|
24
|
+
Provides-Extra: all
|
|
25
|
+
Requires-Dist: crewai>=0.80; extra == 'all'
|
|
26
|
+
Requires-Dist: google-adk>=1.0; extra == 'all'
|
|
27
|
+
Provides-Extra: crewai
|
|
28
|
+
Requires-Dist: crewai>=0.80; extra == 'crewai'
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
# aip-agents
|
|
35
|
+
|
|
36
|
+
AIP identity and delegation for AI agent frameworks. Add cryptographic identity, scoped delegation chains, and audit-ready token flows to your CrewAI and Google ADK agents in 5 lines of code.
|
|
37
|
+
|
|
38
|
+
## Install
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install aip-agents[crewai] # CrewAI
|
|
42
|
+
pip install aip-agents[adk] # Google ADK
|
|
43
|
+
pip install aip-agents[all] # Both
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Quick Start: CrewAI
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
from crewai import Crew, Agent, Task
|
|
50
|
+
from aip_agents.adapters.crewai import AIPCrewPlugin
|
|
51
|
+
|
|
52
|
+
plugin = AIPCrewPlugin()
|
|
53
|
+
|
|
54
|
+
researcher = Agent(role="researcher", ...)
|
|
55
|
+
writer = Agent(role="writer", ...)
|
|
56
|
+
crew = Crew(agents=[researcher, writer], tasks=[...])
|
|
57
|
+
|
|
58
|
+
plugin.register(crew) # Each agent gets an AIP identity + delegation token
|
|
59
|
+
crew.kickoff()
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Quick Start: Google ADK
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
from google.adk import Agent, Runner
|
|
66
|
+
from aip_agents.adapters.adk import AIPAdkPlugin
|
|
67
|
+
|
|
68
|
+
plugin = AIPAdkPlugin()
|
|
69
|
+
|
|
70
|
+
agent = Agent(name="coordinator", sub_agents=[worker1, worker2], ...)
|
|
71
|
+
runner = Runner(agent=agent)
|
|
72
|
+
|
|
73
|
+
plugin.register(runner) # Walks agent tree, creates delegation chains
|
|
74
|
+
runner.run("task")
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## What You Get
|
|
78
|
+
|
|
79
|
+
When you register a plugin, every agent gets:
|
|
80
|
+
|
|
81
|
+
1. **Cryptographic identity** - An Ed25519 keypair and AIP identifier (`aip:key:ed25519:z...`)
|
|
82
|
+
2. **Delegation chain** - When a parent agent delegates to a sub-agent, a Biscuit token chain records the delegation with attenuated scope
|
|
83
|
+
3. **Tool call headers** - `X-AIP-Token` headers ready to attach to outgoing tool/MCP calls
|
|
84
|
+
|
|
85
|
+
Enable logging to see it in action:
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from aip_agents import AIPConfig
|
|
89
|
+
|
|
90
|
+
plugin = AIPCrewPlugin(AIPConfig(log_tokens=True))
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Output:
|
|
94
|
+
```
|
|
95
|
+
[AIP] Identity created: researcher -> aip:key:ed25519:z6Fk3...
|
|
96
|
+
[AIP] Delegation: manager -> researcher [scope: web_search] [chain depth: 2]
|
|
97
|
+
[AIP] Tool call: researcher -> web_search [chain depth: 3, verified]
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Configuration
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
AIPConfig(
|
|
104
|
+
app_name="my-app", # Root identity label
|
|
105
|
+
auto_identity=True, # Auto-assign identity to every agent
|
|
106
|
+
auto_delegation=True, # Auto-create delegation chains
|
|
107
|
+
persist_keys=False, # Save keys to ~/.aip/keys/
|
|
108
|
+
log_tokens=False, # Log token operations
|
|
109
|
+
default_scope=None, # Default scope for root token
|
|
110
|
+
)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## How It Works
|
|
114
|
+
|
|
115
|
+
- **Identity**: Each agent gets an Ed25519 keypair. The public key becomes the agent's AIP identifier.
|
|
116
|
+
- **Tokens**: Authority tokens use [Biscuit](https://www.biscuitsec.org/) - an append-only cryptographic token that enforces scope can only narrow, never widen.
|
|
117
|
+
- **Delegation**: When Agent A delegates to Agent B, a new block is appended to A's token with B's identity and attenuated scope. The chain is cryptographically verifiable.
|
|
118
|
+
- **Tool calls**: Tokens are attached via `X-AIP-Token` header, compatible with [AIP MCP middleware](https://github.com/sunilp/aip) for end-to-end verification.
|
|
119
|
+
|
|
120
|
+
## Links
|
|
121
|
+
|
|
122
|
+
- [AIP Specification](https://github.com/sunilp/aip/tree/main/spec)
|
|
123
|
+
- [AIP Paper (arXiv:2603.24775)](https://arxiv.org/abs/2603.24775)
|
|
124
|
+
- [GitHub](https://github.com/sunilp/aip)
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# aip-agents
|
|
2
|
+
|
|
3
|
+
AIP identity and delegation for AI agent frameworks. Add cryptographic identity, scoped delegation chains, and audit-ready token flows to your CrewAI and Google ADK agents in 5 lines of code.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install aip-agents[crewai] # CrewAI
|
|
9
|
+
pip install aip-agents[adk] # Google ADK
|
|
10
|
+
pip install aip-agents[all] # Both
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start: CrewAI
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from crewai import Crew, Agent, Task
|
|
17
|
+
from aip_agents.adapters.crewai import AIPCrewPlugin
|
|
18
|
+
|
|
19
|
+
plugin = AIPCrewPlugin()
|
|
20
|
+
|
|
21
|
+
researcher = Agent(role="researcher", ...)
|
|
22
|
+
writer = Agent(role="writer", ...)
|
|
23
|
+
crew = Crew(agents=[researcher, writer], tasks=[...])
|
|
24
|
+
|
|
25
|
+
plugin.register(crew) # Each agent gets an AIP identity + delegation token
|
|
26
|
+
crew.kickoff()
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Quick Start: Google ADK
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
from google.adk import Agent, Runner
|
|
33
|
+
from aip_agents.adapters.adk import AIPAdkPlugin
|
|
34
|
+
|
|
35
|
+
plugin = AIPAdkPlugin()
|
|
36
|
+
|
|
37
|
+
agent = Agent(name="coordinator", sub_agents=[worker1, worker2], ...)
|
|
38
|
+
runner = Runner(agent=agent)
|
|
39
|
+
|
|
40
|
+
plugin.register(runner) # Walks agent tree, creates delegation chains
|
|
41
|
+
runner.run("task")
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## What You Get
|
|
45
|
+
|
|
46
|
+
When you register a plugin, every agent gets:
|
|
47
|
+
|
|
48
|
+
1. **Cryptographic identity** - An Ed25519 keypair and AIP identifier (`aip:key:ed25519:z...`)
|
|
49
|
+
2. **Delegation chain** - When a parent agent delegates to a sub-agent, a Biscuit token chain records the delegation with attenuated scope
|
|
50
|
+
3. **Tool call headers** - `X-AIP-Token` headers ready to attach to outgoing tool/MCP calls
|
|
51
|
+
|
|
52
|
+
Enable logging to see it in action:
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
from aip_agents import AIPConfig
|
|
56
|
+
|
|
57
|
+
plugin = AIPCrewPlugin(AIPConfig(log_tokens=True))
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Output:
|
|
61
|
+
```
|
|
62
|
+
[AIP] Identity created: researcher -> aip:key:ed25519:z6Fk3...
|
|
63
|
+
[AIP] Delegation: manager -> researcher [scope: web_search] [chain depth: 2]
|
|
64
|
+
[AIP] Tool call: researcher -> web_search [chain depth: 3, verified]
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Configuration
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
AIPConfig(
|
|
71
|
+
app_name="my-app", # Root identity label
|
|
72
|
+
auto_identity=True, # Auto-assign identity to every agent
|
|
73
|
+
auto_delegation=True, # Auto-create delegation chains
|
|
74
|
+
persist_keys=False, # Save keys to ~/.aip/keys/
|
|
75
|
+
log_tokens=False, # Log token operations
|
|
76
|
+
default_scope=None, # Default scope for root token
|
|
77
|
+
)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## How It Works
|
|
81
|
+
|
|
82
|
+
- **Identity**: Each agent gets an Ed25519 keypair. The public key becomes the agent's AIP identifier.
|
|
83
|
+
- **Tokens**: Authority tokens use [Biscuit](https://www.biscuitsec.org/) - an append-only cryptographic token that enforces scope can only narrow, never widen.
|
|
84
|
+
- **Delegation**: When Agent A delegates to Agent B, a new block is appended to A's token with B's identity and attenuated scope. The chain is cryptographically verifiable.
|
|
85
|
+
- **Tool calls**: Tokens are attached via `X-AIP-Token` header, compatible with [AIP MCP middleware](https://github.com/sunilp/aip) for end-to-end verification.
|
|
86
|
+
|
|
87
|
+
## Links
|
|
88
|
+
|
|
89
|
+
- [AIP Specification](https://github.com/sunilp/aip/tree/main/spec)
|
|
90
|
+
- [AIP Paper (arXiv:2603.24775)](https://arxiv.org/abs/2603.24775)
|
|
91
|
+
- [GitHub](https://github.com/sunilp/aip)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""aip-agents: AIP identity and delegation for AI agent frameworks."""
|
|
2
|
+
|
|
3
|
+
from aip_agents.core.config import AIPConfig
|
|
4
|
+
from aip_agents.core.identity_manager import AIPIdentity, IdentityManager
|
|
5
|
+
from aip_agents.core.token_manager import TokenManager
|
|
6
|
+
from aip_agents.core.key_store import KeyStore
|
|
7
|
+
|
|
8
|
+
__version__ = "0.1.0"
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"AIPConfig",
|
|
12
|
+
"AIPIdentity",
|
|
13
|
+
"IdentityManager",
|
|
14
|
+
"KeyStore",
|
|
15
|
+
"TokenManager",
|
|
16
|
+
"__version__",
|
|
17
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Framework adapters for AIP identity."""
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from aip_agents.core.config import AIPConfig
|
|
4
|
+
from aip_agents.core.identity_manager import IdentityManager
|
|
5
|
+
from aip_agents.core.logger import AIPLogger
|
|
6
|
+
from aip_agents.core.token_manager import TokenManager
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AIPAdkPlugin:
|
|
10
|
+
"""AIP identity and delegation plugin for Google ADK."""
|
|
11
|
+
|
|
12
|
+
def __init__(self, config: AIPConfig | None = None):
|
|
13
|
+
self._config = config or AIPConfig()
|
|
14
|
+
self._identity_manager = IdentityManager(self._config)
|
|
15
|
+
self._token_manager = TokenManager(self._identity_manager, self._config)
|
|
16
|
+
self._logger = AIPLogger(enabled=self._config.log_tokens)
|
|
17
|
+
self._agent_tokens: dict[str, str] = {}
|
|
18
|
+
self._agent_scopes: dict[str, list[str]] = {}
|
|
19
|
+
# Tracks the name of the authority (chain root) whose keypair signed the biscuit
|
|
20
|
+
self._authority_name: dict[str, str] = {}
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def identity_manager(self) -> IdentityManager:
|
|
24
|
+
return self._identity_manager
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def token_manager(self) -> TokenManager:
|
|
28
|
+
return self._token_manager
|
|
29
|
+
|
|
30
|
+
def register(self, runner) -> None:
|
|
31
|
+
root_agent = runner.agent
|
|
32
|
+
self._register_agent_tree(root_agent, parent_name=None, authority_name=None)
|
|
33
|
+
|
|
34
|
+
def _register_agent_tree(
|
|
35
|
+
self,
|
|
36
|
+
agent,
|
|
37
|
+
parent_name: str | None,
|
|
38
|
+
authority_name: str | None,
|
|
39
|
+
) -> None:
|
|
40
|
+
name = agent.name
|
|
41
|
+
identity = self._identity_manager.register(name)
|
|
42
|
+
self._logger.identity_created(name, identity.aip_id)
|
|
43
|
+
scope = self._extract_tool_names(agent)
|
|
44
|
+
self._agent_scopes[name] = scope
|
|
45
|
+
|
|
46
|
+
if parent_name is None:
|
|
47
|
+
# Root agent: issue a chained authority token under its own key
|
|
48
|
+
token = self._token_manager.issue_chained(name, scope=scope)
|
|
49
|
+
self._agent_tokens[name] = token
|
|
50
|
+
self._authority_name[name] = name
|
|
51
|
+
self._logger.token_issued(name, scope, "chained")
|
|
52
|
+
effective_authority = name
|
|
53
|
+
else:
|
|
54
|
+
# Sub-agent: delegate from parent's token using the chain authority's key
|
|
55
|
+
parent_token = self._agent_tokens.get(parent_name)
|
|
56
|
+
if parent_token is not None:
|
|
57
|
+
# Always use the chain authority name so from_base64 uses the right pubkey
|
|
58
|
+
chain_authority = authority_name or parent_name
|
|
59
|
+
delegation_token = self._token_manager.delegate(
|
|
60
|
+
parent_token=parent_token,
|
|
61
|
+
parent_name=chain_authority,
|
|
62
|
+
child_name=name,
|
|
63
|
+
attenuated_scope=scope,
|
|
64
|
+
context=f"Sub-agent delegation: {parent_name} -> {name}",
|
|
65
|
+
)
|
|
66
|
+
self._agent_tokens[name] = delegation_token
|
|
67
|
+
self._authority_name[name] = chain_authority
|
|
68
|
+
depth = self._token_manager.chain_depth(delegation_token)
|
|
69
|
+
self._logger.delegation(parent_name, name, scope, depth)
|
|
70
|
+
effective_authority = chain_authority
|
|
71
|
+
else:
|
|
72
|
+
token = self._token_manager.issue_chained(name, scope=scope)
|
|
73
|
+
self._agent_tokens[name] = token
|
|
74
|
+
self._authority_name[name] = name
|
|
75
|
+
effective_authority = name
|
|
76
|
+
|
|
77
|
+
for sub_agent in getattr(agent, "sub_agents", []):
|
|
78
|
+
self._register_agent_tree(
|
|
79
|
+
sub_agent,
|
|
80
|
+
parent_name=name,
|
|
81
|
+
authority_name=effective_authority,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
def get_agent_token(self, name: str) -> str | None:
|
|
85
|
+
return self._agent_tokens.get(name)
|
|
86
|
+
|
|
87
|
+
def get_agent_scope(self, name: str) -> list[str]:
|
|
88
|
+
return self._agent_scopes.get(name, [])
|
|
89
|
+
|
|
90
|
+
def get_chain_depth(self, name: str) -> int:
|
|
91
|
+
token = self._agent_tokens.get(name)
|
|
92
|
+
if token is None:
|
|
93
|
+
raise ValueError(f"No token for agent '{name}'")
|
|
94
|
+
return self._token_manager.chain_depth(token)
|
|
95
|
+
|
|
96
|
+
def get_tool_call_headers(self, name: str) -> dict[str, str]:
|
|
97
|
+
token = self._agent_tokens.get(name)
|
|
98
|
+
if token is None:
|
|
99
|
+
return {}
|
|
100
|
+
return {"X-AIP-Token": token}
|
|
101
|
+
|
|
102
|
+
def _extract_tool_names(self, agent) -> list[str]:
|
|
103
|
+
names = []
|
|
104
|
+
for tool in getattr(agent, "tools", []):
|
|
105
|
+
if hasattr(tool, "name"):
|
|
106
|
+
names.append(tool.name)
|
|
107
|
+
elif isinstance(tool, str):
|
|
108
|
+
names.append(tool)
|
|
109
|
+
return names if names else ["*"]
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from aip_agents.core.config import AIPConfig
|
|
4
|
+
from aip_agents.core.identity_manager import IdentityManager
|
|
5
|
+
from aip_agents.core.logger import AIPLogger
|
|
6
|
+
from aip_agents.core.token_manager import TokenManager
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AIPCrewPlugin:
|
|
10
|
+
"""AIP identity and delegation plugin for CrewAI."""
|
|
11
|
+
|
|
12
|
+
def __init__(self, config: AIPConfig | None = None):
|
|
13
|
+
self._config = config or AIPConfig()
|
|
14
|
+
self._identity_manager = IdentityManager(self._config)
|
|
15
|
+
self._token_manager = TokenManager(self._identity_manager, self._config)
|
|
16
|
+
self._logger = AIPLogger(enabled=self._config.log_tokens)
|
|
17
|
+
self._agent_tokens: dict[str, str] = {}
|
|
18
|
+
self._agent_scopes: dict[str, list[str]] = {}
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def identity_manager(self) -> IdentityManager:
|
|
22
|
+
return self._identity_manager
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def token_manager(self) -> TokenManager:
|
|
26
|
+
return self._token_manager
|
|
27
|
+
|
|
28
|
+
def register(self, crew) -> None:
|
|
29
|
+
for agent in crew.agents:
|
|
30
|
+
role = agent.role
|
|
31
|
+
identity = self._identity_manager.register(role)
|
|
32
|
+
self._logger.identity_created(role, identity.aip_id)
|
|
33
|
+
scope = self._extract_tool_names(agent)
|
|
34
|
+
self._agent_scopes[role] = scope
|
|
35
|
+
if self._config.auto_delegation:
|
|
36
|
+
token = self._token_manager.issue_chained(role, scope=scope)
|
|
37
|
+
else:
|
|
38
|
+
token = self._token_manager.issue(role, scope=scope)
|
|
39
|
+
self._agent_tokens[role] = token
|
|
40
|
+
self._logger.token_issued(role, scope, "chained" if self._config.auto_delegation else "compact")
|
|
41
|
+
|
|
42
|
+
def get_agent_token(self, role: str) -> str | None:
|
|
43
|
+
return self._agent_tokens.get(role)
|
|
44
|
+
|
|
45
|
+
def get_agent_scope(self, role: str) -> list[str]:
|
|
46
|
+
return self._agent_scopes.get(role, [])
|
|
47
|
+
|
|
48
|
+
def create_delegation(self, parent_role: str, child_role: str, task_description: str, scope: list[str] | None = None) -> str:
|
|
49
|
+
parent_token = self._agent_tokens.get(parent_role)
|
|
50
|
+
if parent_token is None:
|
|
51
|
+
raise ValueError(f"No token found for agent '{parent_role}'")
|
|
52
|
+
effective_scope = scope or self._agent_scopes.get(child_role, [])
|
|
53
|
+
delegation_token = self._token_manager.delegate(
|
|
54
|
+
parent_token=parent_token,
|
|
55
|
+
parent_name=parent_role,
|
|
56
|
+
child_name=child_role,
|
|
57
|
+
attenuated_scope=effective_scope,
|
|
58
|
+
context=task_description,
|
|
59
|
+
)
|
|
60
|
+
depth = self._token_manager.chain_depth(delegation_token)
|
|
61
|
+
self._logger.delegation(parent_role, child_role, effective_scope, depth)
|
|
62
|
+
self._agent_tokens[child_role] = delegation_token
|
|
63
|
+
return delegation_token
|
|
64
|
+
|
|
65
|
+
def get_tool_call_headers(self, role: str) -> dict[str, str]:
|
|
66
|
+
token = self._agent_tokens.get(role)
|
|
67
|
+
if token is None:
|
|
68
|
+
return {}
|
|
69
|
+
return {"X-AIP-Token": token}
|
|
70
|
+
|
|
71
|
+
def _extract_tool_names(self, agent) -> list[str]:
|
|
72
|
+
names = []
|
|
73
|
+
for tool in getattr(agent, "tools", []):
|
|
74
|
+
if hasattr(tool, "name"):
|
|
75
|
+
names.append(tool.name)
|
|
76
|
+
elif isinstance(tool, str):
|
|
77
|
+
names.append(tool)
|
|
78
|
+
return names if names else ["*"]
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""Core identity, token, and key management."""
|
|
2
|
+
|
|
3
|
+
from aip_agents.core.config import AIPConfig
|
|
4
|
+
from aip_agents.core.identity_manager import IdentityManager, AIPIdentity
|
|
5
|
+
from aip_agents.core.key_store import KeyStore
|
|
6
|
+
from aip_agents.core.token_manager import TokenManager
|
|
7
|
+
|
|
8
|
+
__all__ = ["AIPConfig", "AIPIdentity", "IdentityManager", "KeyStore", "TokenManager"]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
@dataclass
|
|
5
|
+
class AIPConfig:
|
|
6
|
+
"""Configuration for AIP agent identity and delegation."""
|
|
7
|
+
|
|
8
|
+
app_name: str = "aip-app"
|
|
9
|
+
auto_identity: bool = True
|
|
10
|
+
auto_delegation: bool = True
|
|
11
|
+
persist_keys: bool = False
|
|
12
|
+
log_tokens: bool = False
|
|
13
|
+
default_scope: list[str] | None = None
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from aip_agents.core.config import AIPConfig
|
|
4
|
+
from aip_agents.core.key_store import KeyStore
|
|
5
|
+
from aip_core.crypto import KeyPair
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(frozen=True)
|
|
9
|
+
class AIPIdentity:
|
|
10
|
+
"""An agent's AIP identity."""
|
|
11
|
+
name: str
|
|
12
|
+
aip_id: str
|
|
13
|
+
public_key_bytes: bytes
|
|
14
|
+
keypair: KeyPair
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class IdentityManager:
|
|
18
|
+
"""Creates and caches AIP identities for agents."""
|
|
19
|
+
|
|
20
|
+
def __init__(self, config: AIPConfig):
|
|
21
|
+
self._config = config
|
|
22
|
+
persist_dir = None
|
|
23
|
+
if config.persist_keys:
|
|
24
|
+
import os
|
|
25
|
+
persist_dir = os.path.expanduser(f"~/.aip/keys/{config.app_name}")
|
|
26
|
+
self._key_store = KeyStore(persist_dir=persist_dir)
|
|
27
|
+
self._identities: dict[str, AIPIdentity] = {}
|
|
28
|
+
self._root = self._create_identity(config.app_name)
|
|
29
|
+
|
|
30
|
+
def _create_identity(self, name: str) -> AIPIdentity:
|
|
31
|
+
if name in self._identities:
|
|
32
|
+
return self._identities[name]
|
|
33
|
+
kp = self._key_store.get_or_create(name)
|
|
34
|
+
aip_id = f"aip:key:ed25519:{kp.public_key_multibase()}"
|
|
35
|
+
identity = AIPIdentity(
|
|
36
|
+
name=name,
|
|
37
|
+
aip_id=aip_id,
|
|
38
|
+
public_key_bytes=kp.public_key_bytes(),
|
|
39
|
+
keypair=kp,
|
|
40
|
+
)
|
|
41
|
+
self._identities[name] = identity
|
|
42
|
+
return identity
|
|
43
|
+
|
|
44
|
+
def root_identity(self) -> AIPIdentity:
|
|
45
|
+
return self._root
|
|
46
|
+
|
|
47
|
+
def register(self, agent_name: str) -> AIPIdentity:
|
|
48
|
+
return self._create_identity(agent_name)
|
|
49
|
+
|
|
50
|
+
def get(self, agent_name: str) -> AIPIdentity | None:
|
|
51
|
+
return self._identities.get(agent_name)
|
|
52
|
+
|
|
53
|
+
def all(self) -> list[AIPIdentity]:
|
|
54
|
+
return list(self._identities.values())
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from aip_core.crypto import KeyPair
|
|
4
|
+
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class KeyStore:
|
|
8
|
+
"""Manages Ed25519 keypairs for agents. In-memory by default, optional disk persistence."""
|
|
9
|
+
|
|
10
|
+
def __init__(self, persist_dir: str | None = None):
|
|
11
|
+
self._keys: dict[str, KeyPair] = {}
|
|
12
|
+
self._persist_dir = Path(persist_dir) if persist_dir else None
|
|
13
|
+
if self._persist_dir:
|
|
14
|
+
self._persist_dir.mkdir(parents=True, exist_ok=True)
|
|
15
|
+
|
|
16
|
+
def get_or_create(self, name: str) -> KeyPair:
|
|
17
|
+
if name in self._keys:
|
|
18
|
+
return self._keys[name]
|
|
19
|
+
|
|
20
|
+
if self._persist_dir:
|
|
21
|
+
key_file = self._persist_dir / f"{name}.key"
|
|
22
|
+
if key_file.exists():
|
|
23
|
+
raw = key_file.read_bytes()
|
|
24
|
+
private_key = Ed25519PrivateKey.from_private_bytes(raw)
|
|
25
|
+
kp = KeyPair(private_key)
|
|
26
|
+
self._keys[name] = kp
|
|
27
|
+
return kp
|
|
28
|
+
|
|
29
|
+
kp = KeyPair.generate()
|
|
30
|
+
self._keys[name] = kp
|
|
31
|
+
|
|
32
|
+
if self._persist_dir:
|
|
33
|
+
key_file = self._persist_dir / f"{name}.key"
|
|
34
|
+
key_file.write_bytes(kp.private_key_bytes())
|
|
35
|
+
key_file.chmod(0o600)
|
|
36
|
+
|
|
37
|
+
return kp
|
|
38
|
+
|
|
39
|
+
def has(self, name: str) -> bool:
|
|
40
|
+
if name in self._keys:
|
|
41
|
+
return True
|
|
42
|
+
if self._persist_dir:
|
|
43
|
+
return (self._persist_dir / f"{name}.key").exists()
|
|
44
|
+
return False
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
class AIPLogger:
|
|
2
|
+
"""Structured logging for AIP token operations."""
|
|
3
|
+
|
|
4
|
+
def __init__(self, enabled: bool = False):
|
|
5
|
+
self._enabled = enabled
|
|
6
|
+
|
|
7
|
+
def identity_created(self, name: str, aip_id: str) -> None:
|
|
8
|
+
if self._enabled:
|
|
9
|
+
print(f"[AIP] Identity created: {name} -> {aip_id}")
|
|
10
|
+
|
|
11
|
+
def token_issued(self, agent_name: str, scope: list[str], mode: str) -> None:
|
|
12
|
+
if self._enabled:
|
|
13
|
+
print(f"[AIP] Token issued: {agent_name} scope={scope} mode={mode}")
|
|
14
|
+
|
|
15
|
+
def delegation(self, parent: str, child: str, scope: list[str], chain_depth: int) -> None:
|
|
16
|
+
if self._enabled:
|
|
17
|
+
print(
|
|
18
|
+
f"[AIP] Delegation: {parent} -> {child} "
|
|
19
|
+
f"[scope: {','.join(scope)}] [chain depth: {chain_depth}]"
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
def tool_call(self, agent_name: str, tool_name: str, chain_depth: int) -> None:
|
|
23
|
+
if self._enabled:
|
|
24
|
+
print(
|
|
25
|
+
f"[AIP] Tool call: {agent_name} -> {tool_name} "
|
|
26
|
+
f"[chain depth: {chain_depth}]"
|
|
27
|
+
)
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import time
|
|
2
|
+
|
|
3
|
+
from aip_agents.core.config import AIPConfig
|
|
4
|
+
from aip_agents.core.identity_manager import IdentityManager
|
|
5
|
+
from aip_token.claims import AipClaims
|
|
6
|
+
from aip_token.compact import CompactToken
|
|
7
|
+
from aip_token.chained import ChainedToken
|
|
8
|
+
from aip_token.error import TokenError
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TokenManager:
|
|
12
|
+
"""Issues compact tokens and chained delegation tokens for agents."""
|
|
13
|
+
|
|
14
|
+
DEFAULT_TTL = 3600
|
|
15
|
+
|
|
16
|
+
def __init__(self, identity_manager: IdentityManager, config: AIPConfig):
|
|
17
|
+
self._id_mgr = identity_manager
|
|
18
|
+
self._config = config
|
|
19
|
+
|
|
20
|
+
def issue(self, agent_name: str, scope: list[str], ttl: int = DEFAULT_TTL) -> str:
|
|
21
|
+
identity = self._id_mgr.get(agent_name)
|
|
22
|
+
if identity is None:
|
|
23
|
+
raise TokenError(f"Agent '{agent_name}' not registered", "identity_unresolvable")
|
|
24
|
+
root = self._id_mgr.root_identity()
|
|
25
|
+
now = int(time.time())
|
|
26
|
+
claims = AipClaims(
|
|
27
|
+
iss=root.aip_id,
|
|
28
|
+
sub=identity.aip_id,
|
|
29
|
+
scope=scope,
|
|
30
|
+
max_depth=0,
|
|
31
|
+
iat=now,
|
|
32
|
+
exp=now + ttl,
|
|
33
|
+
)
|
|
34
|
+
return CompactToken.create(claims, root.keypair)
|
|
35
|
+
|
|
36
|
+
def verify(self, token_str: str, required_scope: str) -> CompactToken:
|
|
37
|
+
root = self._id_mgr.root_identity()
|
|
38
|
+
verified = CompactToken.verify(token_str, root.public_key_bytes)
|
|
39
|
+
if not verified.has_scope(required_scope):
|
|
40
|
+
raise TokenError(
|
|
41
|
+
f"Token does not authorize '{required_scope}'",
|
|
42
|
+
"scope_insufficient",
|
|
43
|
+
)
|
|
44
|
+
return verified
|
|
45
|
+
|
|
46
|
+
def issue_chained(
|
|
47
|
+
self,
|
|
48
|
+
agent_name: str,
|
|
49
|
+
scope: list[str],
|
|
50
|
+
max_depth: int = 5,
|
|
51
|
+
budget_cents: int | None = None,
|
|
52
|
+
ttl: int = DEFAULT_TTL,
|
|
53
|
+
) -> str:
|
|
54
|
+
identity = self._id_mgr.get(agent_name)
|
|
55
|
+
if identity is None:
|
|
56
|
+
raise TokenError(f"Agent '{agent_name}' not registered", "identity_unresolvable")
|
|
57
|
+
token = ChainedToken.create_authority(
|
|
58
|
+
issuer=identity.aip_id,
|
|
59
|
+
scopes=scope,
|
|
60
|
+
budget_cents=budget_cents,
|
|
61
|
+
max_depth=max_depth,
|
|
62
|
+
ttl_seconds=ttl,
|
|
63
|
+
keypair=identity.keypair,
|
|
64
|
+
)
|
|
65
|
+
return token.to_base64()
|
|
66
|
+
|
|
67
|
+
def delegate(
|
|
68
|
+
self,
|
|
69
|
+
parent_token: str,
|
|
70
|
+
parent_name: str,
|
|
71
|
+
child_name: str,
|
|
72
|
+
attenuated_scope: list[str],
|
|
73
|
+
context: str,
|
|
74
|
+
budget_cents: int | None = None,
|
|
75
|
+
) -> str:
|
|
76
|
+
parent_identity = self._id_mgr.get(parent_name)
|
|
77
|
+
child_identity = self._id_mgr.get(child_name)
|
|
78
|
+
if parent_identity is None:
|
|
79
|
+
raise TokenError(f"Agent '{parent_name}' not registered", "identity_unresolvable")
|
|
80
|
+
if child_identity is None:
|
|
81
|
+
raise TokenError(f"Agent '{child_name}' not registered", "identity_unresolvable")
|
|
82
|
+
chained = ChainedToken.from_base64(
|
|
83
|
+
parent_token, parent_identity.public_key_bytes
|
|
84
|
+
)
|
|
85
|
+
delegated = chained.delegate(
|
|
86
|
+
delegator=parent_identity.aip_id,
|
|
87
|
+
delegate=child_identity.aip_id,
|
|
88
|
+
scopes=attenuated_scope,
|
|
89
|
+
budget_cents=budget_cents,
|
|
90
|
+
context=context,
|
|
91
|
+
)
|
|
92
|
+
return delegated.to_base64()
|
|
93
|
+
|
|
94
|
+
def authorize_chained(self, token_str: str, tool: str) -> None:
|
|
95
|
+
for identity in self._id_mgr.all():
|
|
96
|
+
try:
|
|
97
|
+
chained = ChainedToken.from_base64(token_str, identity.public_key_bytes)
|
|
98
|
+
chained.authorize(tool, identity.public_key_bytes)
|
|
99
|
+
return
|
|
100
|
+
except Exception:
|
|
101
|
+
continue
|
|
102
|
+
raise TokenError(f"No valid authority found for tool '{tool}'", "scope_insufficient")
|
|
103
|
+
|
|
104
|
+
def chain_depth(self, token_str: str) -> int:
|
|
105
|
+
for identity in self._id_mgr.all():
|
|
106
|
+
try:
|
|
107
|
+
chained = ChainedToken.from_base64(token_str, identity.public_key_bytes)
|
|
108
|
+
return chained.current_depth()
|
|
109
|
+
except Exception:
|
|
110
|
+
continue
|
|
111
|
+
raise TokenError("Cannot determine chain depth", "token_malformed")
|
|
File without changes
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Example: Add AIP identity and delegation to Google ADK agents.
|
|
2
|
+
|
|
3
|
+
Usage:
|
|
4
|
+
pip install aip-agents[adk]
|
|
5
|
+
python adk_example.py
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from google.adk import Agent, Runner
|
|
9
|
+
from aip_agents import AIPConfig
|
|
10
|
+
from aip_agents.adapters.adk import AIPAdkPlugin
|
|
11
|
+
|
|
12
|
+
# 1. Create the plugin
|
|
13
|
+
plugin = AIPAdkPlugin(AIPConfig(
|
|
14
|
+
app_name="research-pipeline",
|
|
15
|
+
log_tokens=True,
|
|
16
|
+
))
|
|
17
|
+
|
|
18
|
+
# 2. Define sub-agents
|
|
19
|
+
summarizer = Agent(
|
|
20
|
+
name="summarizer",
|
|
21
|
+
model="gemini-2.5-flash",
|
|
22
|
+
instruction="Summarize the research findings concisely.",
|
|
23
|
+
description="Summarization specialist",
|
|
24
|
+
tools=[],
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
fact_checker = Agent(
|
|
28
|
+
name="fact_checker",
|
|
29
|
+
model="gemini-2.5-flash",
|
|
30
|
+
instruction="Verify claims against sources.",
|
|
31
|
+
description="Fact verification agent",
|
|
32
|
+
tools=[],
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# 3. Define coordinator with sub-agents
|
|
36
|
+
coordinator = Agent(
|
|
37
|
+
name="coordinator",
|
|
38
|
+
model="gemini-2.5-flash",
|
|
39
|
+
instruction="Route tasks to the appropriate sub-agent.",
|
|
40
|
+
description="Task coordinator",
|
|
41
|
+
tools=[],
|
|
42
|
+
sub_agents=[summarizer, fact_checker],
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# 4. Create runner and register plugin
|
|
46
|
+
runner = Runner(agent=coordinator)
|
|
47
|
+
plugin.register(runner)
|
|
48
|
+
|
|
49
|
+
# 5. Inspect delegation chains
|
|
50
|
+
print("\n--- Delegation Chain Depths ---")
|
|
51
|
+
for name in ["coordinator", "summarizer", "fact_checker"]:
|
|
52
|
+
depth = plugin.get_chain_depth(name)
|
|
53
|
+
print(f"{name}: depth {depth}")
|
|
54
|
+
|
|
55
|
+
print("\n--- Tool Call Headers ---")
|
|
56
|
+
headers = plugin.get_tool_call_headers("summarizer")
|
|
57
|
+
print(f"X-AIP-Token: {headers.get('X-AIP-Token', 'N/A')[:50]}...")
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""Example: Add AIP identity and delegation to a CrewAI crew.
|
|
2
|
+
|
|
3
|
+
Usage:
|
|
4
|
+
pip install aip-agents[crewai]
|
|
5
|
+
python crewai_example.py
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from crewai import Agent, Crew, Task, Process
|
|
9
|
+
from aip_agents import AIPConfig
|
|
10
|
+
from aip_agents.adapters.crewai import AIPCrewPlugin
|
|
11
|
+
|
|
12
|
+
# 1. Create the plugin (with logging to see what happens)
|
|
13
|
+
plugin = AIPCrewPlugin(AIPConfig(
|
|
14
|
+
app_name="research-crew",
|
|
15
|
+
log_tokens=True,
|
|
16
|
+
))
|
|
17
|
+
|
|
18
|
+
# 2. Define your agents as normal
|
|
19
|
+
researcher = Agent(
|
|
20
|
+
role="researcher",
|
|
21
|
+
goal="Find accurate information about AI agent identity protocols",
|
|
22
|
+
backstory="Expert at web research and source verification",
|
|
23
|
+
tools=[],
|
|
24
|
+
allow_delegation=False,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
writer = Agent(
|
|
28
|
+
role="writer",
|
|
29
|
+
goal="Write clear, concise summaries of research findings",
|
|
30
|
+
backstory="Technical writer specializing in AI systems",
|
|
31
|
+
tools=[],
|
|
32
|
+
allow_delegation=False,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# 3. Define tasks
|
|
36
|
+
research_task = Task(
|
|
37
|
+
description="Research the current state of AI agent identity protocols",
|
|
38
|
+
expected_output="A summary of existing protocols and gaps",
|
|
39
|
+
agent=researcher,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
write_task = Task(
|
|
43
|
+
description="Write a blog post based on the research",
|
|
44
|
+
expected_output="A 500-word blog post",
|
|
45
|
+
agent=writer,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# 4. Create crew and register plugin
|
|
49
|
+
crew = Crew(
|
|
50
|
+
agents=[researcher, writer],
|
|
51
|
+
tasks=[research_task, write_task],
|
|
52
|
+
process=Process.sequential,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
plugin.register(crew)
|
|
56
|
+
|
|
57
|
+
# 5. Access tokens programmatically
|
|
58
|
+
print("\n--- Agent Identities ---")
|
|
59
|
+
for agent_name in ["researcher", "writer"]:
|
|
60
|
+
identity = plugin.identity_manager.get(agent_name)
|
|
61
|
+
print(f"{agent_name}: {identity.aip_id}")
|
|
62
|
+
|
|
63
|
+
print("\n--- Tool Call Headers ---")
|
|
64
|
+
headers = plugin.get_tool_call_headers("researcher")
|
|
65
|
+
print(f"X-AIP-Token: {headers.get('X-AIP-Token', 'N/A')[:50]}...")
|
|
66
|
+
|
|
67
|
+
# 6. Create delegation
|
|
68
|
+
delegation_token = plugin.create_delegation(
|
|
69
|
+
parent_role="researcher",
|
|
70
|
+
child_role="writer",
|
|
71
|
+
task_description="Write summary based on research findings",
|
|
72
|
+
scope=["write", "summarize"],
|
|
73
|
+
)
|
|
74
|
+
print(f"\nDelegation chain depth: {plugin.token_manager.chain_depth(delegation_token)}")
|
|
File without changes
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "aip-agents"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "AIP identity and delegation for AI agent frameworks"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "Apache-2.0"
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Sunil Prakash", email = "sunil@sunilprakash.com" },
|
|
14
|
+
]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: Apache Software License",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Topic :: Security :: Cryptography",
|
|
21
|
+
"Topic :: Software Development :: Libraries",
|
|
22
|
+
]
|
|
23
|
+
dependencies = [
|
|
24
|
+
"cryptography>=43.0",
|
|
25
|
+
"PyJWT[crypto]>=2.9",
|
|
26
|
+
"pydantic>=2.0",
|
|
27
|
+
"base58>=2.1",
|
|
28
|
+
"biscuit-python>=0.4",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.optional-dependencies]
|
|
32
|
+
crewai = ["crewai>=0.80"]
|
|
33
|
+
adk = ["google-adk>=1.0"]
|
|
34
|
+
all = ["aip-agents[crewai,adk]"]
|
|
35
|
+
dev = ["pytest>=8.0", "pytest-asyncio>=0.24"]
|
|
36
|
+
|
|
37
|
+
[project.urls]
|
|
38
|
+
Homepage = "https://github.com/sunilp/aip"
|
|
39
|
+
Documentation = "https://github.com/sunilp/aip#aip-agents"
|
|
40
|
+
Repository = "https://github.com/sunilp/aip"
|
|
41
|
+
|
|
42
|
+
[tool.hatch.build.targets.wheel]
|
|
43
|
+
packages = ["aip_agents"]
|
|
44
|
+
|
|
45
|
+
[tool.hatch.build]
|
|
46
|
+
root = "."
|