lollmsbot 0.0.1__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.
- lollmsbot/__init__.py +1 -0
- lollmsbot/agent.py +1682 -0
- lollmsbot/channels/__init__.py +22 -0
- lollmsbot/channels/discord.py +408 -0
- lollmsbot/channels/http_api.py +449 -0
- lollmsbot/channels/telegram.py +272 -0
- lollmsbot/cli.py +217 -0
- lollmsbot/config.py +90 -0
- lollmsbot/gateway.py +606 -0
- lollmsbot/guardian.py +692 -0
- lollmsbot/heartbeat.py +826 -0
- lollmsbot/lollms_client.py +37 -0
- lollmsbot/skills.py +1483 -0
- lollmsbot/soul.py +482 -0
- lollmsbot/storage/__init__.py +245 -0
- lollmsbot/storage/sqlite_store.py +332 -0
- lollmsbot/tools/__init__.py +151 -0
- lollmsbot/tools/calendar.py +717 -0
- lollmsbot/tools/filesystem.py +663 -0
- lollmsbot/tools/http.py +498 -0
- lollmsbot/tools/shell.py +519 -0
- lollmsbot/ui/__init__.py +11 -0
- lollmsbot/ui/__main__.py +121 -0
- lollmsbot/ui/app.py +1122 -0
- lollmsbot/ui/routes.py +39 -0
- lollmsbot/wizard.py +1493 -0
- lollmsbot-0.0.1.dist-info/METADATA +25 -0
- lollmsbot-0.0.1.dist-info/RECORD +32 -0
- lollmsbot-0.0.1.dist-info/WHEEL +5 -0
- lollmsbot-0.0.1.dist-info/entry_points.txt +2 -0
- lollmsbot-0.0.1.dist-info/licenses/LICENSE +201 -0
- lollmsbot-0.0.1.dist-info/top_level.txt +1 -0
lollmsbot/tools/http.py
ADDED
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
"""
|
|
2
|
+
HTTP tool for LollmsBot.
|
|
3
|
+
|
|
4
|
+
This module provides the HttpTool class for making HTTP requests with
|
|
5
|
+
built-in timeout, retry logic, and response parsing. Supports GET, POST,
|
|
6
|
+
PUT, and DELETE operations with safe URL validation.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
import json
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
from typing import Any, Dict, List, Optional, Union
|
|
13
|
+
from urllib.parse import urlparse
|
|
14
|
+
|
|
15
|
+
import aiohttp
|
|
16
|
+
from aiohttp import ClientTimeout, ClientError, ClientResponse
|
|
17
|
+
|
|
18
|
+
from lollmsbot.agent import Tool, ToolResult, ToolError
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class RetryConfig:
|
|
23
|
+
"""Configuration for retry behavior."""
|
|
24
|
+
max_retries: int = 3
|
|
25
|
+
base_delay: float = 1.0
|
|
26
|
+
max_delay: float = 10.0
|
|
27
|
+
exponential_base: float = 2.0
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class HttpTool(Tool):
|
|
31
|
+
"""Tool for making HTTP requests with retry logic and safe URL validation.
|
|
32
|
+
|
|
33
|
+
This tool provides methods for GET, POST, PUT, and DELETE HTTP operations
|
|
34
|
+
with built-in timeout handling, automatic retries with exponential backoff,
|
|
35
|
+
and intelligent response parsing (JSON or text).
|
|
36
|
+
|
|
37
|
+
Attributes:
|
|
38
|
+
name: Unique identifier for the tool.
|
|
39
|
+
description: Human-readable description of what the tool does.
|
|
40
|
+
parameters: JSON Schema describing expected parameters.
|
|
41
|
+
default_timeout: Default request timeout in seconds.
|
|
42
|
+
retry_config: Configuration for retry behavior.
|
|
43
|
+
allowed_schemes: Set of allowed URL schemes for security.
|
|
44
|
+
max_response_size: Maximum response size in bytes.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
name: str = "http"
|
|
48
|
+
description: str = (
|
|
49
|
+
"Make HTTP requests (GET, POST, PUT, DELETE) to external APIs "
|
|
50
|
+
"and web services. Automatically parses JSON responses and "
|
|
51
|
+
"handles timeouts, retries, and errors gracefully."
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
parameters: dict[str, Any] = {
|
|
55
|
+
"type": "object",
|
|
56
|
+
"properties": {
|
|
57
|
+
"method": {
|
|
58
|
+
"type": "string",
|
|
59
|
+
"enum": ["get", "post", "put", "delete"],
|
|
60
|
+
"description": "HTTP method to use",
|
|
61
|
+
},
|
|
62
|
+
"url": {
|
|
63
|
+
"type": "string",
|
|
64
|
+
"description": "Target URL for the request",
|
|
65
|
+
},
|
|
66
|
+
"data": {
|
|
67
|
+
"type": "object",
|
|
68
|
+
"description": "Request body data (for POST, PUT)",
|
|
69
|
+
},
|
|
70
|
+
"headers": {
|
|
71
|
+
"type": "object",
|
|
72
|
+
"description": "Additional HTTP headers",
|
|
73
|
+
},
|
|
74
|
+
"params": {
|
|
75
|
+
"type": "object",
|
|
76
|
+
"description": "URL query parameters",
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
"required": ["method", "url"],
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
def __init__(
|
|
83
|
+
self,
|
|
84
|
+
default_timeout: float = 30.0,
|
|
85
|
+
retry_config: Optional[RetryConfig] = None,
|
|
86
|
+
allowed_schemes: Optional[set[str]] = None,
|
|
87
|
+
max_response_size: int = 10 * 1024 * 1024, # 10 MB
|
|
88
|
+
) -> None:
|
|
89
|
+
"""Initialize the HttpTool.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
default_timeout: Default request timeout in seconds.
|
|
93
|
+
retry_config: Retry configuration. Uses defaults if None.
|
|
94
|
+
allowed_schemes: Set of allowed URL schemes. Defaults to http, https.
|
|
95
|
+
max_response_size: Maximum response size in bytes.
|
|
96
|
+
"""
|
|
97
|
+
self.default_timeout: float = default_timeout
|
|
98
|
+
self.retry_config: RetryConfig = retry_config or RetryConfig()
|
|
99
|
+
self.allowed_schemes: set[str] = allowed_schemes or {"http", "https"}
|
|
100
|
+
self.max_response_size: int = max_response_size
|
|
101
|
+
|
|
102
|
+
# Create session with connection pooling
|
|
103
|
+
self._session: Optional[aiohttp.ClientSession] = None
|
|
104
|
+
self._session_lock: asyncio.Lock = asyncio.Lock()
|
|
105
|
+
|
|
106
|
+
async def _get_session(self) -> aiohttp.ClientSession:
|
|
107
|
+
"""Get or create aiohttp session with connection pooling."""
|
|
108
|
+
async with self._session_lock:
|
|
109
|
+
if self._session is None or self._session.closed:
|
|
110
|
+
timeout = ClientTimeout(total=self.default_timeout)
|
|
111
|
+
connector = aiohttp.TCPConnector(
|
|
112
|
+
limit=10,
|
|
113
|
+
limit_per_host=5,
|
|
114
|
+
enable_cleanup_closed=True,
|
|
115
|
+
force_close=True,
|
|
116
|
+
)
|
|
117
|
+
self._session = aiohttp.ClientSession(
|
|
118
|
+
timeout=timeout,
|
|
119
|
+
connector=connector,
|
|
120
|
+
headers={
|
|
121
|
+
"User-Agent": "LollmsBot-HttpTool/0.1.0",
|
|
122
|
+
"Accept": "application/json, text/plain, */*",
|
|
123
|
+
},
|
|
124
|
+
)
|
|
125
|
+
return self._session
|
|
126
|
+
|
|
127
|
+
def _validate_url(self, url: str) -> tuple[bool, Optional[str]]:
|
|
128
|
+
"""Validate URL scheme and format.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
url: URL to validate.
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
Tuple of (is_valid, error_message).
|
|
135
|
+
"""
|
|
136
|
+
try:
|
|
137
|
+
parsed = urlparse(url)
|
|
138
|
+
|
|
139
|
+
# Check scheme
|
|
140
|
+
if parsed.scheme not in self.allowed_schemes:
|
|
141
|
+
allowed = ", ".join(self.allowed_schemes)
|
|
142
|
+
return False, f"URL scheme '{parsed.scheme}' not allowed. Allowed: {allowed}"
|
|
143
|
+
|
|
144
|
+
# Check netloc (host)
|
|
145
|
+
if not parsed.netloc:
|
|
146
|
+
return False, "URL must include a host"
|
|
147
|
+
|
|
148
|
+
# Block localhost and private IPs by default for security
|
|
149
|
+
hostname = parsed.hostname
|
|
150
|
+
if hostname:
|
|
151
|
+
blocked_hosts = {"localhost", "127.0.0.1", "localhost", "::1"}
|
|
152
|
+
if hostname in blocked_hosts:
|
|
153
|
+
return False, f"Access to '{hostname}' is not allowed"
|
|
154
|
+
|
|
155
|
+
# Check for private IP ranges
|
|
156
|
+
if hostname.startswith("10.") or hostname.startswith("192.168.") or hostname.startswith("172."):
|
|
157
|
+
return False, f"Access to private IP '{hostname}' is not allowed"
|
|
158
|
+
|
|
159
|
+
return True, None
|
|
160
|
+
|
|
161
|
+
except ValueError as exc:
|
|
162
|
+
return False, f"Invalid URL format: {str(exc)}"
|
|
163
|
+
|
|
164
|
+
async def _execute_with_retry(
|
|
165
|
+
self,
|
|
166
|
+
method: str,
|
|
167
|
+
url: str,
|
|
168
|
+
**kwargs: Any,
|
|
169
|
+
) -> tuple[bool, Any, Optional[str], Dict[str, Any]]:
|
|
170
|
+
"""Execute HTTP request with retry logic.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
method: HTTP method (get, post, put, delete).
|
|
174
|
+
url: Target URL.
|
|
175
|
+
**kwargs: Additional arguments for aiohttp request.
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
Tuple of (success, output_data, error_message, metadata).
|
|
179
|
+
"""
|
|
180
|
+
session = await self._get_session()
|
|
181
|
+
|
|
182
|
+
last_error: Optional[str] = None
|
|
183
|
+
metadata: Dict[str, Any] = {
|
|
184
|
+
"attempts": 0,
|
|
185
|
+
"url": url,
|
|
186
|
+
"method": method.upper(),
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
for attempt in range(self.retry_config.max_retries):
|
|
190
|
+
metadata["attempts"] = attempt + 1
|
|
191
|
+
|
|
192
|
+
try:
|
|
193
|
+
async with session.request(method, url, **kwargs) as response:
|
|
194
|
+
return await self._handle_response(response, metadata)
|
|
195
|
+
|
|
196
|
+
except asyncio.TimeoutError:
|
|
197
|
+
last_error = f"Request timeout after {self.default_timeout}s"
|
|
198
|
+
|
|
199
|
+
except ClientError as exc:
|
|
200
|
+
last_error = f"HTTP client error: {str(exc)}"
|
|
201
|
+
|
|
202
|
+
except Exception as exc:
|
|
203
|
+
last_error = f"Unexpected error: {str(exc)}"
|
|
204
|
+
|
|
205
|
+
# Calculate delay with exponential backoff
|
|
206
|
+
if attempt < self.retry_config.max_retries - 1:
|
|
207
|
+
delay = min(
|
|
208
|
+
self.retry_config.base_delay * (self.retry_config.exponential_base ** attempt),
|
|
209
|
+
self.retry_config.max_delay,
|
|
210
|
+
)
|
|
211
|
+
await asyncio.sleep(delay)
|
|
212
|
+
|
|
213
|
+
# All retries exhausted
|
|
214
|
+
return False, None, last_error, metadata
|
|
215
|
+
|
|
216
|
+
async def _handle_response(
|
|
217
|
+
self,
|
|
218
|
+
response: ClientResponse,
|
|
219
|
+
metadata: Dict[str, Any],
|
|
220
|
+
) -> tuple[bool, Any, Optional[str], Dict[str, Any]]:
|
|
221
|
+
"""Process HTTP response and parse content.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
response: aiohttp ClientResponse.
|
|
225
|
+
metadata: Metadata dict to augment.
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
Tuple of (success, output_data, error_message, metadata).
|
|
229
|
+
"""
|
|
230
|
+
metadata["status_code"] = response.status
|
|
231
|
+
metadata["content_type"] = response.content_type or "unknown"
|
|
232
|
+
|
|
233
|
+
# Check for HTTP error status
|
|
234
|
+
if response.status >= 400:
|
|
235
|
+
try:
|
|
236
|
+
error_text = await response.text()
|
|
237
|
+
except Exception:
|
|
238
|
+
error_text = "Could not read error response"
|
|
239
|
+
|
|
240
|
+
return False, None, f"HTTP {response.status}: {error_text[:500]}", metadata
|
|
241
|
+
|
|
242
|
+
# Check response size
|
|
243
|
+
content_length = response.content_length
|
|
244
|
+
if content_length and content_length > self.max_response_size:
|
|
245
|
+
return False, None, f"Response too large: {content_length} bytes", metadata
|
|
246
|
+
|
|
247
|
+
# Read response content with size limit
|
|
248
|
+
try:
|
|
249
|
+
text = await response.text()
|
|
250
|
+
|
|
251
|
+
if len(text.encode("utf-8")) > self.max_response_size:
|
|
252
|
+
return False, None, f"Response exceeds max size of {self.max_response_size} bytes", metadata
|
|
253
|
+
|
|
254
|
+
except Exception as exc:
|
|
255
|
+
return False, None, f"Failed to read response: {str(exc)}", metadata
|
|
256
|
+
|
|
257
|
+
# Try to parse as JSON
|
|
258
|
+
try:
|
|
259
|
+
parsed = json.loads(text)
|
|
260
|
+
metadata["parsed_as"] = "json"
|
|
261
|
+
return True, parsed, None, metadata
|
|
262
|
+
except json.JSONDecodeError:
|
|
263
|
+
# Return as text
|
|
264
|
+
metadata["parsed_as"] = "text"
|
|
265
|
+
return True, text, None, metadata
|
|
266
|
+
|
|
267
|
+
async def get(
|
|
268
|
+
self,
|
|
269
|
+
url: str,
|
|
270
|
+
headers: Optional[Dict[str, str]] = None,
|
|
271
|
+
params: Optional[Dict[str, Any]] = None,
|
|
272
|
+
) -> ToolResult:
|
|
273
|
+
"""Execute GET request.
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
url: Target URL.
|
|
277
|
+
headers: Optional additional headers.
|
|
278
|
+
params: Optional query parameters.
|
|
279
|
+
|
|
280
|
+
Returns:
|
|
281
|
+
ToolResult with parsed response data.
|
|
282
|
+
"""
|
|
283
|
+
# Validate URL
|
|
284
|
+
is_valid, error = self._validate_url(url)
|
|
285
|
+
if not is_valid:
|
|
286
|
+
return ToolResult(success=False, output=None, error=error)
|
|
287
|
+
|
|
288
|
+
# Prepare request arguments
|
|
289
|
+
kwargs: Dict[str, Any] = {}
|
|
290
|
+
if headers:
|
|
291
|
+
kwargs["headers"] = headers
|
|
292
|
+
if params:
|
|
293
|
+
kwargs["params"] = params
|
|
294
|
+
|
|
295
|
+
success, output, error, metadata = await self._execute_with_retry("GET", url, **kwargs)
|
|
296
|
+
|
|
297
|
+
return ToolResult(
|
|
298
|
+
success=success,
|
|
299
|
+
output=output,
|
|
300
|
+
error=error,
|
|
301
|
+
execution_time=0.0, # Tracked per attempt internally
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
async def post(
|
|
305
|
+
self,
|
|
306
|
+
url: str,
|
|
307
|
+
data: Optional[Union[Dict[str, Any], str]] = None,
|
|
308
|
+
headers: Optional[Dict[str, str]] = None,
|
|
309
|
+
params: Optional[Dict[str, Any]] = None,
|
|
310
|
+
) -> ToolResult:
|
|
311
|
+
"""Execute POST request.
|
|
312
|
+
|
|
313
|
+
Args:
|
|
314
|
+
url: Target URL.
|
|
315
|
+
data: Request body data (dict for JSON, str for raw body).
|
|
316
|
+
headers: Optional additional headers.
|
|
317
|
+
params: Optional query parameters.
|
|
318
|
+
|
|
319
|
+
Returns:
|
|
320
|
+
ToolResult with parsed response data.
|
|
321
|
+
"""
|
|
322
|
+
# Validate URL
|
|
323
|
+
is_valid, error = self._validate_url(url)
|
|
324
|
+
if not is_valid:
|
|
325
|
+
return ToolResult(success=False, output=None, error=error)
|
|
326
|
+
|
|
327
|
+
# Prepare request arguments
|
|
328
|
+
kwargs: Dict[str, Any] = {}
|
|
329
|
+
|
|
330
|
+
# Determine content type and format data
|
|
331
|
+
if data is not None:
|
|
332
|
+
if isinstance(data, dict):
|
|
333
|
+
kwargs["json"] = data
|
|
334
|
+
else:
|
|
335
|
+
kwargs["data"] = data
|
|
336
|
+
|
|
337
|
+
if headers:
|
|
338
|
+
kwargs["headers"] = headers
|
|
339
|
+
if params:
|
|
340
|
+
kwargs["params"] = params
|
|
341
|
+
|
|
342
|
+
success, output, error, metadata = await self._execute_with_retry("POST", url, **kwargs)
|
|
343
|
+
|
|
344
|
+
return ToolResult(
|
|
345
|
+
success=success,
|
|
346
|
+
output=output,
|
|
347
|
+
error=error,
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
async def put(
|
|
351
|
+
self,
|
|
352
|
+
url: str,
|
|
353
|
+
data: Optional[Union[Dict[str, Any], str]] = None,
|
|
354
|
+
headers: Optional[Dict[str, str]] = None,
|
|
355
|
+
params: Optional[Dict[str, Any]] = None,
|
|
356
|
+
) -> ToolResult:
|
|
357
|
+
"""Execute PUT request.
|
|
358
|
+
|
|
359
|
+
Args:
|
|
360
|
+
url: Target URL.
|
|
361
|
+
data: Request body data (dict for JSON, str for raw body).
|
|
362
|
+
headers: Optional additional headers.
|
|
363
|
+
params: Optional query parameters.
|
|
364
|
+
|
|
365
|
+
Returns:
|
|
366
|
+
ToolResult with parsed response data.
|
|
367
|
+
"""
|
|
368
|
+
# Validate URL
|
|
369
|
+
is_valid, error = self._validate_url(url)
|
|
370
|
+
if not is_valid:
|
|
371
|
+
return ToolResult(success=False, output=None, error=error)
|
|
372
|
+
|
|
373
|
+
# Prepare request arguments
|
|
374
|
+
kwargs: Dict[str, Any] = {}
|
|
375
|
+
|
|
376
|
+
if data is not None:
|
|
377
|
+
if isinstance(data, dict):
|
|
378
|
+
kwargs["json"] = data
|
|
379
|
+
else:
|
|
380
|
+
kwargs["data"] = data
|
|
381
|
+
|
|
382
|
+
if headers:
|
|
383
|
+
kwargs["headers"] = headers
|
|
384
|
+
if params:
|
|
385
|
+
kwargs["params"] = params
|
|
386
|
+
|
|
387
|
+
success, output, error, metadata = await self._execute_with_retry("PUT", url, **kwargs)
|
|
388
|
+
|
|
389
|
+
return ToolResult(
|
|
390
|
+
success=success,
|
|
391
|
+
output=output,
|
|
392
|
+
error=error,
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
async def delete(
|
|
396
|
+
self,
|
|
397
|
+
url: str,
|
|
398
|
+
headers: Optional[Dict[str, str]] = None,
|
|
399
|
+
params: Optional[Dict[str, Any]] = None,
|
|
400
|
+
) -> ToolResult:
|
|
401
|
+
"""Execute DELETE request.
|
|
402
|
+
|
|
403
|
+
Args:
|
|
404
|
+
url: Target URL.
|
|
405
|
+
headers: Optional additional headers.
|
|
406
|
+
params: Optional query parameters.
|
|
407
|
+
|
|
408
|
+
Returns:
|
|
409
|
+
ToolResult with parsed response data.
|
|
410
|
+
"""
|
|
411
|
+
# Validate URL
|
|
412
|
+
is_valid, error = self._validate_url(url)
|
|
413
|
+
if not is_valid:
|
|
414
|
+
return ToolResult(success=False, output=None, error=error)
|
|
415
|
+
|
|
416
|
+
# Prepare request arguments
|
|
417
|
+
kwargs: Dict[str, Any] = {}
|
|
418
|
+
if headers:
|
|
419
|
+
kwargs["headers"] = headers
|
|
420
|
+
if params:
|
|
421
|
+
kwargs["params"] = params
|
|
422
|
+
|
|
423
|
+
success, output, error, metadata = await self._execute_with_retry("DELETE", url, **kwargs)
|
|
424
|
+
|
|
425
|
+
return ToolResult(
|
|
426
|
+
success=success,
|
|
427
|
+
output=output,
|
|
428
|
+
error=error,
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
async def execute(self, **params: Any) -> ToolResult:
|
|
432
|
+
"""Execute HTTP request based on parameters.
|
|
433
|
+
|
|
434
|
+
Main entry point for Tool base class. Dispatches to appropriate
|
|
435
|
+
HTTP method based on the 'method' parameter.
|
|
436
|
+
|
|
437
|
+
Args:
|
|
438
|
+
**params: Parameters must include:
|
|
439
|
+
- method: HTTP method (get, post, put, delete)
|
|
440
|
+
- url: Target URL
|
|
441
|
+
- data: Request body (for POST, PUT)
|
|
442
|
+
- headers: Additional headers
|
|
443
|
+
- params: Query parameters
|
|
444
|
+
|
|
445
|
+
Returns:
|
|
446
|
+
ToolResult from the executed HTTP request.
|
|
447
|
+
"""
|
|
448
|
+
method = params.get("method", "").lower()
|
|
449
|
+
url = params.get("url")
|
|
450
|
+
|
|
451
|
+
if not method:
|
|
452
|
+
return ToolResult(
|
|
453
|
+
success=False,
|
|
454
|
+
output=None,
|
|
455
|
+
error="Missing required parameter: 'method'",
|
|
456
|
+
)
|
|
457
|
+
|
|
458
|
+
if not url:
|
|
459
|
+
return ToolResult(
|
|
460
|
+
success=False,
|
|
461
|
+
output=None,
|
|
462
|
+
error="Missing required parameter: 'url'",
|
|
463
|
+
)
|
|
464
|
+
|
|
465
|
+
# Extract optional parameters
|
|
466
|
+
data = params.get("data")
|
|
467
|
+
headers = params.get("headers")
|
|
468
|
+
query_params = params.get("params")
|
|
469
|
+
|
|
470
|
+
# Dispatch to appropriate method
|
|
471
|
+
if method == "get":
|
|
472
|
+
return await self.get(url, headers=headers, params=query_params)
|
|
473
|
+
|
|
474
|
+
elif method == "post":
|
|
475
|
+
return await self.post(url, data=data, headers=headers, params=query_params)
|
|
476
|
+
|
|
477
|
+
elif method == "put":
|
|
478
|
+
return await self.put(url, data=data, headers=headers, params=query_params)
|
|
479
|
+
|
|
480
|
+
elif method == "delete":
|
|
481
|
+
return await self.delete(url, headers=headers, params=query_params)
|
|
482
|
+
|
|
483
|
+
else:
|
|
484
|
+
return ToolResult(
|
|
485
|
+
success=False,
|
|
486
|
+
output=None,
|
|
487
|
+
error=f"Unknown HTTP method: '{method}'. Valid methods: get, post, put, delete",
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
async def close(self) -> None:
|
|
491
|
+
"""Close the aiohttp session and cleanup resources."""
|
|
492
|
+
async with self._session_lock:
|
|
493
|
+
if self._session and not self._session.closed:
|
|
494
|
+
await self._session.close()
|
|
495
|
+
self._session = None
|
|
496
|
+
|
|
497
|
+
def __repr__(self) -> str:
|
|
498
|
+
return f"HttpTool(timeout={self.default_timeout}, max_retries={self.retry_config.max_retries})"
|