haive-hap 1.0.0__py3-none-any.whl
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.
- haive/hap/README.md +118 -0
- haive/hap/__init__.py +66 -0
- haive/hap/cli/__init__.py +0 -0
- haive/hap/client/__init__.py +5 -0
- haive/hap/client/base.py +8 -0
- haive/hap/client/http.py +31 -0
- haive/hap/client/local.py +21 -0
- haive/hap/context.py +295 -0
- haive/hap/models/README.md +86 -0
- haive/hap/models/__init__.py +12 -0
- haive/hap/models/context.py +69 -0
- haive/hap/models/graph.py +184 -0
- haive/hap/models/manifest.py +11 -0
- haive/hap/server/README.md +103 -0
- haive/hap/server/__init__.py +0 -0
- haive/hap/server/runtime.py +87 -0
- haive/hap/server/typing.py +5 -0
- haive/hap/server.py +535 -0
- haive/hap/servers/__init__.py +9 -0
- haive/hap/servers/agent.py +421 -0
- haive/hap/transports/__init__.py +10 -0
- haive/hap/transports/stdio.py +267 -0
- haive/hap/types/__init__.py +55 -0
- haive/hap/types/agents.py +186 -0
- haive/hap/types/aliases.py +4 -0
- haive/hap/types/engines.py +123 -0
- haive/hap/types/graphs.py +177 -0
- haive/hap/types/nodes.py +167 -0
- haive/hap/types/protocol.py +978 -0
- haive/hap/types/schemas.py +176 -0
- haive_hap-1.0.0.dist-info/LICENSE +21 -0
- haive_hap-1.0.0.dist-info/METADATA +309 -0
- haive_hap-1.0.0.dist-info/RECORD +34 -0
- haive_hap-1.0.0.dist-info/WHEEL +4 -0
haive/hap/README.md
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# HAP (Haive Agent Protocol) Module
|
|
2
|
+
|
|
3
|
+
This module implements the Haive Agent Protocol - a JSON-RPC 2.0 based protocol for agent communication and orchestration.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
HAP is the protocol layer that enables:
|
|
8
|
+
- Agent discovery and registration
|
|
9
|
+
- Resource management
|
|
10
|
+
- Progress tracking
|
|
11
|
+
- Authentication and authorization
|
|
12
|
+
- Tool and prompt exposure
|
|
13
|
+
|
|
14
|
+
## Components
|
|
15
|
+
|
|
16
|
+
### Core Classes
|
|
17
|
+
|
|
18
|
+
- **HAPContext**: Execution context with logging, progress, and resource access
|
|
19
|
+
- **HAPServer**: JSON-RPC server for agent exposure
|
|
20
|
+
- **HAPClient**: Client for interacting with HAP servers
|
|
21
|
+
- **Resource Providers**: Pluggable resource access
|
|
22
|
+
- **Auth Providers**: Authentication and authorization
|
|
23
|
+
|
|
24
|
+
### Protocol Structure
|
|
25
|
+
|
|
26
|
+
HAP follows JSON-RPC 2.0 with extensions for:
|
|
27
|
+
- Progress notifications
|
|
28
|
+
- Streaming responses
|
|
29
|
+
- Resource URIs
|
|
30
|
+
- Authentication tokens
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
# Example HAP request
|
|
34
|
+
{
|
|
35
|
+
"jsonrpc": "2.0",
|
|
36
|
+
"method": "agent/execute",
|
|
37
|
+
"params": {
|
|
38
|
+
"agent": "analyzer",
|
|
39
|
+
"input": {"text": "..."}
|
|
40
|
+
},
|
|
41
|
+
"id": "req-123"
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Key Features
|
|
46
|
+
|
|
47
|
+
### Context Management
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from haive.hap.hap import HAPContext
|
|
51
|
+
|
|
52
|
+
context = HAPContext(request_id="req-123")
|
|
53
|
+
await context.info("Starting agent execution")
|
|
54
|
+
await context.report_progress(0, 100, "Initializing")
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Resource Access
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
# Read resources through context
|
|
61
|
+
data = await context.read_resource("file://data.json")
|
|
62
|
+
config = await context.read_resource("config://agent/settings")
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Authentication
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
from haive.hap.hap import SimpleAuthProvider
|
|
69
|
+
|
|
70
|
+
auth = SimpleAuthProvider(user="alice", scopes=["execute", "read"])
|
|
71
|
+
context = HAPContext(auth_provider=auth)
|
|
72
|
+
|
|
73
|
+
if context.has_scope("execute"):
|
|
74
|
+
# Allowed to execute agents
|
|
75
|
+
pass
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Integration with AGP
|
|
79
|
+
|
|
80
|
+
HAP provides the protocol layer for AGP:
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
# Future implementation
|
|
84
|
+
server = HAPServer()
|
|
85
|
+
|
|
86
|
+
# Register HAP runtime
|
|
87
|
+
server.register_runtime(agp_runtime)
|
|
88
|
+
|
|
89
|
+
# Expose agents
|
|
90
|
+
server.expose_agent("analyzer", analyzer_agent)
|
|
91
|
+
server.expose_agent("writer", writer_agent)
|
|
92
|
+
|
|
93
|
+
# Start server
|
|
94
|
+
await server.start(port=8080)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Protocol Methods
|
|
98
|
+
|
|
99
|
+
### Standard Methods
|
|
100
|
+
|
|
101
|
+
- `agent/list` - List available agents
|
|
102
|
+
- `agent/execute` - Execute an agent
|
|
103
|
+
- `agent/describe` - Get agent metadata
|
|
104
|
+
- `resource/read` - Read a resource
|
|
105
|
+
- `resource/list` - List available resources
|
|
106
|
+
|
|
107
|
+
### Notifications
|
|
108
|
+
|
|
109
|
+
- `progress` - Progress updates
|
|
110
|
+
- `log` - Log messages
|
|
111
|
+
- `state` - State changes
|
|
112
|
+
|
|
113
|
+
## Future Development
|
|
114
|
+
|
|
115
|
+
1. **Full Protocol Implementation**: Complete JSON-RPC server/client
|
|
116
|
+
2. **MCP Compatibility**: Bridge between HAP and MCP
|
|
117
|
+
3. **Discovery Service**: Agent discovery and registration
|
|
118
|
+
4. **Distributed Execution**: Multi-server agent orchestration
|
haive/hap/__init__.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Haive Agent Protocol (HAP) - MCP-like server for agents and graphs.
|
|
3
|
+
|
|
4
|
+
This module provides a standardized protocol for exposing Haive agents,
|
|
5
|
+
graphs, and state schemas as servers with tools, resources, and prompts.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
# Core models
|
|
9
|
+
from haive.hap.models.graph import HAPGraph, HAPNode
|
|
10
|
+
from haive.hap.models.context import HAPContext
|
|
11
|
+
|
|
12
|
+
# Protocol types from types package
|
|
13
|
+
from haive.hap.types import (
|
|
14
|
+
HAPRequest,
|
|
15
|
+
HAPResponse,
|
|
16
|
+
HAPError,
|
|
17
|
+
ErrorCode,
|
|
18
|
+
ToolInfo,
|
|
19
|
+
ResourceInfo,
|
|
20
|
+
PromptInfo,
|
|
21
|
+
PromptArgument,
|
|
22
|
+
AgentInfo,
|
|
23
|
+
GraphInfo,
|
|
24
|
+
NodeInfo,
|
|
25
|
+
EdgeInfo,
|
|
26
|
+
ServerRegistration,
|
|
27
|
+
ServerCapability,
|
|
28
|
+
AgentExecutionRequest,
|
|
29
|
+
AgentExecutionResult,
|
|
30
|
+
GraphExecutionRequest,
|
|
31
|
+
GraphExecutionResult,
|
|
32
|
+
BaseInfo,
|
|
33
|
+
# Aliases
|
|
34
|
+
AgentId,
|
|
35
|
+
Entrypoint,
|
|
36
|
+
JSONMap
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
__version__ = "0.1.0"
|
|
40
|
+
|
|
41
|
+
__all__ = [
|
|
42
|
+
# Core models
|
|
43
|
+
"HAPGraph",
|
|
44
|
+
"HAPNode",
|
|
45
|
+
"HAPContext",
|
|
46
|
+
# Protocol types
|
|
47
|
+
"HAPRequest",
|
|
48
|
+
"HAPResponse",
|
|
49
|
+
"HAPError",
|
|
50
|
+
"ErrorCode",
|
|
51
|
+
"ToolInfo",
|
|
52
|
+
"ResourceInfo",
|
|
53
|
+
"PromptInfo",
|
|
54
|
+
"PromptArgument",
|
|
55
|
+
"AgentInfo",
|
|
56
|
+
"GraphInfo",
|
|
57
|
+
"NodeInfo",
|
|
58
|
+
"EdgeInfo",
|
|
59
|
+
"ServerRegistration",
|
|
60
|
+
"ServerCapability",
|
|
61
|
+
"AgentExecutionRequest",
|
|
62
|
+
"AgentExecutionResult",
|
|
63
|
+
"GraphExecutionRequest",
|
|
64
|
+
"GraphExecutionResult",
|
|
65
|
+
"BaseInfo"
|
|
66
|
+
]
|
|
File without changes
|
haive/hap/client/base.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import Protocol, Mapping, Any
|
|
3
|
+
from ..models.context import HAPContext
|
|
4
|
+
from ..models.graph import AgentGraph
|
|
5
|
+
|
|
6
|
+
class HAPClientProtocol(Protocol):
|
|
7
|
+
def run_agent(self, entrypoint: str, *, inputs: Mapping[str, Any] | None = None) -> HAPContext: ...
|
|
8
|
+
def run_graph(self, graph: AgentGraph, *, inputs: Mapping[str, Any] | None = None) -> HAPContext: ...
|
haive/hap/client/http.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import Mapping, Any
|
|
3
|
+
import json
|
|
4
|
+
import urllib.request
|
|
5
|
+
from ..models.context import HAPContext
|
|
6
|
+
from ..models.graph import AgentGraph
|
|
7
|
+
|
|
8
|
+
class HTTPClient:
|
|
9
|
+
"""Very small HTTP client for a future HAP server.
|
|
10
|
+
|
|
11
|
+
Expects a server that exposes /run-agent and /run-graph endpoints.
|
|
12
|
+
|
|
13
|
+
This is a placeholder you can wire to your FastAPI app later.
|
|
14
|
+
"""
|
|
15
|
+
def __init__(self, base_url: str, *, timeout: float = 30.0):
|
|
16
|
+
self.base_url = base_url.rstrip("/")
|
|
17
|
+
self.timeout = timeout
|
|
18
|
+
|
|
19
|
+
def _post_json(self, path: str, payload: dict) -> dict:
|
|
20
|
+
data = json.dumps(payload).encode("utf-8")
|
|
21
|
+
req = urllib.request.Request(self.base_url + path, data=data, headers={"Content-Type": "application/json"})
|
|
22
|
+
with urllib.request.urlopen(req, timeout=self.timeout) as resp:
|
|
23
|
+
return json.loads(resp.read().decode("utf-8"))
|
|
24
|
+
|
|
25
|
+
def run_agent(self, entrypoint: str, *, inputs: Mapping[str, Any] | None = None) -> HAPContext:
|
|
26
|
+
res = self._post_json("/run-agent", {"entrypoint": entrypoint, "inputs": dict(inputs or {})})
|
|
27
|
+
return HAPContext.model_validate(res)
|
|
28
|
+
|
|
29
|
+
def run_graph(self, graph: AgentGraph, *, inputs: Mapping[str, Any] | None = None) -> HAPContext:
|
|
30
|
+
res = self._post_json("/run-graph", {"graph": graph.model_dump(), "inputs": dict(inputs or {})})
|
|
31
|
+
return HAPContext.model_validate(res)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import Mapping, Any
|
|
3
|
+
from ..models.context import HAPContext
|
|
4
|
+
from ..models.graph import AgentGraph
|
|
5
|
+
from ..server.runtime import HAPRuntime
|
|
6
|
+
import importlib
|
|
7
|
+
from haive.agents.base.agent import Agent
|
|
8
|
+
|
|
9
|
+
class LocalClient:
|
|
10
|
+
"""Runs agents/graphs in-process using the local runtime."""
|
|
11
|
+
|
|
12
|
+
def run_agent(self, entrypoint: str, *, inputs: Mapping[str, Any] | None = None) -> HAPContext:
|
|
13
|
+
ctx = HAPContext(inputs=dict(inputs or {}))
|
|
14
|
+
# Build a trivial one-node graph for the single agent
|
|
15
|
+
from ..models.graph import AgentGraph, AgentNode
|
|
16
|
+
g = AgentGraph(nodes={"__single__": AgentNode(id="__single__", agent=entrypoint)}, entry="__single__")
|
|
17
|
+
return HAPRuntime(g).run(ctx)
|
|
18
|
+
|
|
19
|
+
def run_graph(self, graph: AgentGraph, *, inputs: Mapping[str, Any] | None = None) -> HAPContext:
|
|
20
|
+
ctx = HAPContext(inputs=dict(inputs or {}))
|
|
21
|
+
return HAPRuntime(graph).run(ctx)
|
haive/hap/context.py
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Context system for HAP, providing execution context for tools and resources.
|
|
3
|
+
|
|
4
|
+
Similar to MCP's context, this allows tools to:
|
|
5
|
+
- Log messages at different levels
|
|
6
|
+
- Report progress
|
|
7
|
+
- Access resources
|
|
8
|
+
- Get authentication info
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from typing import Optional, Any, Dict, List, Callable, Protocol
|
|
12
|
+
from datetime import datetime
|
|
13
|
+
import asyncio
|
|
14
|
+
from enum import Enum
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class LogLevel(str, Enum):
|
|
18
|
+
"""Log levels for context messages."""
|
|
19
|
+
DEBUG = "debug"
|
|
20
|
+
INFO = "info"
|
|
21
|
+
WARNING = "warning"
|
|
22
|
+
ERROR = "error"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ProgressState(Enum):
|
|
26
|
+
"""Progress reporting state."""
|
|
27
|
+
STARTED = "started"
|
|
28
|
+
IN_PROGRESS = "in_progress"
|
|
29
|
+
COMPLETED = "completed"
|
|
30
|
+
FAILED = "failed"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class IResourceProvider(Protocol):
|
|
34
|
+
"""Interface for resource providers."""
|
|
35
|
+
|
|
36
|
+
async def read_resource(self, uri: str) -> Any:
|
|
37
|
+
"""Read a resource by URI."""
|
|
38
|
+
...
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class IAuthProvider(Protocol):
|
|
42
|
+
"""Interface for authentication providers."""
|
|
43
|
+
|
|
44
|
+
def get_current_user(self) -> Optional[str]:
|
|
45
|
+
"""Get the current authenticated user."""
|
|
46
|
+
...
|
|
47
|
+
|
|
48
|
+
def get_user_scopes(self) -> List[str]:
|
|
49
|
+
"""Get the current user's scopes."""
|
|
50
|
+
...
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class HAPContext:
|
|
54
|
+
"""
|
|
55
|
+
Execution context for HAP tools and resources.
|
|
56
|
+
|
|
57
|
+
Provides utilities for logging, progress reporting, resource access,
|
|
58
|
+
and authentication info during execution.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
def __init__(
|
|
62
|
+
self,
|
|
63
|
+
request_id: str | int,
|
|
64
|
+
resource_provider: Optional[IResourceProvider] = None,
|
|
65
|
+
auth_provider: Optional[IAuthProvider] = None,
|
|
66
|
+
log_handler: Optional[Callable[[str, LogLevel, dict], None]] = None,
|
|
67
|
+
progress_handler: Optional[Callable[[int, int, str, ProgressState], None]] = None,
|
|
68
|
+
):
|
|
69
|
+
self.request_id = request_id
|
|
70
|
+
self._resource_provider = resource_provider
|
|
71
|
+
self._auth_provider = auth_provider
|
|
72
|
+
self._log_handler = log_handler
|
|
73
|
+
self._progress_handler = progress_handler
|
|
74
|
+
|
|
75
|
+
# Track progress for nested operations
|
|
76
|
+
self._progress_stack: List[tuple[int, str]] = []
|
|
77
|
+
|
|
78
|
+
# Store context data
|
|
79
|
+
self._data: Dict[str, Any] = {}
|
|
80
|
+
|
|
81
|
+
# Logging Methods
|
|
82
|
+
|
|
83
|
+
async def debug(self, message: str, **kwargs):
|
|
84
|
+
"""Log debug message."""
|
|
85
|
+
await self._log(LogLevel.DEBUG, message, kwargs)
|
|
86
|
+
|
|
87
|
+
async def info(self, message: str, **kwargs):
|
|
88
|
+
"""Log info message."""
|
|
89
|
+
await self._log(LogLevel.INFO, message, kwargs)
|
|
90
|
+
|
|
91
|
+
async def warning(self, message: str, **kwargs):
|
|
92
|
+
"""Log warning message."""
|
|
93
|
+
await self._log(LogLevel.WARNING, message, kwargs)
|
|
94
|
+
|
|
95
|
+
async def error(self, message: str, **kwargs):
|
|
96
|
+
"""Log error message."""
|
|
97
|
+
await self._log(LogLevel.ERROR, message, kwargs)
|
|
98
|
+
|
|
99
|
+
async def _log(self, level: LogLevel, message: str, data: dict):
|
|
100
|
+
"""Internal logging method."""
|
|
101
|
+
if self._log_handler:
|
|
102
|
+
log_entry = {
|
|
103
|
+
"timestamp": datetime.now().isoformat(),
|
|
104
|
+
"level": level.value,
|
|
105
|
+
"message": message,
|
|
106
|
+
"request_id": self.request_id,
|
|
107
|
+
**data
|
|
108
|
+
}
|
|
109
|
+
# Run in background to not block
|
|
110
|
+
asyncio.create_task(
|
|
111
|
+
asyncio.to_thread(self._log_handler, message, level, log_entry)
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
# Progress Reporting
|
|
115
|
+
|
|
116
|
+
async def report_progress(
|
|
117
|
+
self,
|
|
118
|
+
current: int,
|
|
119
|
+
total: int,
|
|
120
|
+
message: str = "",
|
|
121
|
+
state: Optional[ProgressState] = None
|
|
122
|
+
):
|
|
123
|
+
"""
|
|
124
|
+
Report progress for the current operation.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
current: Current progress value
|
|
128
|
+
total: Total expected value
|
|
129
|
+
message: Optional progress message
|
|
130
|
+
state: Optional progress state
|
|
131
|
+
"""
|
|
132
|
+
if not state:
|
|
133
|
+
if current == 0:
|
|
134
|
+
state = ProgressState.STARTED
|
|
135
|
+
elif current >= total:
|
|
136
|
+
state = ProgressState.COMPLETED
|
|
137
|
+
else:
|
|
138
|
+
state = ProgressState.IN_PROGRESS
|
|
139
|
+
|
|
140
|
+
if self._progress_handler:
|
|
141
|
+
asyncio.create_task(
|
|
142
|
+
asyncio.to_thread(
|
|
143
|
+
self._progress_handler,
|
|
144
|
+
current,
|
|
145
|
+
total,
|
|
146
|
+
message,
|
|
147
|
+
state
|
|
148
|
+
)
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
def start_nested_progress(self, total: int, description: str = ""):
|
|
152
|
+
"""Start a nested progress operation."""
|
|
153
|
+
self._progress_stack.append((total, description))
|
|
154
|
+
|
|
155
|
+
async def update_nested_progress(self, current: int, message: str = ""):
|
|
156
|
+
"""Update the current nested progress."""
|
|
157
|
+
if self._progress_stack:
|
|
158
|
+
total, description = self._progress_stack[-1]
|
|
159
|
+
full_message = f"{description}: {message}" if description else message
|
|
160
|
+
await self.report_progress(current, total, full_message)
|
|
161
|
+
|
|
162
|
+
def end_nested_progress(self):
|
|
163
|
+
"""End the current nested progress operation."""
|
|
164
|
+
if self._progress_stack:
|
|
165
|
+
total, description = self._progress_stack.pop()
|
|
166
|
+
asyncio.create_task(
|
|
167
|
+
self.report_progress(total, total, f"{description} completed")
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
# Resource Access
|
|
171
|
+
|
|
172
|
+
async def read_resource(self, uri: str) -> Any:
|
|
173
|
+
"""
|
|
174
|
+
Read a resource by URI.
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
uri: Resource URI (e.g., "agent://my-agent/state")
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
Resource content
|
|
181
|
+
|
|
182
|
+
Raises:
|
|
183
|
+
ResourceNotFoundError: If resource doesn't exist
|
|
184
|
+
PermissionError: If access is denied
|
|
185
|
+
"""
|
|
186
|
+
if not self._resource_provider:
|
|
187
|
+
raise RuntimeError("No resource provider configured")
|
|
188
|
+
|
|
189
|
+
await self.debug(f"Reading resource: {uri}")
|
|
190
|
+
try:
|
|
191
|
+
result = await self._resource_provider.read_resource(uri)
|
|
192
|
+
await self.debug(f"Successfully read resource: {uri}")
|
|
193
|
+
return result
|
|
194
|
+
except Exception as e:
|
|
195
|
+
await self.error(f"Failed to read resource {uri}: {str(e)}")
|
|
196
|
+
raise
|
|
197
|
+
|
|
198
|
+
# Authentication
|
|
199
|
+
|
|
200
|
+
def get_auth_user(self) -> Optional[str]:
|
|
201
|
+
"""Get the current authenticated user."""
|
|
202
|
+
if not self._auth_provider:
|
|
203
|
+
return None
|
|
204
|
+
return self._auth_provider.get_current_user()
|
|
205
|
+
|
|
206
|
+
def get_auth_scopes(self) -> List[str]:
|
|
207
|
+
"""Get the current user's scopes."""
|
|
208
|
+
if not self._auth_provider:
|
|
209
|
+
return []
|
|
210
|
+
return self._auth_provider.get_user_scopes()
|
|
211
|
+
|
|
212
|
+
def has_scope(self, scope: str) -> bool:
|
|
213
|
+
"""Check if the current user has a specific scope."""
|
|
214
|
+
return scope in self.get_auth_scopes()
|
|
215
|
+
|
|
216
|
+
# Context Data
|
|
217
|
+
|
|
218
|
+
def set_data(self, key: str, value: Any):
|
|
219
|
+
"""Store data in the context."""
|
|
220
|
+
self._data[key] = value
|
|
221
|
+
|
|
222
|
+
def get_data(self, key: str, default: Any = None) -> Any:
|
|
223
|
+
"""Retrieve data from the context."""
|
|
224
|
+
return self._data.get(key, default)
|
|
225
|
+
|
|
226
|
+
def update_data(self, data: dict):
|
|
227
|
+
"""Update context data with a dictionary."""
|
|
228
|
+
self._data.update(data)
|
|
229
|
+
|
|
230
|
+
# Utilities
|
|
231
|
+
|
|
232
|
+
async def with_progress(
|
|
233
|
+
self,
|
|
234
|
+
operation: Callable,
|
|
235
|
+
total: int,
|
|
236
|
+
description: str = "Processing"
|
|
237
|
+
):
|
|
238
|
+
"""
|
|
239
|
+
Execute an operation with automatic progress tracking.
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
operation: Async callable that accepts (current, context) args
|
|
243
|
+
total: Total number of items
|
|
244
|
+
description: Progress description
|
|
245
|
+
"""
|
|
246
|
+
self.start_nested_progress(total, description)
|
|
247
|
+
try:
|
|
248
|
+
for i in range(total):
|
|
249
|
+
await self.update_nested_progress(i, f"Item {i+1}/{total}")
|
|
250
|
+
await operation(i, self)
|
|
251
|
+
finally:
|
|
252
|
+
self.end_nested_progress()
|
|
253
|
+
|
|
254
|
+
def create_child_context(self, **overrides) -> "HAPContext":
|
|
255
|
+
"""Create a child context with optional overrides."""
|
|
256
|
+
return HAPContext(
|
|
257
|
+
request_id=overrides.get("request_id", f"{self.request_id}.child"),
|
|
258
|
+
resource_provider=overrides.get("resource_provider", self._resource_provider),
|
|
259
|
+
auth_provider=overrides.get("auth_provider", self._auth_provider),
|
|
260
|
+
log_handler=overrides.get("log_handler", self._log_handler),
|
|
261
|
+
progress_handler=overrides.get("progress_handler", self._progress_handler),
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
class SimpleResourceProvider:
|
|
266
|
+
"""Simple in-memory resource provider for testing."""
|
|
267
|
+
|
|
268
|
+
def __init__(self):
|
|
269
|
+
self.resources: Dict[str, Any] = {}
|
|
270
|
+
|
|
271
|
+
def add_resource(self, uri: str, content: Any):
|
|
272
|
+
"""Add a resource."""
|
|
273
|
+
self.resources[uri] = content
|
|
274
|
+
|
|
275
|
+
async def read_resource(self, uri: str) -> Any:
|
|
276
|
+
"""Read a resource."""
|
|
277
|
+
if uri not in self.resources:
|
|
278
|
+
raise KeyError(f"Resource not found: {uri}")
|
|
279
|
+
return self.resources[uri]
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
class SimpleAuthProvider:
|
|
283
|
+
"""Simple auth provider for testing."""
|
|
284
|
+
|
|
285
|
+
def __init__(self, user: Optional[str] = None, scopes: Optional[List[str]] = None):
|
|
286
|
+
self.user = user
|
|
287
|
+
self.scopes = scopes or []
|
|
288
|
+
|
|
289
|
+
def get_current_user(self) -> Optional[str]:
|
|
290
|
+
"""Get current user."""
|
|
291
|
+
return self.user
|
|
292
|
+
|
|
293
|
+
def get_user_scopes(self) -> List[str]:
|
|
294
|
+
"""Get user scopes."""
|
|
295
|
+
return self.scopes
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# AGP Models Module
|
|
2
|
+
|
|
3
|
+
This module contains the core data models for the Haive Agent Protocol (AGP).
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The models module provides the fundamental building blocks for AGP:
|
|
8
|
+
- **HAPContext**: State management with execution tracking
|
|
9
|
+
- **HAPGraph**: Graph structure for agent workflows
|
|
10
|
+
- **HAPNode**: Individual nodes in the graph
|
|
11
|
+
|
|
12
|
+
## Components
|
|
13
|
+
|
|
14
|
+
### HAPContext
|
|
15
|
+
|
|
16
|
+
The `HAPContext` class extends Haive's `StateSchema` to provide:
|
|
17
|
+
- Execution path tracking
|
|
18
|
+
- Agent metadata storage
|
|
19
|
+
- Graph context management
|
|
20
|
+
- Backward compatibility with AGP v1 properties
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
from haive.hap.models.context import HAPContext
|
|
24
|
+
|
|
25
|
+
context = HAPContext()
|
|
26
|
+
context.execution_path.append("node1")
|
|
27
|
+
context.agent_metadata["node1"] = {"status": "complete"}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### HAPGraph
|
|
31
|
+
|
|
32
|
+
The `HAPGraph` class manages the workflow structure:
|
|
33
|
+
- Node management (add, remove, connect)
|
|
34
|
+
- Topological ordering for execution
|
|
35
|
+
- Entry point configuration
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from haive.hap.models.graph import HAPGraph
|
|
39
|
+
|
|
40
|
+
graph = HAPGraph()
|
|
41
|
+
graph.add_agent_node("step1", agent1, next_nodes=["step2"])
|
|
42
|
+
graph.add_agent_node("step2", agent2)
|
|
43
|
+
graph.entry_node = "step1"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### HAPNode
|
|
47
|
+
|
|
48
|
+
Individual nodes in the graph that can contain:
|
|
49
|
+
- Agent instances
|
|
50
|
+
- Agent entrypoints (module:class format)
|
|
51
|
+
- Connections to other nodes
|
|
52
|
+
|
|
53
|
+
## Backward Compatibility
|
|
54
|
+
|
|
55
|
+
The module maintains backward compatibility with AGP v1:
|
|
56
|
+
- `HAPContext` supports `inputs`, `outputs`, `state`, `meta` properties
|
|
57
|
+
- `AgentGraph` and `AgentNode` are aliases for the AGP classes
|
|
58
|
+
|
|
59
|
+
## Usage Example
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from haive.hap.models import HAPGraph, HAPContext
|
|
63
|
+
from haive.agents.simple.agent import SimpleAgent
|
|
64
|
+
|
|
65
|
+
# Create context
|
|
66
|
+
context = HAPContext()
|
|
67
|
+
|
|
68
|
+
# Build graph
|
|
69
|
+
graph = HAPGraph()
|
|
70
|
+
agent = SimpleAgent(name="worker", engine=config)
|
|
71
|
+
graph.add_agent_node("worker_node", agent)
|
|
72
|
+
|
|
73
|
+
# Track execution
|
|
74
|
+
context.execution_path.append("worker_node")
|
|
75
|
+
context.agent_metadata["worker_node"] = {
|
|
76
|
+
"start_time": time.time(),
|
|
77
|
+
"status": "running"
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Design Principles
|
|
82
|
+
|
|
83
|
+
1. **StateSchema Integration**: HAPContext inherits from StateSchema for proper Haive integration
|
|
84
|
+
2. **Simplicity**: Uses BaseModel instead of complex inheritance chains
|
|
85
|
+
3. **Flexibility**: Supports both agent instances and entrypoint strings
|
|
86
|
+
4. **Compatibility**: Maintains all AGP v1 interfaces
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""HAP models for agent graphs and contexts."""
|
|
2
|
+
|
|
3
|
+
from .context import HAPContext
|
|
4
|
+
from .graph import HAPGraph, HAPNode, AgentGraph, AgentNode
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"HAPContext",
|
|
8
|
+
"HAPGraph",
|
|
9
|
+
"HAPNode",
|
|
10
|
+
"AgentGraph", # Backward compatibility
|
|
11
|
+
"AgentNode", # Backward compatibility
|
|
12
|
+
]
|