agentrun-inner-test 0.0.62__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.
Potentially problematic release.
This version of agentrun-inner-test might be problematic. Click here for more details.
- agentrun/__init__.py +358 -0
- agentrun/agent_runtime/__client_async_template.py +466 -0
- agentrun/agent_runtime/__endpoint_async_template.py +345 -0
- agentrun/agent_runtime/__init__.py +53 -0
- agentrun/agent_runtime/__runtime_async_template.py +477 -0
- agentrun/agent_runtime/api/__data_async_template.py +58 -0
- agentrun/agent_runtime/api/__init__.py +6 -0
- agentrun/agent_runtime/api/control.py +1362 -0
- agentrun/agent_runtime/api/data.py +98 -0
- agentrun/agent_runtime/client.py +868 -0
- agentrun/agent_runtime/endpoint.py +649 -0
- agentrun/agent_runtime/model.py +362 -0
- agentrun/agent_runtime/runtime.py +904 -0
- agentrun/credential/__client_async_template.py +177 -0
- agentrun/credential/__credential_async_template.py +216 -0
- agentrun/credential/__init__.py +28 -0
- agentrun/credential/api/__init__.py +5 -0
- agentrun/credential/api/control.py +606 -0
- agentrun/credential/client.py +319 -0
- agentrun/credential/credential.py +381 -0
- agentrun/credential/model.py +248 -0
- agentrun/integration/__init__.py +21 -0
- agentrun/integration/agentscope/__init__.py +13 -0
- agentrun/integration/agentscope/adapter.py +17 -0
- agentrun/integration/agentscope/builtin.py +88 -0
- agentrun/integration/agentscope/message_adapter.py +185 -0
- agentrun/integration/agentscope/model_adapter.py +60 -0
- agentrun/integration/agentscope/tool_adapter.py +59 -0
- agentrun/integration/builtin/__init__.py +18 -0
- agentrun/integration/builtin/knowledgebase.py +137 -0
- agentrun/integration/builtin/model.py +93 -0
- agentrun/integration/builtin/sandbox.py +1234 -0
- agentrun/integration/builtin/toolset.py +47 -0
- agentrun/integration/crewai/__init__.py +13 -0
- agentrun/integration/crewai/adapter.py +9 -0
- agentrun/integration/crewai/builtin.py +88 -0
- agentrun/integration/crewai/model_adapter.py +31 -0
- agentrun/integration/crewai/tool_adapter.py +26 -0
- agentrun/integration/google_adk/__init__.py +13 -0
- agentrun/integration/google_adk/adapter.py +15 -0
- agentrun/integration/google_adk/builtin.py +88 -0
- agentrun/integration/google_adk/message_adapter.py +144 -0
- agentrun/integration/google_adk/model_adapter.py +46 -0
- agentrun/integration/google_adk/tool_adapter.py +235 -0
- agentrun/integration/langchain/__init__.py +31 -0
- agentrun/integration/langchain/adapter.py +15 -0
- agentrun/integration/langchain/builtin.py +94 -0
- agentrun/integration/langchain/message_adapter.py +141 -0
- agentrun/integration/langchain/model_adapter.py +37 -0
- agentrun/integration/langchain/tool_adapter.py +50 -0
- agentrun/integration/langgraph/__init__.py +36 -0
- agentrun/integration/langgraph/adapter.py +20 -0
- agentrun/integration/langgraph/agent_converter.py +1073 -0
- agentrun/integration/langgraph/builtin.py +88 -0
- agentrun/integration/pydantic_ai/__init__.py +13 -0
- agentrun/integration/pydantic_ai/adapter.py +13 -0
- agentrun/integration/pydantic_ai/builtin.py +88 -0
- agentrun/integration/pydantic_ai/model_adapter.py +44 -0
- agentrun/integration/pydantic_ai/tool_adapter.py +19 -0
- agentrun/integration/utils/__init__.py +112 -0
- agentrun/integration/utils/adapter.py +560 -0
- agentrun/integration/utils/canonical.py +164 -0
- agentrun/integration/utils/converter.py +134 -0
- agentrun/integration/utils/model.py +110 -0
- agentrun/integration/utils/tool.py +1759 -0
- agentrun/knowledgebase/__client_async_template.py +173 -0
- agentrun/knowledgebase/__init__.py +53 -0
- agentrun/knowledgebase/__knowledgebase_async_template.py +438 -0
- agentrun/knowledgebase/api/__data_async_template.py +414 -0
- agentrun/knowledgebase/api/__init__.py +19 -0
- agentrun/knowledgebase/api/control.py +606 -0
- agentrun/knowledgebase/api/data.py +624 -0
- agentrun/knowledgebase/client.py +311 -0
- agentrun/knowledgebase/knowledgebase.py +748 -0
- agentrun/knowledgebase/model.py +270 -0
- agentrun/memory_collection/__client_async_template.py +178 -0
- agentrun/memory_collection/__init__.py +37 -0
- agentrun/memory_collection/__memory_collection_async_template.py +457 -0
- agentrun/memory_collection/api/__init__.py +5 -0
- agentrun/memory_collection/api/control.py +610 -0
- agentrun/memory_collection/client.py +323 -0
- agentrun/memory_collection/memory_collection.py +844 -0
- agentrun/memory_collection/model.py +162 -0
- agentrun/model/__client_async_template.py +357 -0
- agentrun/model/__init__.py +57 -0
- agentrun/model/__model_proxy_async_template.py +270 -0
- agentrun/model/__model_service_async_template.py +267 -0
- agentrun/model/api/__init__.py +6 -0
- agentrun/model/api/control.py +1173 -0
- agentrun/model/api/data.py +196 -0
- agentrun/model/client.py +674 -0
- agentrun/model/model.py +235 -0
- agentrun/model/model_proxy.py +439 -0
- agentrun/model/model_service.py +438 -0
- agentrun/sandbox/__aio_sandbox_async_template.py +523 -0
- agentrun/sandbox/__browser_sandbox_async_template.py +110 -0
- agentrun/sandbox/__client_async_template.py +491 -0
- agentrun/sandbox/__code_interpreter_sandbox_async_template.py +463 -0
- agentrun/sandbox/__init__.py +69 -0
- agentrun/sandbox/__sandbox_async_template.py +463 -0
- agentrun/sandbox/__template_async_template.py +152 -0
- agentrun/sandbox/aio_sandbox.py +912 -0
- agentrun/sandbox/api/__aio_data_async_template.py +335 -0
- agentrun/sandbox/api/__browser_data_async_template.py +140 -0
- agentrun/sandbox/api/__code_interpreter_data_async_template.py +206 -0
- agentrun/sandbox/api/__init__.py +19 -0
- agentrun/sandbox/api/__sandbox_data_async_template.py +107 -0
- agentrun/sandbox/api/aio_data.py +551 -0
- agentrun/sandbox/api/browser_data.py +172 -0
- agentrun/sandbox/api/code_interpreter_data.py +396 -0
- agentrun/sandbox/api/control.py +1051 -0
- agentrun/sandbox/api/playwright_async.py +492 -0
- agentrun/sandbox/api/playwright_sync.py +492 -0
- agentrun/sandbox/api/sandbox_data.py +154 -0
- agentrun/sandbox/browser_sandbox.py +185 -0
- agentrun/sandbox/client.py +925 -0
- agentrun/sandbox/code_interpreter_sandbox.py +823 -0
- agentrun/sandbox/model.py +384 -0
- agentrun/sandbox/sandbox.py +848 -0
- agentrun/sandbox/template.py +217 -0
- agentrun/server/__init__.py +191 -0
- agentrun/server/agui_normalizer.py +180 -0
- agentrun/server/agui_protocol.py +797 -0
- agentrun/server/invoker.py +309 -0
- agentrun/server/model.py +427 -0
- agentrun/server/openai_protocol.py +535 -0
- agentrun/server/protocol.py +140 -0
- agentrun/server/server.py +208 -0
- agentrun/toolset/__client_async_template.py +62 -0
- agentrun/toolset/__init__.py +51 -0
- agentrun/toolset/__toolset_async_template.py +204 -0
- agentrun/toolset/api/__init__.py +17 -0
- agentrun/toolset/api/control.py +262 -0
- agentrun/toolset/api/mcp.py +100 -0
- agentrun/toolset/api/openapi.py +1251 -0
- agentrun/toolset/client.py +102 -0
- agentrun/toolset/model.py +321 -0
- agentrun/toolset/toolset.py +271 -0
- agentrun/utils/__data_api_async_template.py +721 -0
- agentrun/utils/__init__.py +5 -0
- agentrun/utils/__resource_async_template.py +158 -0
- agentrun/utils/config.py +270 -0
- agentrun/utils/control_api.py +105 -0
- agentrun/utils/data_api.py +1121 -0
- agentrun/utils/exception.py +151 -0
- agentrun/utils/helper.py +108 -0
- agentrun/utils/log.py +77 -0
- agentrun/utils/model.py +168 -0
- agentrun/utils/resource.py +291 -0
- agentrun_inner_test-0.0.62.dist-info/METADATA +265 -0
- agentrun_inner_test-0.0.62.dist-info/RECORD +154 -0
- agentrun_inner_test-0.0.62.dist-info/WHEEL +5 -0
- agentrun_inner_test-0.0.62.dist-info/licenses/LICENSE +201 -0
- agentrun_inner_test-0.0.62.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,721 @@
|
|
|
1
|
+
"""AgentRun Data Client SDK / AgentRun 数据客户端 SDK
|
|
2
|
+
|
|
3
|
+
This module provides an async HTTP client for interacting with the AgentRun Data API.
|
|
4
|
+
此模块提供用于与 AgentRun Data API 交互的异步 HTTP 客户端。
|
|
5
|
+
|
|
6
|
+
It supports standard HTTP methods (GET, POST, PUT, PATCH, DELETE) with proper
|
|
7
|
+
error handling, type hints, and JSON serialization.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from enum import Enum
|
|
11
|
+
from typing import Any, Dict, Optional, Union
|
|
12
|
+
from urllib.parse import parse_qs, urlencode, urlparse, urlunparse
|
|
13
|
+
|
|
14
|
+
import httpx
|
|
15
|
+
|
|
16
|
+
from agentrun.utils.config import Config
|
|
17
|
+
from agentrun.utils.exception import ClientError
|
|
18
|
+
from agentrun.utils.helper import mask_password
|
|
19
|
+
from agentrun.utils.log import logger
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ResourceType(Enum):
|
|
23
|
+
Runtime = "runtime"
|
|
24
|
+
LiteLLM = "litellm"
|
|
25
|
+
Tool = "tool"
|
|
26
|
+
Template = "template"
|
|
27
|
+
Sandbox = "sandbox"
|
|
28
|
+
KnowledgeBase = "knowledgebase"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class DataAPI:
|
|
32
|
+
"""
|
|
33
|
+
Async HTTP client for AgentRun Data API.
|
|
34
|
+
|
|
35
|
+
This client provides async methods for making HTTP requests to the AgentRun Data API
|
|
36
|
+
with automatic URL construction, JSON handling, and error management.
|
|
37
|
+
|
|
38
|
+
The client automatically manages HTTP sessions - no need for manual session management
|
|
39
|
+
or context managers in simple use cases.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def __init__(
|
|
43
|
+
self,
|
|
44
|
+
resource_name: str,
|
|
45
|
+
resource_type: ResourceType,
|
|
46
|
+
config: Optional[Config] = None,
|
|
47
|
+
namespace: str = "agents",
|
|
48
|
+
):
|
|
49
|
+
"""
|
|
50
|
+
Initialize the AgentRun Data Client.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
region: Aliyun region (default: "cn-hangzhou")
|
|
54
|
+
protocol: Protocol to use (default: "https")
|
|
55
|
+
account_id: Aliyun account ID
|
|
56
|
+
version: API version (default: "2025-09-10")
|
|
57
|
+
namespace: API namespace (default: "agents")
|
|
58
|
+
timeout: Request timeout in seconds (default: 600)
|
|
59
|
+
headers: Default headers to include in all requests
|
|
60
|
+
auto_manage_session: Whether to automatically manage sessions (default: True)
|
|
61
|
+
|
|
62
|
+
Raises:
|
|
63
|
+
ValueError: If account_id is not provided or protocol is invalid
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
self.resource_name = resource_name
|
|
67
|
+
self.resource_type = resource_type
|
|
68
|
+
self.access_token = None
|
|
69
|
+
|
|
70
|
+
self.config = Config.with_configs(config)
|
|
71
|
+
self.namespace = namespace
|
|
72
|
+
|
|
73
|
+
if self.config.get_token():
|
|
74
|
+
logger.debug(
|
|
75
|
+
"using provided access token from config, %s",
|
|
76
|
+
mask_password(self.config.get_token() or ""),
|
|
77
|
+
)
|
|
78
|
+
self.access_token = self.config.get_token()
|
|
79
|
+
|
|
80
|
+
def get_base_url(self) -> str:
|
|
81
|
+
"""
|
|
82
|
+
Get the base URL for API requests.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
The base URL string
|
|
86
|
+
"""
|
|
87
|
+
return self.config.get_data_endpoint()
|
|
88
|
+
|
|
89
|
+
def with_path(
|
|
90
|
+
self, path: str, query: Optional[Dict[str, Any]] = None
|
|
91
|
+
) -> str:
|
|
92
|
+
"""
|
|
93
|
+
Construct full URL with the given path and query parameters.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
path: API path (may include query string)
|
|
97
|
+
query: Query parameters to add/merge
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
Complete URL string with query parameters
|
|
101
|
+
|
|
102
|
+
Examples:
|
|
103
|
+
>>> client.with_path("resources")
|
|
104
|
+
"http://account.agentrun-data.cn-hangzhou.aliyuncs.com/2025-09-10/agents/resources"
|
|
105
|
+
|
|
106
|
+
>>> client.with_path("resources", {"limit": 10})
|
|
107
|
+
"http://account.agentrun-data.cn-hangzhou.aliyuncs.com/2025-09-10/agents/resources?limit=10"
|
|
108
|
+
|
|
109
|
+
>>> client.with_path("resources?page=1", {"limit": 10})
|
|
110
|
+
"http://account.agentrun-data.cn-hangzhou.aliyuncs.com/2025-09-10/agents/resources?page=1&limit=10"
|
|
111
|
+
"""
|
|
112
|
+
# Remove leading slash if present
|
|
113
|
+
path = path.lstrip("/")
|
|
114
|
+
base_url = "/".join([
|
|
115
|
+
part.strip("/")
|
|
116
|
+
for part in [
|
|
117
|
+
self.get_base_url(),
|
|
118
|
+
self.namespace,
|
|
119
|
+
path,
|
|
120
|
+
]
|
|
121
|
+
if part
|
|
122
|
+
])
|
|
123
|
+
|
|
124
|
+
# If no query parameters, return the base URL
|
|
125
|
+
if not query:
|
|
126
|
+
return base_url
|
|
127
|
+
|
|
128
|
+
# Parse the URL to handle existing query parameters
|
|
129
|
+
parsed = urlparse(base_url)
|
|
130
|
+
|
|
131
|
+
# Parse existing query parameters
|
|
132
|
+
existing_params = parse_qs(parsed.query, keep_blank_values=True)
|
|
133
|
+
|
|
134
|
+
# Merge with new query parameters
|
|
135
|
+
# Convert new query dict to the same format as parse_qs (values as lists)
|
|
136
|
+
for key, value in query.items():
|
|
137
|
+
if isinstance(value, list):
|
|
138
|
+
existing_params[key] = value
|
|
139
|
+
else:
|
|
140
|
+
existing_params[key] = [str(value)]
|
|
141
|
+
|
|
142
|
+
# Flatten the parameters (convert lists to single values where appropriate)
|
|
143
|
+
flattened_params = {}
|
|
144
|
+
for key, value_list in existing_params.items():
|
|
145
|
+
if len(value_list) == 1:
|
|
146
|
+
flattened_params[key] = value_list[0]
|
|
147
|
+
else:
|
|
148
|
+
flattened_params[key] = value_list
|
|
149
|
+
|
|
150
|
+
# Encode query string
|
|
151
|
+
new_query = urlencode(flattened_params, doseq=True)
|
|
152
|
+
|
|
153
|
+
# Reconstruct URL with new query string
|
|
154
|
+
return urlunparse((
|
|
155
|
+
parsed.scheme,
|
|
156
|
+
parsed.netloc,
|
|
157
|
+
parsed.path,
|
|
158
|
+
parsed.params,
|
|
159
|
+
new_query,
|
|
160
|
+
parsed.fragment,
|
|
161
|
+
))
|
|
162
|
+
|
|
163
|
+
def auth(
|
|
164
|
+
self,
|
|
165
|
+
url: str = "",
|
|
166
|
+
headers: Optional[Dict[str, str]] = None,
|
|
167
|
+
query: Optional[Dict[str, Any]] = None,
|
|
168
|
+
config: Optional[Config] = None,
|
|
169
|
+
) -> tuple[str, Dict[str, str], Optional[Dict[str, Any]]]:
|
|
170
|
+
"""
|
|
171
|
+
Authentication hook for modifying requests before sending.
|
|
172
|
+
|
|
173
|
+
This method can be overridden in subclasses to implement custom
|
|
174
|
+
authentication logic (e.g., signing requests, adding auth tokens).
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
url: The request URL
|
|
178
|
+
headers: The request headers
|
|
179
|
+
query: The query parameters
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
Tuple of (modified_url, modified_headers, modified_query)
|
|
183
|
+
|
|
184
|
+
Examples:
|
|
185
|
+
Override this method to add custom authentication:
|
|
186
|
+
|
|
187
|
+
>>> class AuthedClient(AgentRunDataClient):
|
|
188
|
+
... def auth(self, url, headers, query):
|
|
189
|
+
... # Add auth token to headers
|
|
190
|
+
... headers["Authorization"] = "Bearer token123"
|
|
191
|
+
... # Or add signature to query
|
|
192
|
+
... query = query or {}
|
|
193
|
+
... query["signature"] = self._sign_request(url)
|
|
194
|
+
... return url, headers, query
|
|
195
|
+
"""
|
|
196
|
+
cfg = Config.with_configs(self.config, config)
|
|
197
|
+
|
|
198
|
+
if (
|
|
199
|
+
self.access_token is None
|
|
200
|
+
and self.resource_name
|
|
201
|
+
and self.resource_type
|
|
202
|
+
and not cfg.get_token()
|
|
203
|
+
):
|
|
204
|
+
try:
|
|
205
|
+
from alibabacloud_agentrun20250910.models import (
|
|
206
|
+
GetAccessTokenRequest,
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
from .control_api import ControlAPI
|
|
210
|
+
|
|
211
|
+
cli = ControlAPI(self.config)._get_client()
|
|
212
|
+
input = (
|
|
213
|
+
GetAccessTokenRequest(
|
|
214
|
+
resource_id=self.resource_name,
|
|
215
|
+
resource_type=self.resource_type.value,
|
|
216
|
+
)
|
|
217
|
+
if self.resource_type == ResourceType.Sandbox
|
|
218
|
+
else GetAccessTokenRequest(
|
|
219
|
+
resource_name=self.resource_name,
|
|
220
|
+
resource_type=self.resource_type.value,
|
|
221
|
+
)
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
resp = cli.get_access_token(input)
|
|
225
|
+
self.access_token = resp.body.data.access_token
|
|
226
|
+
|
|
227
|
+
except Exception as e:
|
|
228
|
+
logger.warning(
|
|
229
|
+
"Failed to get access token for"
|
|
230
|
+
f" {self.resource_type}({self.resource_name}): {e}"
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
logger.debug(
|
|
234
|
+
"fetching access token for resource %s of type %s, %s",
|
|
235
|
+
self.resource_name,
|
|
236
|
+
self.resource_type,
|
|
237
|
+
mask_password(self.access_token or ""),
|
|
238
|
+
)
|
|
239
|
+
headers = {
|
|
240
|
+
"Agentrun-Access-Token": cfg.get_token() or self.access_token or "",
|
|
241
|
+
**cfg.get_headers(),
|
|
242
|
+
**(headers or {}),
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return url, headers, query
|
|
246
|
+
|
|
247
|
+
def _prepare_request(
|
|
248
|
+
self,
|
|
249
|
+
method: str,
|
|
250
|
+
url: str,
|
|
251
|
+
data: Optional[Union[Dict[str, Any], str]] = None,
|
|
252
|
+
headers: Optional[Dict[str, str]] = None,
|
|
253
|
+
query: Optional[Dict[str, Any]] = None,
|
|
254
|
+
config: Optional[Config] = None,
|
|
255
|
+
):
|
|
256
|
+
req_headers = {
|
|
257
|
+
"Content-Type": "application/json",
|
|
258
|
+
"User-Agent": "AgentRunDataClient/1.0",
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
# Merge with instance default headers
|
|
262
|
+
cfg = Config.with_configs(self.config, config)
|
|
263
|
+
|
|
264
|
+
req_headers.update(cfg.get_headers())
|
|
265
|
+
|
|
266
|
+
# Merge request-specific headers
|
|
267
|
+
if headers:
|
|
268
|
+
req_headers.update(headers)
|
|
269
|
+
|
|
270
|
+
# Apply authentication (may modify URL, headers, and query)
|
|
271
|
+
url, req_headers, query = self.auth(url, req_headers, query, config=cfg)
|
|
272
|
+
|
|
273
|
+
# Add query parameters to URL if provided
|
|
274
|
+
if query:
|
|
275
|
+
parsed = urlparse(url)
|
|
276
|
+
existing_params = parse_qs(parsed.query, keep_blank_values=True)
|
|
277
|
+
|
|
278
|
+
# Merge query parameters
|
|
279
|
+
for key, value in query.items():
|
|
280
|
+
if isinstance(value, list):
|
|
281
|
+
existing_params[key] = value
|
|
282
|
+
else:
|
|
283
|
+
existing_params[key] = [str(value)]
|
|
284
|
+
|
|
285
|
+
# Flatten and encode
|
|
286
|
+
flattened_params = {}
|
|
287
|
+
for key, value_list in existing_params.items():
|
|
288
|
+
if len(value_list) == 1:
|
|
289
|
+
flattened_params[key] = value_list[0]
|
|
290
|
+
else:
|
|
291
|
+
flattened_params[key] = value_list
|
|
292
|
+
|
|
293
|
+
new_query = urlencode(flattened_params, doseq=True)
|
|
294
|
+
url = urlunparse((
|
|
295
|
+
parsed.scheme,
|
|
296
|
+
parsed.netloc,
|
|
297
|
+
parsed.path,
|
|
298
|
+
parsed.params,
|
|
299
|
+
new_query,
|
|
300
|
+
parsed.fragment,
|
|
301
|
+
))
|
|
302
|
+
|
|
303
|
+
# Prepare request body
|
|
304
|
+
req_json = None
|
|
305
|
+
req_content = None
|
|
306
|
+
|
|
307
|
+
if data is not None:
|
|
308
|
+
if isinstance(data, dict):
|
|
309
|
+
req_json = data
|
|
310
|
+
elif isinstance(data, str):
|
|
311
|
+
req_content = data
|
|
312
|
+
else:
|
|
313
|
+
req_content = str(data)
|
|
314
|
+
|
|
315
|
+
logger.debug(
|
|
316
|
+
"%s %s headers=%s, json=%s, content=%s",
|
|
317
|
+
method,
|
|
318
|
+
url,
|
|
319
|
+
req_headers,
|
|
320
|
+
req_json,
|
|
321
|
+
req_content,
|
|
322
|
+
)
|
|
323
|
+
return method, url, req_headers, req_json, req_content
|
|
324
|
+
|
|
325
|
+
async def _make_request_async(
|
|
326
|
+
self,
|
|
327
|
+
method: str,
|
|
328
|
+
url: str,
|
|
329
|
+
data: Optional[Union[Dict[str, Any], str]] = None,
|
|
330
|
+
headers: Optional[Dict[str, str]] = None,
|
|
331
|
+
query: Optional[Dict[str, Any]] = None,
|
|
332
|
+
config: Optional[Config] = None,
|
|
333
|
+
) -> Dict[str, Any]:
|
|
334
|
+
"""
|
|
335
|
+
Make a sync HTTP request using httpx.
|
|
336
|
+
|
|
337
|
+
Args:
|
|
338
|
+
method: HTTP method (GET, POST, PUT, PATCH, DELETE)
|
|
339
|
+
url: Full URL to request
|
|
340
|
+
data: Request body (dict or string)
|
|
341
|
+
headers: Additional headers to include
|
|
342
|
+
query: Query parameters to add to URL
|
|
343
|
+
|
|
344
|
+
Returns:
|
|
345
|
+
Response body as dictionary
|
|
346
|
+
|
|
347
|
+
Raises:
|
|
348
|
+
AgentRunClientError: If the request fails
|
|
349
|
+
"""
|
|
350
|
+
method, url, req_headers, req_json, req_content = self._prepare_request(
|
|
351
|
+
method, url, data, headers, query, config=config
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
try:
|
|
355
|
+
async with httpx.AsyncClient(
|
|
356
|
+
timeout=self.config.get_timeout()
|
|
357
|
+
) as client:
|
|
358
|
+
response = await client.request(
|
|
359
|
+
method,
|
|
360
|
+
url,
|
|
361
|
+
headers=req_headers,
|
|
362
|
+
json=req_json,
|
|
363
|
+
content=req_content,
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
# # Raise for HTTP error status codes
|
|
367
|
+
# response.raise_for_status()
|
|
368
|
+
|
|
369
|
+
response_text = response.text
|
|
370
|
+
logger.debug(f"Response: {response_text}")
|
|
371
|
+
|
|
372
|
+
# Parse JSON response
|
|
373
|
+
if response_text:
|
|
374
|
+
try:
|
|
375
|
+
return response.json()
|
|
376
|
+
except ValueError as e:
|
|
377
|
+
error_msg = f"Failed to parse JSON response: {e}"
|
|
378
|
+
bad_gateway_error_message = "502 Bad Gateway"
|
|
379
|
+
if response.status_code == 502 and (
|
|
380
|
+
bad_gateway_error_message in response_text
|
|
381
|
+
):
|
|
382
|
+
error_msg = bad_gateway_error_message
|
|
383
|
+
logger.error(error_msg)
|
|
384
|
+
raise ClientError(
|
|
385
|
+
status_code=response.status_code, message=error_msg
|
|
386
|
+
) from e
|
|
387
|
+
|
|
388
|
+
return {}
|
|
389
|
+
|
|
390
|
+
except httpx.HTTPStatusError as e:
|
|
391
|
+
# HTTP error response
|
|
392
|
+
error_text = ""
|
|
393
|
+
try:
|
|
394
|
+
error_text = e.response.text
|
|
395
|
+
except Exception:
|
|
396
|
+
error_text = str(e)
|
|
397
|
+
|
|
398
|
+
error_msg = (
|
|
399
|
+
f"HTTP {e.response.status_code} error:"
|
|
400
|
+
f" {error_text or e.response.reason_phrase}"
|
|
401
|
+
)
|
|
402
|
+
raise ClientError(
|
|
403
|
+
status_code=e.response.status_code, message=error_msg
|
|
404
|
+
) from e
|
|
405
|
+
|
|
406
|
+
except httpx.RequestError as e:
|
|
407
|
+
error_msg = f"Request error: {e!s}"
|
|
408
|
+
raise ClientError(status_code=0, message=error_msg) from e
|
|
409
|
+
|
|
410
|
+
async def get_async(
|
|
411
|
+
self,
|
|
412
|
+
path: str,
|
|
413
|
+
query: Optional[Dict[str, Any]] = None,
|
|
414
|
+
headers: Optional[Dict[str, str]] = None,
|
|
415
|
+
config: Optional[Config] = None,
|
|
416
|
+
) -> Dict[str, Any]:
|
|
417
|
+
"""
|
|
418
|
+
Make an async GET request.
|
|
419
|
+
|
|
420
|
+
Args:
|
|
421
|
+
path: API path (may include query string)
|
|
422
|
+
query: Query parameters to add/merge
|
|
423
|
+
headers: Additional headers
|
|
424
|
+
|
|
425
|
+
Returns:
|
|
426
|
+
Response body as dictionary
|
|
427
|
+
|
|
428
|
+
Raises:
|
|
429
|
+
AgentRunClientError: If the request fails
|
|
430
|
+
|
|
431
|
+
Examples:
|
|
432
|
+
>>> await client.get("resources")
|
|
433
|
+
>>> await client.get("resources", query={"limit": 10, "page": 1})
|
|
434
|
+
>>> await client.get("resources?status=active", query={"limit": 10})
|
|
435
|
+
"""
|
|
436
|
+
return await self._make_request_async(
|
|
437
|
+
"GET",
|
|
438
|
+
self.with_path(path, query=query),
|
|
439
|
+
headers=headers,
|
|
440
|
+
config=config,
|
|
441
|
+
)
|
|
442
|
+
|
|
443
|
+
async def post_async(
|
|
444
|
+
self,
|
|
445
|
+
path: str,
|
|
446
|
+
data: Optional[Union[Dict[str, Any], str]] = None,
|
|
447
|
+
query: Optional[Dict[str, Any]] = None,
|
|
448
|
+
headers: Optional[Dict[str, str]] = None,
|
|
449
|
+
config: Optional[Config] = None,
|
|
450
|
+
) -> Dict[str, Any]:
|
|
451
|
+
"""
|
|
452
|
+
Make an async POST request.
|
|
453
|
+
|
|
454
|
+
Args:
|
|
455
|
+
path: API path (may include query string)
|
|
456
|
+
data: Request body
|
|
457
|
+
query: Query parameters to add/merge
|
|
458
|
+
headers: Additional headers
|
|
459
|
+
|
|
460
|
+
Returns:
|
|
461
|
+
Response body as dictionary
|
|
462
|
+
|
|
463
|
+
Raises:
|
|
464
|
+
AgentRunClientError: If the request fails
|
|
465
|
+
|
|
466
|
+
Examples:
|
|
467
|
+
>>> await client.post("resources", data={"name": "test"})
|
|
468
|
+
>>> await client.post("resources", data={"name": "test"}, query={"async": "true"})
|
|
469
|
+
"""
|
|
470
|
+
|
|
471
|
+
return await self._make_request_async(
|
|
472
|
+
"POST",
|
|
473
|
+
self.with_path(path, query=query),
|
|
474
|
+
data=data,
|
|
475
|
+
headers=headers,
|
|
476
|
+
config=config,
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
async def put_async(
|
|
480
|
+
self,
|
|
481
|
+
path: str,
|
|
482
|
+
data: Optional[Union[Dict[str, Any], str]] = None,
|
|
483
|
+
query: Optional[Dict[str, Any]] = None,
|
|
484
|
+
headers: Optional[Dict[str, str]] = None,
|
|
485
|
+
config: Optional[Config] = None,
|
|
486
|
+
) -> Dict[str, Any]:
|
|
487
|
+
"""
|
|
488
|
+
Make an async PUT request.
|
|
489
|
+
|
|
490
|
+
Args:
|
|
491
|
+
path: API path (may include query string)
|
|
492
|
+
data: Request body
|
|
493
|
+
query: Query parameters to add/merge
|
|
494
|
+
headers: Additional headers
|
|
495
|
+
|
|
496
|
+
Returns:
|
|
497
|
+
Response body as dictionary
|
|
498
|
+
|
|
499
|
+
Raises:
|
|
500
|
+
AgentRunClientError: If the request fails
|
|
501
|
+
"""
|
|
502
|
+
return await self._make_request_async(
|
|
503
|
+
"PUT",
|
|
504
|
+
self.with_path(path, query=query),
|
|
505
|
+
data=data,
|
|
506
|
+
headers=headers,
|
|
507
|
+
config=config,
|
|
508
|
+
)
|
|
509
|
+
|
|
510
|
+
async def patch_async(
|
|
511
|
+
self,
|
|
512
|
+
path: str,
|
|
513
|
+
data: Optional[Union[Dict[str, Any], str]] = None,
|
|
514
|
+
query: Optional[Dict[str, Any]] = None,
|
|
515
|
+
headers: Optional[Dict[str, str]] = None,
|
|
516
|
+
config: Optional[Config] = None,
|
|
517
|
+
) -> Dict[str, Any]:
|
|
518
|
+
"""
|
|
519
|
+
Make an async PATCH request.
|
|
520
|
+
|
|
521
|
+
Args:
|
|
522
|
+
path: API path (may include query string)
|
|
523
|
+
data: Request body
|
|
524
|
+
query: Query parameters to add/merge
|
|
525
|
+
headers: Additional headers
|
|
526
|
+
|
|
527
|
+
Returns:
|
|
528
|
+
Response body as dictionary
|
|
529
|
+
|
|
530
|
+
Raises:
|
|
531
|
+
AgentRunClientError: If the request fails
|
|
532
|
+
"""
|
|
533
|
+
return await self._make_request_async(
|
|
534
|
+
"PATCH",
|
|
535
|
+
self.with_path(path, query=query),
|
|
536
|
+
data=data,
|
|
537
|
+
headers=headers,
|
|
538
|
+
config=config,
|
|
539
|
+
)
|
|
540
|
+
|
|
541
|
+
async def delete_async(
|
|
542
|
+
self,
|
|
543
|
+
path: str,
|
|
544
|
+
query: Optional[Dict[str, Any]] = None,
|
|
545
|
+
headers: Optional[Dict[str, str]] = None,
|
|
546
|
+
config: Optional[Config] = None,
|
|
547
|
+
) -> Dict[str, Any]:
|
|
548
|
+
"""
|
|
549
|
+
Make an async DELETE request.
|
|
550
|
+
|
|
551
|
+
Args:
|
|
552
|
+
path: API path (may include query string)
|
|
553
|
+
query: Query parameters to add/merge
|
|
554
|
+
headers: Additional headers
|
|
555
|
+
|
|
556
|
+
Returns:
|
|
557
|
+
Response body as dictionary
|
|
558
|
+
|
|
559
|
+
Raises:
|
|
560
|
+
AgentRunClientError: If the request fails
|
|
561
|
+
"""
|
|
562
|
+
return await self._make_request_async(
|
|
563
|
+
"DELETE",
|
|
564
|
+
self.with_path(path, query=query),
|
|
565
|
+
headers=headers,
|
|
566
|
+
config=config,
|
|
567
|
+
)
|
|
568
|
+
|
|
569
|
+
async def post_file_async(
|
|
570
|
+
self,
|
|
571
|
+
path: str,
|
|
572
|
+
local_file_path: str,
|
|
573
|
+
target_file_path: str,
|
|
574
|
+
form_data: Optional[Dict[str, Any]] = None,
|
|
575
|
+
query: Optional[Dict[str, Any]] = None,
|
|
576
|
+
headers: Optional[Dict[str, str]] = None,
|
|
577
|
+
config: Optional[Config] = None,
|
|
578
|
+
) -> Dict[str, Any]:
|
|
579
|
+
"""
|
|
580
|
+
Asynchronously upload a file using multipart/form-data (POST request).
|
|
581
|
+
|
|
582
|
+
Args:
|
|
583
|
+
path: API path (may include query string)
|
|
584
|
+
local_file_path: Local file path to upload
|
|
585
|
+
target_file_path: Target file path on the server
|
|
586
|
+
form_data: Additional form data fields
|
|
587
|
+
query: Query parameters to add/merge
|
|
588
|
+
headers: Additional headers
|
|
589
|
+
|
|
590
|
+
Returns:
|
|
591
|
+
Response body as dictionary
|
|
592
|
+
|
|
593
|
+
Raises:
|
|
594
|
+
AgentRunClientError: If the request fails
|
|
595
|
+
|
|
596
|
+
Examples:
|
|
597
|
+
>>> await client.post_file_async("/files", local_file_path="/local/data.csv", target_file_path="/remote/data.csv")
|
|
598
|
+
>>> await client.post_file_async("/files", local_file_path="/local/data.csv", target_file_path="/remote/input.csv")
|
|
599
|
+
"""
|
|
600
|
+
import os
|
|
601
|
+
|
|
602
|
+
filename = os.path.basename(local_file_path)
|
|
603
|
+
|
|
604
|
+
url = self.with_path(path, query=query)
|
|
605
|
+
req_headers = self.config.get_headers()
|
|
606
|
+
req_headers.update(headers or {})
|
|
607
|
+
|
|
608
|
+
try:
|
|
609
|
+
with open(local_file_path, "rb") as f:
|
|
610
|
+
file_content = f.read()
|
|
611
|
+
files = {"file": (filename, file_content)}
|
|
612
|
+
data = form_data or {}
|
|
613
|
+
data["path"] = target_file_path
|
|
614
|
+
|
|
615
|
+
async with httpx.AsyncClient(
|
|
616
|
+
timeout=self.config.get_timeout()
|
|
617
|
+
) as client:
|
|
618
|
+
response = await client.post(
|
|
619
|
+
url, files=files, data=data, headers=req_headers
|
|
620
|
+
)
|
|
621
|
+
response.raise_for_status()
|
|
622
|
+
return response.json()
|
|
623
|
+
except httpx.HTTPStatusError as e:
|
|
624
|
+
raise ClientError(
|
|
625
|
+
status_code=e.response.status_code, message=e.response.text
|
|
626
|
+
) from e
|
|
627
|
+
|
|
628
|
+
async def get_file_async(
|
|
629
|
+
self,
|
|
630
|
+
path: str,
|
|
631
|
+
save_path: str,
|
|
632
|
+
query: Optional[Dict[str, Any]] = None,
|
|
633
|
+
headers: Optional[Dict[str, str]] = None,
|
|
634
|
+
config: Optional[Config] = None,
|
|
635
|
+
) -> Dict[str, Any]:
|
|
636
|
+
"""
|
|
637
|
+
Asynchronously download a file and save it to local path (GET request).
|
|
638
|
+
|
|
639
|
+
Args:
|
|
640
|
+
path: API path (may include query string)
|
|
641
|
+
save_path: Local file path to save the downloaded file
|
|
642
|
+
query: Query parameters to add/merge
|
|
643
|
+
headers: Additional headers
|
|
644
|
+
|
|
645
|
+
Returns:
|
|
646
|
+
Dictionary with 'saved_path' and 'size' keys
|
|
647
|
+
|
|
648
|
+
Raises:
|
|
649
|
+
AgentRunClientError: If the request fails
|
|
650
|
+
|
|
651
|
+
Examples:
|
|
652
|
+
>>> await client.get_file_async("/files", save_path="/local/data.csv", query={"path": "/remote/file.csv"})
|
|
653
|
+
"""
|
|
654
|
+
url = self.with_path(path, query=query)
|
|
655
|
+
req_headers = self.config.get_headers()
|
|
656
|
+
req_headers.update(headers or {})
|
|
657
|
+
|
|
658
|
+
try:
|
|
659
|
+
async with httpx.AsyncClient(
|
|
660
|
+
timeout=self.config.get_timeout()
|
|
661
|
+
) as client:
|
|
662
|
+
response = await client.get(url, headers=req_headers)
|
|
663
|
+
response.raise_for_status()
|
|
664
|
+
|
|
665
|
+
with open(save_path, "wb") as f:
|
|
666
|
+
f.write(response.content)
|
|
667
|
+
|
|
668
|
+
return {"saved_path": save_path, "size": len(response.content)}
|
|
669
|
+
except httpx.HTTPStatusError as e:
|
|
670
|
+
raise ClientError(
|
|
671
|
+
status_code=e.response.status_code, message=e.response.text
|
|
672
|
+
) from e
|
|
673
|
+
|
|
674
|
+
async def get_video_async(
|
|
675
|
+
self,
|
|
676
|
+
path: str,
|
|
677
|
+
save_path: str,
|
|
678
|
+
query: Optional[Dict[str, Any]] = None,
|
|
679
|
+
headers: Optional[Dict[str, str]] = None,
|
|
680
|
+
config: Optional[Config] = None,
|
|
681
|
+
) -> Dict[str, Any]:
|
|
682
|
+
"""
|
|
683
|
+
Asynchronously download a video file and save it to local path (GET request).
|
|
684
|
+
|
|
685
|
+
Args:
|
|
686
|
+
path: API path (may include query string)
|
|
687
|
+
save_path: Local file path to save the downloaded video file (.mkv)
|
|
688
|
+
query: Query parameters to add/merge
|
|
689
|
+
headers: Additional headers
|
|
690
|
+
|
|
691
|
+
Returns:
|
|
692
|
+
Dictionary with 'saved_path' and 'size' keys
|
|
693
|
+
|
|
694
|
+
Raises:
|
|
695
|
+
AgentRunClientError: If the request fails
|
|
696
|
+
|
|
697
|
+
Examples:
|
|
698
|
+
>>> await client.get_video_async("/videos", save_path="/local/video.mkv", query={"path": "/remote/video.mp4"})
|
|
699
|
+
"""
|
|
700
|
+
url = self.with_path(path, query=query)
|
|
701
|
+
req_headers = self.config.get_headers()
|
|
702
|
+
req_headers.update(headers or {})
|
|
703
|
+
# Apply authentication (may modify URL, headers, and query)
|
|
704
|
+
cfg = Config.with_configs(self.config, config)
|
|
705
|
+
url, req_headers, query = self.auth(url, req_headers, query, config=cfg)
|
|
706
|
+
|
|
707
|
+
try:
|
|
708
|
+
async with httpx.AsyncClient(
|
|
709
|
+
timeout=self.config.get_timeout()
|
|
710
|
+
) as client:
|
|
711
|
+
response = await client.get(url, headers=req_headers)
|
|
712
|
+
response.raise_for_status()
|
|
713
|
+
|
|
714
|
+
with open(save_path, "wb") as f:
|
|
715
|
+
f.write(response.content)
|
|
716
|
+
|
|
717
|
+
return {"saved_path": save_path, "size": len(response.content)}
|
|
718
|
+
except httpx.HTTPStatusError as e:
|
|
719
|
+
raise ClientError(
|
|
720
|
+
status_code=e.response.status_code, message=e.response.text
|
|
721
|
+
) from e
|