Skip to content

Commit eefb7e5

Browse files
feat: add ANP tool configuration and remove unused HotelBook import
1 parent 82acf88 commit eefb7e5

File tree

5 files changed

+126
-500
lines changed

5 files changed

+126
-500
lines changed

app/agent/manus.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
from app.tool.python_execute import PythonExecute
1111
from app.tool.anp_tool import ANPTool
1212
from app.tool.web_search import WebSearch
13-
from app.tool.hotel_book import HotelBook
1413

1514

1615
class Manus(ToolCallAgent):
@@ -40,7 +39,6 @@ class Manus(ToolCallAgent):
4039
WebSearch(),
4140
BrowserUseTool(),
4241
FileSaver(),
43-
# HotelBook(),
4442
ANPTool(),
4543
Terminate(),
4644
)

app/config.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,15 @@ class BrowserSettings(BaseModel):
6161
)
6262

6363

64+
class ANPSettings(BaseModel):
65+
did_document_path: Optional[str] = Field(
66+
None, description="Path to DID document file"
67+
)
68+
private_key_path: Optional[str] = Field(
69+
None, description="Path to private key file"
70+
)
71+
72+
6473
class AppConfig(BaseModel):
6574
llm: Dict[str, LLMSettings]
6675
browser_config: Optional[BrowserSettings] = Field(
@@ -69,6 +78,9 @@ class AppConfig(BaseModel):
6978
search_config: Optional[SearchSettings] = Field(
7079
None, description="Search configuration"
7180
)
81+
anp_config: Optional[ANPSettings] = Field(
82+
None, description="ANP tool configuration"
83+
)
7284

7385
class Config:
7486
arbitrary_types_allowed = True
@@ -166,6 +178,18 @@ def _load_initial_config(self):
166178
if search_config:
167179
search_settings = SearchSettings(**search_config)
168180

181+
# handle ANP config
182+
anp_config = raw_config.get("anp", {})
183+
anp_settings = None
184+
if anp_config:
185+
anp_settings = ANPSettings(
186+
**{
187+
k: v
188+
for k, v in anp_config.items()
189+
if k in ANPSettings.__annotations__ and v is not None
190+
}
191+
)
192+
169193
config_dict = {
170194
"llm": {
171195
"default": default_settings,
@@ -176,6 +200,7 @@ def _load_initial_config(self):
176200
},
177201
"browser_config": browser_settings,
178202
"search_config": search_settings,
203+
"anp_config": anp_settings,
179204
}
180205

181206
self._config = AppConfig(**config_dict)
@@ -192,5 +217,9 @@ def browser_config(self) -> Optional[BrowserSettings]:
192217
def search_config(self) -> Optional[SearchSettings]:
193218
return self._config.search_config
194219

220+
@property
221+
def anp_config(self) -> Optional[ANPSettings]:
222+
return self._config.anp_config
223+
195224

196225
config = Config()

app/tool/anp_tool.py

Lines changed: 80 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
from app.logger import logger
99
from app.tool.base import BaseTool
1010
from agent_connect.authentication import DIDWbaAuthHeader
11+
from app.config import config, PROJECT_ROOT
12+
1113

1214
class ANPTool(BaseTool):
1315
name: str = "anp_tool"
@@ -47,47 +49,55 @@ class ANPTool(BaseTool):
4749
},
4850
"required": ["url"],
4951
}
50-
52+
5153
# Declare auth_client field
5254
auth_client: Optional[DIDWbaAuthHeader] = None
5355

5456
def __init__(self, **data):
5557
super().__init__(**data)
5658

57-
# Get current script directory
58-
current_dir = Path(__file__).parent
59-
# Get project root directory
60-
base_dir = current_dir.parent.parent
61-
62-
# Initialize DID authentication client
63-
did_path = str(base_dir / "config/did_test_public_doc/did.json")
64-
key_path = str(base_dir / "config/did_test_public_doc/key-1_private.pem")
65-
66-
logger.info(f"ANPTool initialized - DID path: {did_path}, private key path: {key_path}")
67-
59+
# Get default paths relative to project root
60+
default_did_path = str(PROJECT_ROOT / "config/did_test_public_doc/did.json")
61+
default_key_path = str(
62+
PROJECT_ROOT / "config/did_test_public_doc/key-1_private.pem"
63+
)
64+
65+
# Use paths from configuration if available, otherwise use defaults
66+
did_path = default_did_path
67+
key_path = default_key_path
68+
69+
if config.anp_config:
70+
if config.anp_config.did_document_path:
71+
did_path = config.anp_config.did_document_path
72+
if config.anp_config.private_key_path:
73+
key_path = config.anp_config.private_key_path
74+
75+
logger.info(
76+
f"ANPTool initialized - DID path: {did_path}, private key path: {key_path}"
77+
)
78+
6879
self.auth_client = DIDWbaAuthHeader(
69-
did_document_path=did_path,
70-
private_key_path=key_path
80+
did_document_path=did_path, private_key_path=key_path
7181
)
7282

7383
async def execute(
74-
self,
75-
url: str,
76-
method: str = "GET",
77-
headers: Dict[str, str] = None,
78-
params: Dict[str, Any] = None,
79-
body: Dict[str, Any] = None
84+
self,
85+
url: str,
86+
method: str = "GET",
87+
headers: Dict[str, str] = None,
88+
params: Dict[str, Any] = None,
89+
body: Dict[str, Any] = None,
8090
) -> Dict[str, Any]:
8191
"""
8292
Execute HTTP request to interact with other agents
83-
93+
8494
Args:
8595
url (str): URL of the agent description file or API endpoint
8696
method (str, optional): HTTP method, default is "GET"
8797
headers (Dict[str, str], optional): HTTP request headers
8898
params (Dict[str, Any], optional): URL query parameters
8999
body (Dict[str, Any], optional): Request body for POST/PUT requests
90-
100+
91101
Returns:
92102
Dict[str, Any]: Response content
93103
"""
@@ -96,21 +106,20 @@ async def execute(
96106
headers = {}
97107
if params is None:
98108
params = {}
99-
109+
100110
logger.info(f"ANP request: {method} {url}")
101-
111+
102112
# Add basic request headers
103113
if "Content-Type" not in headers and method in ["POST", "PUT", "PATCH"]:
104114
headers["Content-Type"] = "application/json"
105-
115+
106116
# Add DID authentication
107117
if self.auth_client:
108118
try:
109119
auth_headers = self.auth_client.get_auth_header(url)
110120
headers.update(auth_headers)
111121
except Exception as e:
112122
logger.error(f"Failed to get authentication header: {str(e)}")
113-
114123

115124
async with aiohttp.ClientSession() as session:
116125
# Prepare request parameters
@@ -119,40 +128,46 @@ async def execute(
119128
"headers": headers,
120129
"params": params,
121130
}
122-
131+
123132
# If there is a request body and the method supports it, add the request body
124133
if body is not None and method in ["POST", "PUT", "PATCH"]:
125134
request_kwargs["json"] = body
126-
135+
127136
# Execute request
128137
http_method = getattr(session, method.lower())
129-
138+
130139
try:
131140
async with http_method(**request_kwargs) as response:
132141
logger.info(f"ANP response: status code {response.status}")
133-
142+
134143
# Check response status
135-
if response.status == 401 and "Authorization" in headers and self.auth_client:
136-
logger.warning("Authentication failed (401), trying to get authentication again")
144+
if (
145+
response.status == 401
146+
and "Authorization" in headers
147+
and self.auth_client
148+
):
149+
logger.warning(
150+
"Authentication failed (401), trying to get authentication again"
151+
)
137152
# If authentication fails and a token was used, clear the token and retry
138153
self.auth_client.clear_token(url)
139154
# Get authentication header again
140-
headers.update(self.auth_client.get_auth_header(url, force_new=True))
155+
headers.update(
156+
self.auth_client.get_auth_header(url, force_new=True)
157+
)
141158
# Execute request again
142159
request_kwargs["headers"] = headers
143160
async with http_method(**request_kwargs) as retry_response:
144-
logger.info(f"ANP retry response: status code {retry_response.status}")
161+
logger.info(
162+
f"ANP retry response: status code {retry_response.status}"
163+
)
145164
return await self._process_response(retry_response, url)
146165

147166
return await self._process_response(response, url)
148167
except aiohttp.ClientError as e:
149168
logger.error(f"HTTP request failed: {str(e)}")
150-
return {
151-
"error": f"HTTP request failed: {str(e)}",
152-
"status_code": 500
153-
}
169+
return {"error": f"HTTP request failed: {str(e)}", "status_code": 500}
154170

155-
156171
async def _process_response(self, response, url):
157172
"""Process HTTP response"""
158173
# If authentication is successful, update the token
@@ -161,42 +176,55 @@ async def _process_response(self, response, url):
161176
self.auth_client.update_token(url, dict(response.headers))
162177
except Exception as e:
163178
logger.error(f"Failed to update token: {str(e)}")
164-
179+
165180
# Get response content type
166-
content_type = response.headers.get('Content-Type', '').lower()
167-
181+
content_type = response.headers.get("Content-Type", "").lower()
182+
168183
# Get response text
169184
text = await response.text()
170-
185+
171186
# Process response based on content type
172-
if 'application/json' in content_type:
187+
if "application/json" in content_type:
173188
# Process JSON response
174189
try:
175190
result = json.loads(text)
176191
logger.info("Successfully parsed JSON response")
177192
except json.JSONDecodeError:
178-
logger.warning("Content-Type declared as JSON but parsing failed, returning raw text")
193+
logger.warning(
194+
"Content-Type declared as JSON but parsing failed, returning raw text"
195+
)
179196
result = {"text": text, "format": "text", "content_type": content_type}
180-
elif 'application/yaml' in content_type or 'application/x-yaml' in content_type:
197+
elif "application/yaml" in content_type or "application/x-yaml" in content_type:
181198
# Process YAML response
182199
try:
183200
result = yaml.safe_load(text)
184201
logger.info("Successfully parsed YAML response")
185-
result = {"data": result, "format": "yaml", "content_type": content_type}
202+
result = {
203+
"data": result,
204+
"format": "yaml",
205+
"content_type": content_type,
206+
}
186207
except yaml.YAMLError:
187-
logger.warning("Content-Type declared as YAML but parsing failed, returning raw text")
208+
logger.warning(
209+
"Content-Type declared as YAML but parsing failed, returning raw text"
210+
)
188211
result = {"text": text, "format": "text", "content_type": content_type}
189212
else:
190213
# Default to text
191214
result = {"text": text, "format": "text", "content_type": content_type}
192-
215+
193216
# Add status code to result
194217
if isinstance(result, dict):
195218
result["status_code"] = response.status
196219
else:
197-
result = {"data": result, "status_code": response.status, "format": "unknown", "content_type": content_type}
198-
220+
result = {
221+
"data": result,
222+
"status_code": response.status,
223+
"format": "unknown",
224+
"content_type": content_type,
225+
}
226+
199227
# Add URL to result for tracking
200228
result["url"] = str(url)
201-
229+
202230
return result

0 commit comments

Comments
 (0)