agentrun-inner-test 0.0.46__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.
Files changed (135) hide show
  1. agentrun/__init__.py +325 -0
  2. agentrun/agent_runtime/__client_async_template.py +466 -0
  3. agentrun/agent_runtime/__endpoint_async_template.py +345 -0
  4. agentrun/agent_runtime/__init__.py +53 -0
  5. agentrun/agent_runtime/__runtime_async_template.py +477 -0
  6. agentrun/agent_runtime/api/__data_async_template.py +58 -0
  7. agentrun/agent_runtime/api/__init__.py +6 -0
  8. agentrun/agent_runtime/api/control.py +1362 -0
  9. agentrun/agent_runtime/api/data.py +98 -0
  10. agentrun/agent_runtime/client.py +868 -0
  11. agentrun/agent_runtime/endpoint.py +649 -0
  12. agentrun/agent_runtime/model.py +362 -0
  13. agentrun/agent_runtime/runtime.py +904 -0
  14. agentrun/credential/__client_async_template.py +177 -0
  15. agentrun/credential/__credential_async_template.py +216 -0
  16. agentrun/credential/__init__.py +28 -0
  17. agentrun/credential/api/__init__.py +5 -0
  18. agentrun/credential/api/control.py +606 -0
  19. agentrun/credential/client.py +319 -0
  20. agentrun/credential/credential.py +381 -0
  21. agentrun/credential/model.py +248 -0
  22. agentrun/integration/__init__.py +21 -0
  23. agentrun/integration/agentscope/__init__.py +12 -0
  24. agentrun/integration/agentscope/adapter.py +17 -0
  25. agentrun/integration/agentscope/builtin.py +65 -0
  26. agentrun/integration/agentscope/message_adapter.py +185 -0
  27. agentrun/integration/agentscope/model_adapter.py +60 -0
  28. agentrun/integration/agentscope/tool_adapter.py +59 -0
  29. agentrun/integration/builtin/__init__.py +16 -0
  30. agentrun/integration/builtin/model.py +93 -0
  31. agentrun/integration/builtin/sandbox.py +1234 -0
  32. agentrun/integration/builtin/toolset.py +47 -0
  33. agentrun/integration/crewai/__init__.py +12 -0
  34. agentrun/integration/crewai/adapter.py +9 -0
  35. agentrun/integration/crewai/builtin.py +65 -0
  36. agentrun/integration/crewai/model_adapter.py +31 -0
  37. agentrun/integration/crewai/tool_adapter.py +26 -0
  38. agentrun/integration/google_adk/__init__.py +12 -0
  39. agentrun/integration/google_adk/adapter.py +15 -0
  40. agentrun/integration/google_adk/builtin.py +65 -0
  41. agentrun/integration/google_adk/message_adapter.py +144 -0
  42. agentrun/integration/google_adk/model_adapter.py +46 -0
  43. agentrun/integration/google_adk/tool_adapter.py +235 -0
  44. agentrun/integration/langchain/__init__.py +30 -0
  45. agentrun/integration/langchain/adapter.py +15 -0
  46. agentrun/integration/langchain/builtin.py +71 -0
  47. agentrun/integration/langchain/message_adapter.py +141 -0
  48. agentrun/integration/langchain/model_adapter.py +37 -0
  49. agentrun/integration/langchain/tool_adapter.py +50 -0
  50. agentrun/integration/langgraph/__init__.py +35 -0
  51. agentrun/integration/langgraph/adapter.py +20 -0
  52. agentrun/integration/langgraph/agent_converter.py +1073 -0
  53. agentrun/integration/langgraph/builtin.py +65 -0
  54. agentrun/integration/pydantic_ai/__init__.py +12 -0
  55. agentrun/integration/pydantic_ai/adapter.py +13 -0
  56. agentrun/integration/pydantic_ai/builtin.py +65 -0
  57. agentrun/integration/pydantic_ai/model_adapter.py +44 -0
  58. agentrun/integration/pydantic_ai/tool_adapter.py +19 -0
  59. agentrun/integration/utils/__init__.py +112 -0
  60. agentrun/integration/utils/adapter.py +560 -0
  61. agentrun/integration/utils/canonical.py +164 -0
  62. agentrun/integration/utils/converter.py +134 -0
  63. agentrun/integration/utils/model.py +110 -0
  64. agentrun/integration/utils/tool.py +1759 -0
  65. agentrun/model/__client_async_template.py +357 -0
  66. agentrun/model/__init__.py +57 -0
  67. agentrun/model/__model_proxy_async_template.py +270 -0
  68. agentrun/model/__model_service_async_template.py +267 -0
  69. agentrun/model/api/__init__.py +6 -0
  70. agentrun/model/api/control.py +1173 -0
  71. agentrun/model/api/data.py +196 -0
  72. agentrun/model/client.py +674 -0
  73. agentrun/model/model.py +235 -0
  74. agentrun/model/model_proxy.py +439 -0
  75. agentrun/model/model_service.py +438 -0
  76. agentrun/sandbox/__aio_sandbox_async_template.py +523 -0
  77. agentrun/sandbox/__browser_sandbox_async_template.py +110 -0
  78. agentrun/sandbox/__client_async_template.py +491 -0
  79. agentrun/sandbox/__code_interpreter_sandbox_async_template.py +463 -0
  80. agentrun/sandbox/__init__.py +69 -0
  81. agentrun/sandbox/__sandbox_async_template.py +463 -0
  82. agentrun/sandbox/__template_async_template.py +152 -0
  83. agentrun/sandbox/aio_sandbox.py +905 -0
  84. agentrun/sandbox/api/__aio_data_async_template.py +335 -0
  85. agentrun/sandbox/api/__browser_data_async_template.py +140 -0
  86. agentrun/sandbox/api/__code_interpreter_data_async_template.py +206 -0
  87. agentrun/sandbox/api/__init__.py +19 -0
  88. agentrun/sandbox/api/__sandbox_data_async_template.py +107 -0
  89. agentrun/sandbox/api/aio_data.py +551 -0
  90. agentrun/sandbox/api/browser_data.py +172 -0
  91. agentrun/sandbox/api/code_interpreter_data.py +396 -0
  92. agentrun/sandbox/api/control.py +1051 -0
  93. agentrun/sandbox/api/playwright_async.py +492 -0
  94. agentrun/sandbox/api/playwright_sync.py +492 -0
  95. agentrun/sandbox/api/sandbox_data.py +154 -0
  96. agentrun/sandbox/browser_sandbox.py +185 -0
  97. agentrun/sandbox/client.py +925 -0
  98. agentrun/sandbox/code_interpreter_sandbox.py +823 -0
  99. agentrun/sandbox/model.py +397 -0
  100. agentrun/sandbox/sandbox.py +848 -0
  101. agentrun/sandbox/template.py +217 -0
  102. agentrun/server/__init__.py +191 -0
  103. agentrun/server/agui_normalizer.py +180 -0
  104. agentrun/server/agui_protocol.py +797 -0
  105. agentrun/server/invoker.py +309 -0
  106. agentrun/server/model.py +427 -0
  107. agentrun/server/openai_protocol.py +535 -0
  108. agentrun/server/protocol.py +140 -0
  109. agentrun/server/server.py +208 -0
  110. agentrun/toolset/__client_async_template.py +62 -0
  111. agentrun/toolset/__init__.py +51 -0
  112. agentrun/toolset/__toolset_async_template.py +204 -0
  113. agentrun/toolset/api/__init__.py +17 -0
  114. agentrun/toolset/api/control.py +262 -0
  115. agentrun/toolset/api/mcp.py +100 -0
  116. agentrun/toolset/api/openapi.py +1251 -0
  117. agentrun/toolset/client.py +102 -0
  118. agentrun/toolset/model.py +321 -0
  119. agentrun/toolset/toolset.py +270 -0
  120. agentrun/utils/__data_api_async_template.py +720 -0
  121. agentrun/utils/__init__.py +5 -0
  122. agentrun/utils/__resource_async_template.py +158 -0
  123. agentrun/utils/config.py +258 -0
  124. agentrun/utils/control_api.py +78 -0
  125. agentrun/utils/data_api.py +1120 -0
  126. agentrun/utils/exception.py +151 -0
  127. agentrun/utils/helper.py +108 -0
  128. agentrun/utils/log.py +77 -0
  129. agentrun/utils/model.py +168 -0
  130. agentrun/utils/resource.py +291 -0
  131. agentrun_inner_test-0.0.46.dist-info/METADATA +263 -0
  132. agentrun_inner_test-0.0.46.dist-info/RECORD +135 -0
  133. agentrun_inner_test-0.0.46.dist-info/WHEEL +5 -0
  134. agentrun_inner_test-0.0.46.dist-info/licenses/LICENSE +201 -0
  135. agentrun_inner_test-0.0.46.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1120 @@
1
+ """
2
+ This file is auto generated by the code generation script.
3
+ Do not modify this file manually.
4
+ Use the `make codegen` command to regenerate.
5
+
6
+ 当前文件为自动生成的控制 API 客户端代码。请勿手动修改此文件。
7
+ 使用 `make codegen` 命令重新生成。
8
+
9
+ source: agentrun/utils/__data_api_async_template.py
10
+
11
+ AgentRun Data Client SDK / AgentRun 数据客户端 SDK
12
+
13
+ This module provides an async HTTP client for interacting with the AgentRun Data API.
14
+ 此模块提供用于与 AgentRun Data API 交互的异步 HTTP 客户端。
15
+
16
+ It supports standard HTTP methods (GET, POST, PUT, PATCH, DELETE) with proper
17
+ error handling, type hints, and JSON serialization.
18
+ """
19
+
20
+ from enum import Enum
21
+ from typing import Any, Dict, Optional, Union
22
+ from urllib.parse import parse_qs, urlencode, urlparse, urlunparse
23
+
24
+ import httpx
25
+
26
+ from agentrun.utils.config import Config
27
+ from agentrun.utils.exception import ClientError
28
+ from agentrun.utils.helper import mask_password
29
+ from agentrun.utils.log import logger
30
+
31
+
32
+ class ResourceType(Enum):
33
+ Runtime = "runtime"
34
+ LiteLLM = "litellm"
35
+ Tool = "tool"
36
+ Template = "template"
37
+ Sandbox = "sandbox"
38
+
39
+
40
+ class DataAPI:
41
+ """
42
+ Async HTTP client for AgentRun Data API.
43
+
44
+ This client provides async methods for making HTTP requests to the AgentRun Data API
45
+ with automatic URL construction, JSON handling, and error management.
46
+
47
+ The client automatically manages HTTP sessions - no need for manual session management
48
+ or context managers in simple use cases.
49
+ """
50
+
51
+ def __init__(
52
+ self,
53
+ resource_name: str,
54
+ resource_type: ResourceType,
55
+ config: Optional[Config] = None,
56
+ namespace: str = "agents",
57
+ ):
58
+ """
59
+ Initialize the AgentRun Data Client.
60
+
61
+ Args:
62
+ region: Aliyun region (default: "cn-hangzhou")
63
+ protocol: Protocol to use (default: "https")
64
+ account_id: Aliyun account ID
65
+ version: API version (default: "2025-09-10")
66
+ namespace: API namespace (default: "agents")
67
+ timeout: Request timeout in seconds (default: 600)
68
+ headers: Default headers to include in all requests
69
+ auto_manage_session: Whether to automatically manage sessions (default: True)
70
+
71
+ Raises:
72
+ ValueError: If account_id is not provided or protocol is invalid
73
+ """
74
+
75
+ self.resource_name = resource_name
76
+ self.resource_type = resource_type
77
+ self.access_token = None
78
+
79
+ self.config = Config.with_configs(config)
80
+ self.namespace = namespace
81
+
82
+ if self.config.get_token():
83
+ logger.debug(
84
+ "using provided access token from config, %s",
85
+ mask_password(self.config.get_token() or ""),
86
+ )
87
+ self.access_token = self.config.get_token()
88
+
89
+ def get_base_url(self) -> str:
90
+ """
91
+ Get the base URL for API requests.
92
+
93
+ Returns:
94
+ The base URL string
95
+ """
96
+ return self.config.get_data_endpoint()
97
+
98
+ def with_path(
99
+ self, path: str, query: Optional[Dict[str, Any]] = None
100
+ ) -> str:
101
+ """
102
+ Construct full URL with the given path and query parameters.
103
+
104
+ Args:
105
+ path: API path (may include query string)
106
+ query: Query parameters to add/merge
107
+
108
+ Returns:
109
+ Complete URL string with query parameters
110
+
111
+ Examples:
112
+ >>> client.with_path("resources")
113
+ "http://account.agentrun-data.cn-hangzhou.aliyuncs.com/2025-09-10/agents/resources"
114
+
115
+ >>> client.with_path("resources", {"limit": 10})
116
+ "http://account.agentrun-data.cn-hangzhou.aliyuncs.com/2025-09-10/agents/resources?limit=10"
117
+
118
+ >>> client.with_path("resources?page=1", {"limit": 10})
119
+ "http://account.agentrun-data.cn-hangzhou.aliyuncs.com/2025-09-10/agents/resources?page=1&limit=10"
120
+ """
121
+ # Remove leading slash if present
122
+ path = path.lstrip("/")
123
+ base_url = "/".join([
124
+ part.strip("/")
125
+ for part in [
126
+ self.get_base_url(),
127
+ self.namespace,
128
+ path,
129
+ ]
130
+ if part
131
+ ])
132
+
133
+ # If no query parameters, return the base URL
134
+ if not query:
135
+ return base_url
136
+
137
+ # Parse the URL to handle existing query parameters
138
+ parsed = urlparse(base_url)
139
+
140
+ # Parse existing query parameters
141
+ existing_params = parse_qs(parsed.query, keep_blank_values=True)
142
+
143
+ # Merge with new query parameters
144
+ # Convert new query dict to the same format as parse_qs (values as lists)
145
+ for key, value in query.items():
146
+ if isinstance(value, list):
147
+ existing_params[key] = value
148
+ else:
149
+ existing_params[key] = [str(value)]
150
+
151
+ # Flatten the parameters (convert lists to single values where appropriate)
152
+ flattened_params = {}
153
+ for key, value_list in existing_params.items():
154
+ if len(value_list) == 1:
155
+ flattened_params[key] = value_list[0]
156
+ else:
157
+ flattened_params[key] = value_list
158
+
159
+ # Encode query string
160
+ new_query = urlencode(flattened_params, doseq=True)
161
+
162
+ # Reconstruct URL with new query string
163
+ return urlunparse((
164
+ parsed.scheme,
165
+ parsed.netloc,
166
+ parsed.path,
167
+ parsed.params,
168
+ new_query,
169
+ parsed.fragment,
170
+ ))
171
+
172
+ def auth(
173
+ self,
174
+ url: str = "",
175
+ headers: Optional[Dict[str, str]] = None,
176
+ query: Optional[Dict[str, Any]] = None,
177
+ config: Optional[Config] = None,
178
+ ) -> tuple[str, Dict[str, str], Optional[Dict[str, Any]]]:
179
+ """
180
+ Authentication hook for modifying requests before sending.
181
+
182
+ This method can be overridden in subclasses to implement custom
183
+ authentication logic (e.g., signing requests, adding auth tokens).
184
+
185
+ Args:
186
+ url: The request URL
187
+ headers: The request headers
188
+ query: The query parameters
189
+
190
+ Returns:
191
+ Tuple of (modified_url, modified_headers, modified_query)
192
+
193
+ Examples:
194
+ Override this method to add custom authentication:
195
+
196
+ >>> class AuthedClient(AgentRunDataClient):
197
+ ... def auth(self, url, headers, query):
198
+ ... # Add auth token to headers
199
+ ... headers["Authorization"] = "Bearer token123"
200
+ ... # Or add signature to query
201
+ ... query = query or {}
202
+ ... query["signature"] = self._sign_request(url)
203
+ ... return url, headers, query
204
+ """
205
+ cfg = Config.with_configs(self.config, config)
206
+
207
+ if (
208
+ self.access_token is None
209
+ and self.resource_name
210
+ and self.resource_type
211
+ and not cfg.get_token()
212
+ ):
213
+ try:
214
+ from alibabacloud_agentrun20250910.models import (
215
+ GetAccessTokenRequest,
216
+ )
217
+
218
+ from .control_api import ControlAPI
219
+
220
+ cli = ControlAPI(self.config)._get_client()
221
+ input = (
222
+ GetAccessTokenRequest(
223
+ resource_id=self.resource_name,
224
+ resource_type=self.resource_type.value,
225
+ )
226
+ if self.resource_type == ResourceType.Sandbox
227
+ else GetAccessTokenRequest(
228
+ resource_name=self.resource_name,
229
+ resource_type=self.resource_type.value,
230
+ )
231
+ )
232
+
233
+ resp = cli.get_access_token(input)
234
+ self.access_token = resp.body.data.access_token
235
+
236
+ except Exception as e:
237
+ logger.warning(
238
+ "Failed to get access token for"
239
+ f" {self.resource_type}({self.resource_name}): {e}"
240
+ )
241
+
242
+ logger.debug(
243
+ "fetching access token for resource %s of type %s, %s",
244
+ self.resource_name,
245
+ self.resource_type,
246
+ mask_password(self.access_token or ""),
247
+ )
248
+ headers = {
249
+ "Agentrun-Access-Token": cfg.get_token() or self.access_token or "",
250
+ **cfg.get_headers(),
251
+ **(headers or {}),
252
+ }
253
+
254
+ return url, headers, query
255
+
256
+ def _prepare_request(
257
+ self,
258
+ method: str,
259
+ url: str,
260
+ data: Optional[Union[Dict[str, Any], str]] = None,
261
+ headers: Optional[Dict[str, str]] = None,
262
+ query: Optional[Dict[str, Any]] = None,
263
+ config: Optional[Config] = None,
264
+ ):
265
+ req_headers = {
266
+ "Content-Type": "application/json",
267
+ "User-Agent": "AgentRunDataClient/1.0",
268
+ }
269
+
270
+ # Merge with instance default headers
271
+ cfg = Config.with_configs(self.config, config)
272
+
273
+ req_headers.update(cfg.get_headers())
274
+
275
+ # Merge request-specific headers
276
+ if headers:
277
+ req_headers.update(headers)
278
+
279
+ # Apply authentication (may modify URL, headers, and query)
280
+ url, req_headers, query = self.auth(url, req_headers, query, config=cfg)
281
+
282
+ # Add query parameters to URL if provided
283
+ if query:
284
+ parsed = urlparse(url)
285
+ existing_params = parse_qs(parsed.query, keep_blank_values=True)
286
+
287
+ # Merge query parameters
288
+ for key, value in query.items():
289
+ if isinstance(value, list):
290
+ existing_params[key] = value
291
+ else:
292
+ existing_params[key] = [str(value)]
293
+
294
+ # Flatten and encode
295
+ flattened_params = {}
296
+ for key, value_list in existing_params.items():
297
+ if len(value_list) == 1:
298
+ flattened_params[key] = value_list[0]
299
+ else:
300
+ flattened_params[key] = value_list
301
+
302
+ new_query = urlencode(flattened_params, doseq=True)
303
+ url = urlunparse((
304
+ parsed.scheme,
305
+ parsed.netloc,
306
+ parsed.path,
307
+ parsed.params,
308
+ new_query,
309
+ parsed.fragment,
310
+ ))
311
+
312
+ # Prepare request body
313
+ req_json = None
314
+ req_content = None
315
+
316
+ if data is not None:
317
+ if isinstance(data, dict):
318
+ req_json = data
319
+ elif isinstance(data, str):
320
+ req_content = data
321
+ else:
322
+ req_content = str(data)
323
+
324
+ logger.debug(
325
+ "%s %s headers=%s, json=%s, content=%s",
326
+ method,
327
+ url,
328
+ req_headers,
329
+ req_json,
330
+ req_content,
331
+ )
332
+ return method, url, req_headers, req_json, req_content
333
+
334
+ async def _make_request_async(
335
+ self,
336
+ method: str,
337
+ url: str,
338
+ data: Optional[Union[Dict[str, Any], str]] = None,
339
+ headers: Optional[Dict[str, str]] = None,
340
+ query: Optional[Dict[str, Any]] = None,
341
+ config: Optional[Config] = None,
342
+ ) -> Dict[str, Any]:
343
+ """
344
+ Make a sync HTTP request using httpx.
345
+
346
+ Args:
347
+ method: HTTP method (GET, POST, PUT, PATCH, DELETE)
348
+ url: Full URL to request
349
+ data: Request body (dict or string)
350
+ headers: Additional headers to include
351
+ query: Query parameters to add to URL
352
+
353
+ Returns:
354
+ Response body as dictionary
355
+
356
+ Raises:
357
+ AgentRunClientError: If the request fails
358
+ """
359
+ method, url, req_headers, req_json, req_content = self._prepare_request(
360
+ method, url, data, headers, query, config=config
361
+ )
362
+
363
+ try:
364
+ async with httpx.AsyncClient(
365
+ timeout=self.config.get_timeout()
366
+ ) as client:
367
+ response = await client.request(
368
+ method,
369
+ url,
370
+ headers=req_headers,
371
+ json=req_json,
372
+ content=req_content,
373
+ )
374
+
375
+ # # Raise for HTTP error status codes
376
+ # response.raise_for_status()
377
+
378
+ response_text = response.text
379
+ logger.debug(f"Response: {response_text}")
380
+
381
+ # Parse JSON response
382
+ if response_text:
383
+ try:
384
+ return response.json()
385
+ except ValueError as e:
386
+ error_msg = f"Failed to parse JSON response: {e}"
387
+ bad_gateway_error_message = "502 Bad Gateway"
388
+ if response.status_code == 502 and (
389
+ bad_gateway_error_message in response_text
390
+ ):
391
+ error_msg = bad_gateway_error_message
392
+ logger.error(error_msg)
393
+ raise ClientError(
394
+ status_code=response.status_code, message=error_msg
395
+ ) from e
396
+
397
+ return {}
398
+
399
+ except httpx.HTTPStatusError as e:
400
+ # HTTP error response
401
+ error_text = ""
402
+ try:
403
+ error_text = e.response.text
404
+ except Exception:
405
+ error_text = str(e)
406
+
407
+ error_msg = (
408
+ f"HTTP {e.response.status_code} error:"
409
+ f" {error_text or e.response.reason_phrase}"
410
+ )
411
+ raise ClientError(
412
+ status_code=e.response.status_code, message=error_msg
413
+ ) from e
414
+
415
+ except httpx.RequestError as e:
416
+ error_msg = f"Request error: {e!s}"
417
+ raise ClientError(status_code=0, message=error_msg) from e
418
+
419
+ def _make_request(
420
+ self,
421
+ method: str,
422
+ url: str,
423
+ data: Optional[Union[Dict[str, Any], str]] = None,
424
+ headers: Optional[Dict[str, str]] = None,
425
+ query: Optional[Dict[str, Any]] = None,
426
+ config: Optional[Config] = None,
427
+ ) -> Dict[str, Any]:
428
+ """
429
+ Make a sync HTTP request using httpx.
430
+
431
+ Args:
432
+ method: HTTP method (GET, POST, PUT, PATCH, DELETE)
433
+ url: Full URL to request
434
+ data: Request body (dict or string)
435
+ headers: Additional headers to include
436
+ query: Query parameters to add to URL
437
+
438
+ Returns:
439
+ Response body as dictionary
440
+
441
+ Raises:
442
+ AgentRunClientError: If the request fails
443
+ """
444
+ method, url, req_headers, req_json, req_content = self._prepare_request(
445
+ method, url, data, headers, query, config=config
446
+ )
447
+
448
+ try:
449
+ with httpx.Client(timeout=self.config.get_timeout()) as client:
450
+ response = client.request(
451
+ method,
452
+ url,
453
+ headers=req_headers,
454
+ json=req_json,
455
+ content=req_content,
456
+ )
457
+
458
+ # # Raise for HTTP error status codes
459
+ # response.raise_for_status()
460
+
461
+ response_text = response.text
462
+ logger.debug(f"Response: {response_text}")
463
+
464
+ # Parse JSON response
465
+ if response_text:
466
+ try:
467
+ return response.json()
468
+ except ValueError as e:
469
+ error_msg = f"Failed to parse JSON response: {e}"
470
+ bad_gateway_error_message = "502 Bad Gateway"
471
+ if response.status_code == 502 and (
472
+ bad_gateway_error_message in response_text
473
+ ):
474
+ error_msg = bad_gateway_error_message
475
+ logger.error(error_msg)
476
+ raise ClientError(
477
+ status_code=response.status_code, message=error_msg
478
+ ) from e
479
+
480
+ return {}
481
+
482
+ except httpx.HTTPStatusError as e:
483
+ # HTTP error response
484
+ error_text = ""
485
+ try:
486
+ error_text = e.response.text
487
+ except Exception:
488
+ error_text = str(e)
489
+
490
+ error_msg = (
491
+ f"HTTP {e.response.status_code} error:"
492
+ f" {error_text or e.response.reason_phrase}"
493
+ )
494
+ raise ClientError(
495
+ status_code=e.response.status_code, message=error_msg
496
+ ) from e
497
+
498
+ except httpx.RequestError as e:
499
+ error_msg = f"Request error: {e!s}"
500
+ raise ClientError(status_code=0, message=error_msg) from e
501
+
502
+ async def get_async(
503
+ self,
504
+ path: str,
505
+ query: Optional[Dict[str, Any]] = None,
506
+ headers: Optional[Dict[str, str]] = None,
507
+ config: Optional[Config] = None,
508
+ ) -> Dict[str, Any]:
509
+ """
510
+ Make an async GET request.
511
+
512
+ Args:
513
+ path: API path (may include query string)
514
+ query: Query parameters to add/merge
515
+ headers: Additional headers
516
+
517
+ Returns:
518
+ Response body as dictionary
519
+
520
+ Raises:
521
+ AgentRunClientError: If the request fails
522
+
523
+ Examples:
524
+ >>> await client.get("resources")
525
+ >>> await client.get("resources", query={"limit": 10, "page": 1})
526
+ >>> await client.get("resources?status=active", query={"limit": 10})
527
+ """
528
+ return await self._make_request_async(
529
+ "GET",
530
+ self.with_path(path, query=query),
531
+ headers=headers,
532
+ config=config,
533
+ )
534
+
535
+ def get(
536
+ self,
537
+ path: str,
538
+ query: Optional[Dict[str, Any]] = None,
539
+ headers: Optional[Dict[str, str]] = None,
540
+ config: Optional[Config] = None,
541
+ ) -> Dict[str, Any]:
542
+ """
543
+ Make an async GET request.
544
+
545
+ Args:
546
+ path: API path (may include query string)
547
+ query: Query parameters to add/merge
548
+ headers: Additional headers
549
+
550
+ Returns:
551
+ Response body as dictionary
552
+
553
+ Raises:
554
+ AgentRunClientError: If the request fails
555
+
556
+ Examples:
557
+ >>> client.get("resources")
558
+ >>> client.get("resources", query={"limit": 10, "page": 1})
559
+ >>> client.get("resources?status=active", query={"limit": 10})
560
+ """
561
+ return self._make_request(
562
+ "GET",
563
+ self.with_path(path, query=query),
564
+ headers=headers,
565
+ config=config,
566
+ )
567
+
568
+ async def post_async(
569
+ self,
570
+ path: str,
571
+ data: Optional[Union[Dict[str, Any], str]] = None,
572
+ query: Optional[Dict[str, Any]] = None,
573
+ headers: Optional[Dict[str, str]] = None,
574
+ config: Optional[Config] = None,
575
+ ) -> Dict[str, Any]:
576
+ """
577
+ Make an async POST request.
578
+
579
+ Args:
580
+ path: API path (may include query string)
581
+ data: Request body
582
+ query: Query parameters to add/merge
583
+ headers: Additional headers
584
+
585
+ Returns:
586
+ Response body as dictionary
587
+
588
+ Raises:
589
+ AgentRunClientError: If the request fails
590
+
591
+ Examples:
592
+ >>> await client.post("resources", data={"name": "test"})
593
+ >>> await client.post("resources", data={"name": "test"}, query={"async": "true"})
594
+ """
595
+
596
+ return await self._make_request_async(
597
+ "POST",
598
+ self.with_path(path, query=query),
599
+ data=data,
600
+ headers=headers,
601
+ config=config,
602
+ )
603
+
604
+ def post(
605
+ self,
606
+ path: str,
607
+ data: Optional[Union[Dict[str, Any], str]] = None,
608
+ query: Optional[Dict[str, Any]] = None,
609
+ headers: Optional[Dict[str, str]] = None,
610
+ config: Optional[Config] = None,
611
+ ) -> Dict[str, Any]:
612
+ """
613
+ Make an async POST request.
614
+
615
+ Args:
616
+ path: API path (may include query string)
617
+ data: Request body
618
+ query: Query parameters to add/merge
619
+ headers: Additional headers
620
+
621
+ Returns:
622
+ Response body as dictionary
623
+
624
+ Raises:
625
+ AgentRunClientError: If the request fails
626
+
627
+ Examples:
628
+ >>> client.post("resources", data={"name": "test"})
629
+ >>> client.post("resources", data={"name": "test"}, query={"async": "true"})
630
+ """
631
+
632
+ return self._make_request(
633
+ "POST",
634
+ self.with_path(path, query=query),
635
+ data=data,
636
+ headers=headers,
637
+ config=config,
638
+ )
639
+
640
+ async def put_async(
641
+ self,
642
+ path: str,
643
+ data: Optional[Union[Dict[str, Any], str]] = None,
644
+ query: Optional[Dict[str, Any]] = None,
645
+ headers: Optional[Dict[str, str]] = None,
646
+ config: Optional[Config] = None,
647
+ ) -> Dict[str, Any]:
648
+ """
649
+ Make an async PUT request.
650
+
651
+ Args:
652
+ path: API path (may include query string)
653
+ data: Request body
654
+ query: Query parameters to add/merge
655
+ headers: Additional headers
656
+
657
+ Returns:
658
+ Response body as dictionary
659
+
660
+ Raises:
661
+ AgentRunClientError: If the request fails
662
+ """
663
+ return await self._make_request_async(
664
+ "PUT",
665
+ self.with_path(path, query=query),
666
+ data=data,
667
+ headers=headers,
668
+ config=config,
669
+ )
670
+
671
+ def put(
672
+ self,
673
+ path: str,
674
+ data: Optional[Union[Dict[str, Any], str]] = None,
675
+ query: Optional[Dict[str, Any]] = None,
676
+ headers: Optional[Dict[str, str]] = None,
677
+ config: Optional[Config] = None,
678
+ ) -> Dict[str, Any]:
679
+ """
680
+ Make an async PUT request.
681
+
682
+ Args:
683
+ path: API path (may include query string)
684
+ data: Request body
685
+ query: Query parameters to add/merge
686
+ headers: Additional headers
687
+
688
+ Returns:
689
+ Response body as dictionary
690
+
691
+ Raises:
692
+ AgentRunClientError: If the request fails
693
+ """
694
+ return self._make_request(
695
+ "PUT",
696
+ self.with_path(path, query=query),
697
+ data=data,
698
+ headers=headers,
699
+ config=config,
700
+ )
701
+
702
+ async def patch_async(
703
+ self,
704
+ path: str,
705
+ data: Optional[Union[Dict[str, Any], str]] = None,
706
+ query: Optional[Dict[str, Any]] = None,
707
+ headers: Optional[Dict[str, str]] = None,
708
+ config: Optional[Config] = None,
709
+ ) -> Dict[str, Any]:
710
+ """
711
+ Make an async PATCH request.
712
+
713
+ Args:
714
+ path: API path (may include query string)
715
+ data: Request body
716
+ query: Query parameters to add/merge
717
+ headers: Additional headers
718
+
719
+ Returns:
720
+ Response body as dictionary
721
+
722
+ Raises:
723
+ AgentRunClientError: If the request fails
724
+ """
725
+ return await self._make_request_async(
726
+ "PATCH",
727
+ self.with_path(path, query=query),
728
+ data=data,
729
+ headers=headers,
730
+ config=config,
731
+ )
732
+
733
+ def patch(
734
+ self,
735
+ path: str,
736
+ data: Optional[Union[Dict[str, Any], str]] = None,
737
+ query: Optional[Dict[str, Any]] = None,
738
+ headers: Optional[Dict[str, str]] = None,
739
+ config: Optional[Config] = None,
740
+ ) -> Dict[str, Any]:
741
+ """
742
+ Make an async PATCH request.
743
+
744
+ Args:
745
+ path: API path (may include query string)
746
+ data: Request body
747
+ query: Query parameters to add/merge
748
+ headers: Additional headers
749
+
750
+ Returns:
751
+ Response body as dictionary
752
+
753
+ Raises:
754
+ AgentRunClientError: If the request fails
755
+ """
756
+ return self._make_request(
757
+ "PATCH",
758
+ self.with_path(path, query=query),
759
+ data=data,
760
+ headers=headers,
761
+ config=config,
762
+ )
763
+
764
+ async def delete_async(
765
+ self,
766
+ path: str,
767
+ query: Optional[Dict[str, Any]] = None,
768
+ headers: Optional[Dict[str, str]] = None,
769
+ config: Optional[Config] = None,
770
+ ) -> Dict[str, Any]:
771
+ """
772
+ Make an async DELETE request.
773
+
774
+ Args:
775
+ path: API path (may include query string)
776
+ query: Query parameters to add/merge
777
+ headers: Additional headers
778
+
779
+ Returns:
780
+ Response body as dictionary
781
+
782
+ Raises:
783
+ AgentRunClientError: If the request fails
784
+ """
785
+ return await self._make_request_async(
786
+ "DELETE",
787
+ self.with_path(path, query=query),
788
+ headers=headers,
789
+ config=config,
790
+ )
791
+
792
+ def delete(
793
+ self,
794
+ path: str,
795
+ query: Optional[Dict[str, Any]] = None,
796
+ headers: Optional[Dict[str, str]] = None,
797
+ config: Optional[Config] = None,
798
+ ) -> Dict[str, Any]:
799
+ """
800
+ Make an async DELETE request.
801
+
802
+ Args:
803
+ path: API path (may include query string)
804
+ query: Query parameters to add/merge
805
+ headers: Additional headers
806
+
807
+ Returns:
808
+ Response body as dictionary
809
+
810
+ Raises:
811
+ AgentRunClientError: If the request fails
812
+ """
813
+ return self._make_request(
814
+ "DELETE",
815
+ self.with_path(path, query=query),
816
+ headers=headers,
817
+ config=config,
818
+ )
819
+
820
+ async def post_file_async(
821
+ self,
822
+ path: str,
823
+ local_file_path: str,
824
+ target_file_path: str,
825
+ form_data: Optional[Dict[str, Any]] = None,
826
+ query: Optional[Dict[str, Any]] = None,
827
+ headers: Optional[Dict[str, str]] = None,
828
+ config: Optional[Config] = None,
829
+ ) -> Dict[str, Any]:
830
+ """
831
+ Asynchronously upload a file using multipart/form-data (POST request).
832
+
833
+ Args:
834
+ path: API path (may include query string)
835
+ local_file_path: Local file path to upload
836
+ target_file_path: Target file path on the server
837
+ form_data: Additional form data fields
838
+ query: Query parameters to add/merge
839
+ headers: Additional headers
840
+
841
+ Returns:
842
+ Response body as dictionary
843
+
844
+ Raises:
845
+ AgentRunClientError: If the request fails
846
+
847
+ Examples:
848
+ >>> await client.post_file_async("/files", local_file_path="/local/data.csv", target_file_path="/remote/data.csv")
849
+ >>> await client.post_file_async("/files", local_file_path="/local/data.csv", target_file_path="/remote/input.csv")
850
+ """
851
+ import os
852
+
853
+ filename = os.path.basename(local_file_path)
854
+
855
+ url = self.with_path(path, query=query)
856
+ req_headers = self.config.get_headers()
857
+ req_headers.update(headers or {})
858
+
859
+ try:
860
+ with open(local_file_path, "rb") as f:
861
+ file_content = f.read()
862
+ files = {"file": (filename, file_content)}
863
+ data = form_data or {}
864
+ data["path"] = target_file_path
865
+
866
+ async with httpx.AsyncClient(
867
+ timeout=self.config.get_timeout()
868
+ ) as client:
869
+ response = await client.post(
870
+ url, files=files, data=data, headers=req_headers
871
+ )
872
+ response.raise_for_status()
873
+ return response.json()
874
+ except httpx.HTTPStatusError as e:
875
+ raise ClientError(
876
+ status_code=e.response.status_code, message=e.response.text
877
+ ) from e
878
+
879
+ def post_file(
880
+ self,
881
+ path: str,
882
+ local_file_path: str,
883
+ target_file_path: str,
884
+ form_data: Optional[Dict[str, Any]] = None,
885
+ query: Optional[Dict[str, Any]] = None,
886
+ headers: Optional[Dict[str, str]] = None,
887
+ config: Optional[Config] = None,
888
+ ) -> Dict[str, Any]:
889
+ """
890
+ Synchronously upload a file using multipart/form-data (POST request).
891
+
892
+ Args:
893
+ path: API path (may include query string)
894
+ local_file_path: Local file path to upload
895
+ target_file_path: Target file path on the server
896
+ form_data: Additional form data fields
897
+ query: Query parameters to add/merge
898
+ headers: Additional headers
899
+
900
+ Returns:
901
+ Response body as dictionary
902
+
903
+ Raises:
904
+ AgentRunClientError: If the request fails
905
+
906
+ Examples:
907
+ >>> client.post_file("/files", local_file_path="/local/data.csv", target_file_path="/remote/data.csv")
908
+ >>> client.post_file("/files", local_file_path="/local/data.csv", target_file_path="/remote/input.csv")
909
+ """
910
+ import os
911
+
912
+ filename = os.path.basename(local_file_path)
913
+
914
+ url = self.with_path(path, query=query)
915
+ req_headers = self.config.get_headers()
916
+ req_headers.update(headers or {})
917
+
918
+ try:
919
+ with open(local_file_path, "rb") as f:
920
+ file_content = f.read()
921
+ files = {"file": (filename, file_content)}
922
+ data = form_data or {}
923
+ data["path"] = target_file_path
924
+
925
+ with httpx.Client(timeout=self.config.get_timeout()) as client:
926
+ response = client.post(
927
+ url, files=files, data=data, headers=req_headers
928
+ )
929
+ response.raise_for_status()
930
+ return response.json()
931
+ except httpx.HTTPStatusError as e:
932
+ raise ClientError(
933
+ status_code=e.response.status_code, message=e.response.text
934
+ ) from e
935
+
936
+ async def get_file_async(
937
+ self,
938
+ path: str,
939
+ save_path: str,
940
+ query: Optional[Dict[str, Any]] = None,
941
+ headers: Optional[Dict[str, str]] = None,
942
+ config: Optional[Config] = None,
943
+ ) -> Dict[str, Any]:
944
+ """
945
+ Asynchronously download a file and save it to local path (GET request).
946
+
947
+ Args:
948
+ path: API path (may include query string)
949
+ save_path: Local file path to save the downloaded file
950
+ query: Query parameters to add/merge
951
+ headers: Additional headers
952
+
953
+ Returns:
954
+ Dictionary with 'saved_path' and 'size' keys
955
+
956
+ Raises:
957
+ AgentRunClientError: If the request fails
958
+
959
+ Examples:
960
+ >>> await client.get_file_async("/files", save_path="/local/data.csv", query={"path": "/remote/file.csv"})
961
+ """
962
+ url = self.with_path(path, query=query)
963
+ req_headers = self.config.get_headers()
964
+ req_headers.update(headers or {})
965
+
966
+ try:
967
+ async with httpx.AsyncClient(
968
+ timeout=self.config.get_timeout()
969
+ ) as client:
970
+ response = await client.get(url, headers=req_headers)
971
+ response.raise_for_status()
972
+
973
+ with open(save_path, "wb") as f:
974
+ f.write(response.content)
975
+
976
+ return {"saved_path": save_path, "size": len(response.content)}
977
+ except httpx.HTTPStatusError as e:
978
+ raise ClientError(
979
+ status_code=e.response.status_code, message=e.response.text
980
+ ) from e
981
+
982
+ def get_file(
983
+ self,
984
+ path: str,
985
+ save_path: str,
986
+ query: Optional[Dict[str, Any]] = None,
987
+ headers: Optional[Dict[str, str]] = None,
988
+ config: Optional[Config] = None,
989
+ ) -> Dict[str, Any]:
990
+ """
991
+ Synchronously download a file and save it to local path (GET request).
992
+
993
+ Args:
994
+ path: API path (may include query string)
995
+ save_path: Local file path to save the downloaded file
996
+ query: Query parameters to add/merge
997
+ headers: Additional headers
998
+
999
+ Returns:
1000
+ Dictionary with 'saved_path' and 'size' keys
1001
+
1002
+ Raises:
1003
+ AgentRunClientError: If the request fails
1004
+
1005
+ Examples:
1006
+ >>> client.get_file("/files", save_path="/local/data.csv", query={"path": "/remote/file.csv"})
1007
+ """
1008
+ url = self.with_path(path, query=query)
1009
+ req_headers = self.config.get_headers()
1010
+ req_headers.update(headers or {})
1011
+
1012
+ try:
1013
+ with httpx.Client(timeout=self.config.get_timeout()) as client:
1014
+ response = client.get(url, headers=req_headers)
1015
+ response.raise_for_status()
1016
+
1017
+ with open(save_path, "wb") as f:
1018
+ f.write(response.content)
1019
+
1020
+ return {"saved_path": save_path, "size": len(response.content)}
1021
+ except httpx.HTTPStatusError as e:
1022
+ raise ClientError(
1023
+ status_code=e.response.status_code, message=e.response.text
1024
+ ) from e
1025
+
1026
+ async def get_video_async(
1027
+ self,
1028
+ path: str,
1029
+ save_path: str,
1030
+ query: Optional[Dict[str, Any]] = None,
1031
+ headers: Optional[Dict[str, str]] = None,
1032
+ config: Optional[Config] = None,
1033
+ ) -> Dict[str, Any]:
1034
+ """
1035
+ Asynchronously download a video file and save it to local path (GET request).
1036
+
1037
+ Args:
1038
+ path: API path (may include query string)
1039
+ save_path: Local file path to save the downloaded video file (.mkv)
1040
+ query: Query parameters to add/merge
1041
+ headers: Additional headers
1042
+
1043
+ Returns:
1044
+ Dictionary with 'saved_path' and 'size' keys
1045
+
1046
+ Raises:
1047
+ AgentRunClientError: If the request fails
1048
+
1049
+ Examples:
1050
+ >>> await client.get_video_async("/videos", save_path="/local/video.mkv", query={"path": "/remote/video.mp4"})
1051
+ """
1052
+ url = self.with_path(path, query=query)
1053
+ req_headers = self.config.get_headers()
1054
+ req_headers.update(headers or {})
1055
+ # Apply authentication (may modify URL, headers, and query)
1056
+ cfg = Config.with_configs(self.config, config)
1057
+ url, req_headers, query = self.auth(url, req_headers, query, config=cfg)
1058
+
1059
+ try:
1060
+ async with httpx.AsyncClient(
1061
+ timeout=self.config.get_timeout()
1062
+ ) as client:
1063
+ response = await client.get(url, headers=req_headers)
1064
+ response.raise_for_status()
1065
+
1066
+ with open(save_path, "wb") as f:
1067
+ f.write(response.content)
1068
+
1069
+ return {"saved_path": save_path, "size": len(response.content)}
1070
+ except httpx.HTTPStatusError as e:
1071
+ raise ClientError(
1072
+ status_code=e.response.status_code, message=e.response.text
1073
+ ) from e
1074
+
1075
+ def get_video(
1076
+ self,
1077
+ path: str,
1078
+ save_path: str,
1079
+ query: Optional[Dict[str, Any]] = None,
1080
+ headers: Optional[Dict[str, str]] = None,
1081
+ config: Optional[Config] = None,
1082
+ ) -> Dict[str, Any]:
1083
+ """
1084
+ Synchronously download a video file and save it to local path (GET request).
1085
+
1086
+ Args:
1087
+ path: API path (may include query string)
1088
+ save_path: Local file path to save the downloaded video file (.mkv)
1089
+ query: Query parameters to add/merge
1090
+ headers: Additional headers
1091
+
1092
+ Returns:
1093
+ Dictionary with 'saved_path' and 'size' keys
1094
+
1095
+ Raises:
1096
+ AgentRunClientError: If the request fails
1097
+
1098
+ Examples:
1099
+ >>> client.get_video("/videos", save_path="/local/video.mkv", query={"path": "/remote/video.mp4"})
1100
+ """
1101
+ url = self.with_path(path, query=query)
1102
+ req_headers = self.config.get_headers()
1103
+ req_headers.update(headers or {})
1104
+ # Apply authentication (may modify URL, headers, and query)
1105
+ cfg = Config.with_configs(self.config, config)
1106
+ url, req_headers, query = self.auth(url, req_headers, query, config=cfg)
1107
+
1108
+ try:
1109
+ with httpx.Client(timeout=self.config.get_timeout()) as client:
1110
+ response = client.get(url, headers=req_headers)
1111
+ response.raise_for_status()
1112
+
1113
+ with open(save_path, "wb") as f:
1114
+ f.write(response.content)
1115
+
1116
+ return {"saved_path": save_path, "size": len(response.content)}
1117
+ except httpx.HTTPStatusError as e:
1118
+ raise ClientError(
1119
+ status_code=e.response.status_code, message=e.response.text
1120
+ ) from e