Files
daily-opencode-workspace/.opencode/skills/uni-agent/adapters/aitp.py

218 lines
7.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
AITP (Agent Interaction & Transaction Protocol) 适配器
NEAR 基金会提出的 Agent 交互与交易协议
参考: https://aitp.dev
"""
import json
import uuid
from pathlib import Path
from typing import Any, Dict, List, Optional
import aiohttp
from .base import ProtocolAdapter, Connection, AgentInfo
class AITPAdapter(ProtocolAdapter):
"""AITP 协议适配器"""
protocol_name = "aitp"
def __init__(self, config_dir: Optional[Path] = None):
self.config_dir = config_dir or Path(__file__).parent.parent / "config"
self._threads: Dict[str, dict] = {}
async def connect(self, agent_config: dict) -> Connection:
"""建立连接 - 创建 Thread"""
endpoint = agent_config.get("endpoint")
if not endpoint:
raise ValueError("AITP Agent 配置必须包含 endpoint")
thread_id = str(uuid.uuid4())
self._threads[thread_id] = {
"id": thread_id,
"messages": [],
"status": "open",
}
return Connection(
agent_id=agent_config.get("id", ""),
protocol=self.protocol_name,
endpoint=endpoint,
session=thread_id,
metadata={
"thread_id": thread_id,
"wallet": agent_config.get("wallet", {}),
}
)
async def call(
self,
connection: Connection,
method: str,
params: dict,
timeout: float = 30.0
) -> dict:
"""调用 AITP Agent"""
endpoint = connection.endpoint
thread_id = connection.session
wallet_config = connection.metadata.get("wallet", {})
headers = {
"Content-Type": "application/json",
}
if method == "message":
payload = {
"thread_id": thread_id,
"message": {
"role": "user",
"content": params.get("content", ""),
"parts": params.get("parts", []),
}
}
elif method == "payment":
payload = {
"thread_id": thread_id,
"capability": "aitp-01",
"payment_request": {
"amount": params.get("amount"),
"currency": params.get("currency", "NEAR"),
"recipient": params.get("recipient"),
"memo": params.get("memo", ""),
}
}
if wallet_config.get("type") == "near":
payload["wallet"] = {
"type": "near",
"account_id": wallet_config.get("account_id"),
}
elif method == "decision":
payload = {
"thread_id": thread_id,
"capability": "aitp-02",
"decision_request": {
"question": params.get("question"),
"options": params.get("options", []),
"allow_custom": params.get("allow_custom", False),
}
}
elif method == "data_request":
payload = {
"thread_id": thread_id,
"capability": "aitp-03",
"data_request": {
"schema": params.get("schema", {}),
"description": params.get("description", ""),
}
}
else:
payload = {
"thread_id": thread_id,
"method": method,
"params": params,
}
async with aiohttp.ClientSession() as session:
async with session.post(
f"{endpoint}/threads/{thread_id}/messages",
json=payload,
headers=headers,
timeout=aiohttp.ClientTimeout(total=timeout)
) as resp:
if resp.status == 200:
result = await resp.json()
if thread_id in self._threads:
self._threads[thread_id]["messages"].append(payload)
self._threads[thread_id]["messages"].append(result)
return {
"success": True,
"result": result,
"thread_id": thread_id,
}
else:
error_text = await resp.text()
return {
"success": False,
"error": f"HTTP {resp.status}: {error_text}",
}
async def discover(self, capability: str = "") -> List[AgentInfo]:
"""发现 Agent"""
agents_file = self.config_dir / "agents.yaml"
if not agents_file.exists():
return []
import yaml
with open(agents_file) as f:
config = yaml.safe_load(f)
agents = []
for agent in config.get("agents", []):
if agent.get("protocol") != "aitp":
continue
if capability and capability.lower() not in agent.get("id", "").lower():
continue
agents.append(AgentInfo(
id=f"{agent['id']}@aitp",
protocol="aitp",
name=agent.get("name", agent["id"]),
endpoint=agent.get("endpoint", ""),
metadata=agent
))
return agents
async def close(self, connection: Connection):
"""关闭连接 - 关闭 Thread"""
thread_id = connection.session
if thread_id in self._threads:
self._threads[thread_id]["status"] = "closed"
async def get_methods(self, connection: Connection) -> List[dict]:
"""获取支持的方法AITP 能力)"""
return [
{
"name": "message",
"description": "发送对话消息",
"inputSchema": {"content": "string"},
},
{
"name": "payment",
"description": "AITP-01: 发起支付请求",
"inputSchema": {
"amount": "number",
"currency": "string",
"recipient": "string",
},
},
{
"name": "decision",
"description": "AITP-02: 请求用户决策",
"inputSchema": {
"question": "string",
"options": "array",
},
},
{
"name": "data_request",
"description": "AITP-03: 请求结构化数据",
"inputSchema": {
"schema": "object",
"description": "string",
},
},
]
def validate_config(self, agent_config: dict) -> bool:
"""验证配置"""
return "endpoint" in agent_config