agbcloud-sdk 0.1.0__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 (58) hide show
  1. agb/__init__.py +7 -0
  2. agb/agb.py +215 -0
  3. agb/api/__init__.py +11 -0
  4. agb/api/base_service.py +122 -0
  5. agb/api/client.py +257 -0
  6. agb/api/http_client.py +660 -0
  7. agb/api/models/__init__.py +36 -0
  8. agb/api/models/call_mcp_tool_request.py +51 -0
  9. agb/api/models/call_mcp_tool_response.py +149 -0
  10. agb/api/models/create_session_request.py +32 -0
  11. agb/api/models/create_session_response.py +165 -0
  12. agb/api/models/get_link_request.py +78 -0
  13. agb/api/models/get_link_response.py +128 -0
  14. agb/api/models/get_mcp_resource_request.py +27 -0
  15. agb/api/models/get_mcp_resource_response.py +193 -0
  16. agb/api/models/init_browser_request.py +76 -0
  17. agb/api/models/init_browser_response.py +133 -0
  18. agb/api/models/list_mcp_tools_request.py +27 -0
  19. agb/api/models/list_mcp_tools_response.py +113 -0
  20. agb/api/models/release_session_request.py +27 -0
  21. agb/api/models/release_session_response.py +102 -0
  22. agb/config.py +54 -0
  23. agb/exceptions.py +65 -0
  24. agb/model/__init__.py +15 -0
  25. agb/model/response.py +137 -0
  26. agb/modules/__init__.py +35 -0
  27. agb/modules/browser/__init__.py +21 -0
  28. agb/modules/browser/browser.py +419 -0
  29. agb/modules/browser/browser_agent.py +470 -0
  30. agb/modules/browser/eval/local_page_agent.py +203 -0
  31. agb/modules/browser/eval/page_agent.py +351 -0
  32. agb/modules/browser/eval/page_tasks/allrecipes.py +85 -0
  33. agb/modules/browser/eval/page_tasks/apple.py +40 -0
  34. agb/modules/browser/eval/page_tasks/arxiv.py +107 -0
  35. agb/modules/browser/eval/page_tasks/combination_sauce.py +62 -0
  36. agb/modules/browser/eval/page_tasks/extract_aigrant_companies.py +73 -0
  37. agb/modules/browser/eval/page_tasks/extract_apartments.py +60 -0
  38. agb/modules/browser/eval/page_tasks/extract_area_codes.py +205 -0
  39. agb/modules/browser/eval/page_tasks/imdb_movie_details.py +78 -0
  40. agb/modules/browser/eval/page_tasks/nonsense_action.py +28 -0
  41. agb/modules/browser/eval/page_tasks/observe_amazon_add_to_cart.py +44 -0
  42. agb/modules/browser/eval/page_tasks/observe_iframes1.py +74 -0
  43. agb/modules/browser/eval/page_tasks/simple_google_search.py +16 -0
  44. agb/modules/browser/eval/page_tasks/tegner_shop.py +129 -0
  45. agb/modules/browser/eval/page_tasks/vanta_h.py +23 -0
  46. agb/modules/browser/eval/run_page_evals.py +192 -0
  47. agb/modules/browser/eval/service.py +1879 -0
  48. agb/modules/code.py +90 -0
  49. agb/modules/command.py +70 -0
  50. agb/modules/file_system.py +788 -0
  51. agb/modules/oss.py +331 -0
  52. agb/session.py +205 -0
  53. agb/session_params.py +23 -0
  54. agbcloud_sdk-0.1.0.dist-info/METADATA +131 -0
  55. agbcloud_sdk-0.1.0.dist-info/RECORD +58 -0
  56. agbcloud_sdk-0.1.0.dist-info/WHEEL +5 -0
  57. agbcloud_sdk-0.1.0.dist-info/licenses/LICENSE +201 -0
  58. agbcloud_sdk-0.1.0.dist-info/top_level.txt +1 -0
agb/__init__.py ADDED
@@ -0,0 +1,7 @@
1
+ from .agb import AGB
2
+ from .session import Session
3
+ from .session_params import CreateSessionParams
4
+ from .api.http_client import HTTPClient
5
+ from .api.client import Client
6
+
7
+ __all__ = ["AGB", "Session", "CreateSessionParams", "HTTPClient", "Client"]
agb/agb.py ADDED
@@ -0,0 +1,215 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ AGB represents the main client for interacting with the AGB cloud runtime
4
+ environment.
5
+ """
6
+
7
+ import json
8
+ import os
9
+ from typing import Dict, List, Optional, Union
10
+ from threading import Lock
11
+
12
+ from agb.api.client import Client as mcp_client
13
+ from agb.api.models import (
14
+ CreateSessionRequest,
15
+ ReleaseSessionRequest,
16
+ CreateSessionResponse,
17
+ )
18
+ from agb.config import load_config, Config
19
+ from agb.session import Session, BaseSession
20
+ from agb.session_params import CreateSessionParams
21
+ from agb.model.response import SessionResult, DeleteResult
22
+
23
+
24
+ class AGB:
25
+ """
26
+ AGB represents the main client for interacting with the AGB cloud runtime
27
+ environment.
28
+ """
29
+
30
+ def __init__(self, api_key: str = "", cfg: Optional[Config] = None):
31
+ """
32
+ Initialize the AGB client.
33
+
34
+ Args:
35
+ api_key (str): API key for authentication. If not provided, it will be
36
+ loaded from the AGB_API_KEY environment variable.
37
+ cfg (Optional[Config]): Configuration object. If not provided, default
38
+ configuration will be used.
39
+ """
40
+ if not api_key:
41
+ api_key = os.getenv("AGB_API_KEY")
42
+ if not api_key:
43
+ raise ValueError(
44
+ "API key is required. Provide it as a parameter or set the "
45
+ "AGB_API_KEY environment variable"
46
+ )
47
+
48
+ # Load configuration
49
+ self.config = load_config(cfg)
50
+
51
+ self.api_key = api_key
52
+ self.endpoint = self.config.endpoint
53
+ self.timeout_ms = self.config.timeout_ms
54
+
55
+ # Initialize the HTTP API client with the complete config
56
+ self.client = mcp_client(self.config)
57
+ self._sessions = {}
58
+ self._lock = Lock()
59
+
60
+
61
+ def create(self, params: Optional[CreateSessionParams] = None) -> SessionResult:
62
+ """
63
+ Create a new session in the AGB cloud environment.
64
+
65
+ Args:
66
+ params (Optional[CreateSessionParams], optional): Parameters for
67
+ creating the session.Defaults to None.
68
+
69
+ Returns:
70
+ SessionResult: Result containing the created session and request ID.
71
+ """
72
+ try:
73
+ if params is None:
74
+ params = CreateSessionParams()
75
+
76
+ request = CreateSessionRequest(authorization=f"Bearer {self.api_key}")
77
+
78
+ if params.image_id:
79
+ request.image_id = params.image_id
80
+
81
+ response : CreateSessionResponse = self.client.create_mcp_session(request)
82
+
83
+ # Check if response is empty
84
+ if response is None:
85
+ return SessionResult(
86
+ request_id="",
87
+ success=False,
88
+ error_message="OpenAPI client returned None response",
89
+ )
90
+
91
+ try:
92
+ print("Response body:")
93
+ print(response.to_dict())
94
+ except Exception:
95
+ print(f"Response: {response}")
96
+
97
+ # Extract request ID
98
+ request_id = getattr(response, 'request_id', '') or ''
99
+
100
+ # Check if the session creation was successful
101
+ if response.data and response.data.success is False:
102
+ error_msg = response.data.err_msg
103
+ if error_msg is None:
104
+ error_msg = "Unknown error"
105
+ return SessionResult(
106
+ request_id=request_id,
107
+ success=False,
108
+ error_message=error_msg,
109
+ )
110
+
111
+ session_id = response.get_session_id()
112
+ if not session_id:
113
+ return SessionResult(
114
+ request_id=request_id,
115
+ success=False,
116
+ error_message=response.get_error_message(),
117
+ )
118
+
119
+ # ResourceUrl is optional in CreateMcpSession response
120
+ resource_url = response.get_resource_url()
121
+
122
+ print("session_id =", session_id)
123
+ print("resource_url =", resource_url)
124
+
125
+ # Create Session object
126
+ session = Session(self, session_id)
127
+ if resource_url is not None:
128
+ session.resource_url = resource_url
129
+
130
+ # Store image_id used for this session
131
+ session.image_id = params.image_id
132
+
133
+ with self._lock:
134
+ self._sessions[session_id] = session
135
+
136
+ # Return SessionResult with request ID
137
+ return SessionResult(request_id=request_id, success=True, session=session)
138
+
139
+ except Exception as e:
140
+ print("Error calling create_mcp_session:", e)
141
+ return SessionResult(
142
+ request_id="",
143
+ success=False,
144
+ error_message=f"Failed to create session: {e}",
145
+ )
146
+
147
+ def list(self) -> List[BaseSession]:
148
+ """
149
+ List all available sessions.
150
+
151
+ Returns:
152
+ List[BaseSession]: A list of all available sessions.
153
+ """
154
+ with self._lock:
155
+ return list(self._sessions.values())
156
+
157
+
158
+ def delete(self, session: Session) -> DeleteResult:
159
+ """
160
+ Delete a session by session object.
161
+
162
+ Args:
163
+ session (Session): The session to delete.
164
+
165
+ Returns:
166
+ DeleteResult: Result indicating success or failure and request ID.
167
+ """
168
+ try:
169
+ # Create request to release the session
170
+ request = ReleaseSessionRequest(
171
+ authorization=f"Bearer {self.api_key}",
172
+ session_id=session.session_id,
173
+ )
174
+
175
+ # Make the API call
176
+ response = self.client.release_mcp_session(request)
177
+
178
+ # Check if response is empty
179
+ if response is None:
180
+ return DeleteResult(
181
+ request_id="",
182
+ success=False,
183
+ error_message="OpenAPI client returned None response",
184
+ )
185
+
186
+ # Check response type, if it's ReleaseSessionResponse, use new parsing method
187
+ if hasattr(response, 'is_successful'):
188
+ # This is a ReleaseSessionResponse object
189
+ if response.is_successful():
190
+ # Remove from local cache
191
+ with self._lock:
192
+ self._sessions.pop(session.session_id, None)
193
+
194
+ return DeleteResult(request_id=response.request_id or "", success=True)
195
+ else:
196
+ error_msg = response.get_error_message() or "Failed to delete session"
197
+ return DeleteResult(
198
+ request_id=response.request_id or "",
199
+ success=False,
200
+ error_message=error_msg,
201
+ )
202
+ else:
203
+ return DeleteResult(
204
+ request_id=response.request_id or "",
205
+ success=False,
206
+ error_message="Failed to delete session",
207
+ )
208
+
209
+ except Exception as e:
210
+ print("Error calling release_mcp_session:", e)
211
+ # In case of error, return failure result with error message
212
+ return DeleteResult(
213
+ success=False,
214
+ error_message=f"Failed to delete session {session.session_id}: {e}",
215
+ )
agb/api/__init__.py ADDED
@@ -0,0 +1,11 @@
1
+ from .http_client import HTTPClient
2
+ from .base_service import BaseService
3
+ from .client import Client
4
+
5
+ __version__ = "1.0.0"
6
+
7
+ __all__ = [
8
+ "HTTPClient",
9
+ "BaseService",
10
+ "Client",
11
+ ]
@@ -0,0 +1,122 @@
1
+ import json
2
+ import requests
3
+ import time
4
+ import random
5
+ import string
6
+ from typing import Any, Dict
7
+
8
+ from agb.api.models import CallMcpToolRequest
9
+ from agb.exceptions import AGBError
10
+ from agb.model import OperationResult
11
+
12
+
13
+ class BaseService:
14
+ """
15
+ Base service class that provides common functionality for all service classes.
16
+ This class implements the common methods for calling MCP tools and parsing
17
+ responses.
18
+ """
19
+
20
+ def __init__(self, session):
21
+ """
22
+ Initialize a BaseService object.
23
+
24
+ Args:
25
+ session: The Session instance that this service belongs to.
26
+ """
27
+ self.session = session
28
+
29
+ def _handle_error(self, e):
30
+ """
31
+ Handle and convert exceptions. This method should be overridden by subclasses
32
+ to provide specific error handling.
33
+
34
+ Args:
35
+ e (Exception): The exception to handle.
36
+
37
+ Returns:
38
+ Exception: The handled exception.
39
+ """
40
+ return e
41
+
42
+ def _call_mcp_tool(self, name: str, args: Dict[str, Any], read_timeout: int = None, connect_timeout: int = None) -> OperationResult:
43
+ """
44
+ Internal helper to call MCP tool and handle errors.
45
+
46
+ Args:
47
+ name (str): The name of the tool to call.
48
+ args (Dict[str, Any]): The arguments to pass to the tool.
49
+
50
+ Returns:
51
+ OperationResult: The response from the tool with request ID.
52
+ """
53
+ try:
54
+ args_json = json.dumps(args, ensure_ascii=False)
55
+
56
+ # use traditional API call
57
+ request = CallMcpToolRequest(
58
+ authorization=f"Bearer {self.session.get_api_key()}",
59
+ session_id=self.session.get_session_id(),
60
+ name=name,
61
+ args=args_json,
62
+ )
63
+ response = self.session.get_client().call_mcp_tool(request, read_timeout=read_timeout, connect_timeout=connect_timeout)
64
+
65
+ # Check if response is empty
66
+ if response is None:
67
+ return OperationResult(
68
+ request_id="",
69
+ success=False,
70
+ error_message="OpenAPI client returned None response",
71
+ )
72
+
73
+ request_id = response.request_id or ""
74
+
75
+ # Check response type, if it's CallMcpToolResponse, use new parsing method
76
+ if hasattr(response, 'is_tool_successful'):
77
+ # This is a CallMcpToolResponse object
78
+ try:
79
+ print("Response body:")
80
+ print(json.dumps(response.json_data, ensure_ascii=False, indent=2))
81
+ except Exception:
82
+ print(f"Response: {response}")
83
+
84
+ if response.is_tool_successful():
85
+ # Tool execution successful
86
+ result = response.get_tool_result()
87
+ return OperationResult(request_id=request_id, success=True, data=result)
88
+ else:
89
+ # Tool execution failed
90
+ error_msg = response.get_error_message() or "Tool execution failed"
91
+ return OperationResult(
92
+ request_id=request_id,
93
+ success=False,
94
+ error_message=error_msg,
95
+ )
96
+ else:
97
+ # This is the original OpenAPI response object, use existing parsing method
98
+ # Here you can add existing parsing logic if needed
99
+ error_msg = "Unsupported response type"
100
+ return OperationResult(
101
+ request_id=request_id,
102
+ success=False,
103
+ error_message=error_msg,
104
+ )
105
+
106
+
107
+ except AGBError as e:
108
+ handled_error = self._handle_error(e)
109
+ request_id = "" if "request_id" not in locals() else request_id
110
+ return OperationResult(
111
+ request_id=request_id,
112
+ success=False,
113
+ error_message=str(handled_error),
114
+ )
115
+ except Exception as e:
116
+ handled_error = self._handle_error(e)
117
+ request_id = "" if "request_id" not in locals() else request_id
118
+ return OperationResult(
119
+ request_id=request_id,
120
+ success=False,
121
+ error_message=f"Failed to call MCP tool {name}: {handled_error}",
122
+ )
agb/api/client.py ADDED
@@ -0,0 +1,257 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ AGB API client implementation using HTTP client
4
+ """
5
+
6
+ from typing import Dict, Optional, Union, List, Any
7
+ from agb.api.models import (
8
+ CreateSessionResponse,
9
+ CallMcpToolResponse,
10
+ ListMcpToolsResponse,
11
+ GetMcpResourceResponse,
12
+ ReleaseSessionResponse,
13
+ CreateSessionRequest,
14
+ ReleaseSessionRequest,
15
+ CallMcpToolRequest,
16
+ ListMcpToolsRequest,
17
+ GetMcpResourceRequest,
18
+ InitBrowserRequest,
19
+ InitBrowserResponse,
20
+ GetLinkRequest,
21
+ GetLinkResponse,
22
+ )
23
+ from .http_client import HTTPClient
24
+ import aiohttp
25
+
26
+ class Client:
27
+ """
28
+ AGB API client that uses HTTP client
29
+ """
30
+
31
+ def __init__(self, config=None):
32
+ """
33
+ Initialize the client
34
+
35
+ Args:
36
+ config: Configuration object for HTTP client
37
+ """
38
+ self.config = config
39
+ self._http_client = None
40
+
41
+ def _get_http_client(self, api_key: str) -> HTTPClient:
42
+ """
43
+ Get HTTP client instance, creating a new one for each request
44
+
45
+ Args:
46
+ api_key: API key for authentication
47
+
48
+ Returns:
49
+ HTTPClient: HTTP client instance
50
+ """
51
+ # Always create a new HTTP client for each request
52
+ return HTTPClient(api_key=api_key, cfg=self.config)
53
+
54
+ def create_mcp_session(self, request: CreateSessionRequest) -> CreateSessionResponse:
55
+ """
56
+ Create MCP session using HTTP client
57
+ """
58
+ # Extract API key from authorization header
59
+ if not request.authorization:
60
+ raise ValueError("authorization is required")
61
+
62
+ # Get HTTP client and make request directly with the input request
63
+ http_client = self._get_http_client(request.authorization)
64
+
65
+ try:
66
+ response = http_client.create_session(request)
67
+ return response
68
+ finally:
69
+ # Always close the HTTP client to release resources
70
+ http_client.close()
71
+
72
+ def release_mcp_session(self, request: ReleaseSessionRequest) -> ReleaseSessionResponse:
73
+ """
74
+ Release MCP session using HTTP client
75
+ """
76
+ if not request.session_id:
77
+ raise ValueError("session_id is required")
78
+
79
+ if not request.authorization:
80
+ raise ValueError("authorization is required")
81
+
82
+ # Get HTTP client and make request directly with the input request
83
+ http_client = self._get_http_client(request.authorization)
84
+
85
+ try:
86
+ response = http_client.release_session(request)
87
+ return response
88
+ finally:
89
+ # Always close the HTTP client to release resources
90
+ http_client.close()
91
+
92
+ def call_mcp_tool(self, request: CallMcpToolRequest, read_timeout: int = None, connect_timeout: int = None) -> CallMcpToolResponse:
93
+ """
94
+ Call MCP tool using HTTP client
95
+ """
96
+ if not request.authorization:
97
+ raise ValueError("authorization is required")
98
+
99
+ # Get HTTP client and make request directly with the input request
100
+ http_client = self._get_http_client(request.authorization)
101
+
102
+ try:
103
+ response = http_client.call_mcp_tool(request, read_timeout=read_timeout, connect_timeout=connect_timeout)
104
+ return response
105
+ finally:
106
+ # Always close the HTTP client to release resources
107
+ http_client.close()
108
+
109
+ def list_mcp_tools(self, request: ListMcpToolsRequest) -> ListMcpToolsResponse:
110
+ """
111
+ List MCP tools using HTTP client
112
+ """
113
+ if not request.authorization:
114
+ raise ValueError("authorization is required")
115
+
116
+ # Get HTTP client and make request directly with the input request
117
+ http_client = self._get_http_client(request.authorization)
118
+
119
+ try:
120
+ response = http_client.list_mcp_tools(request)
121
+ return response
122
+ finally:
123
+ # Always close the HTTP client to release resources
124
+ http_client.close()
125
+
126
+ def get_mcp_resource(self, request: GetMcpResourceRequest) -> GetMcpResourceResponse:
127
+ """
128
+ Get MCP resource using HTTP client
129
+ """
130
+ if not request.session_id:
131
+ raise ValueError("session_id is required")
132
+
133
+ if not request.authorization:
134
+ raise ValueError("authorization is required")
135
+
136
+ # Get HTTP client and make request directly with the input request
137
+ http_client = self._get_http_client(request.authorization)
138
+
139
+ try:
140
+ response = http_client.get_mcp_resource(request)
141
+ return response
142
+ finally:
143
+ # Always close the HTTP client to release resources
144
+ http_client.close()
145
+
146
+ def init_browser(self, request: InitBrowserRequest) -> InitBrowserResponse:
147
+ """
148
+ Initialize browser using HTTP client
149
+ """
150
+ if not request.authorization:
151
+ raise ValueError("authorization is required")
152
+
153
+ # Get HTTP client and make request directly with the input request
154
+ http_client = self._get_http_client(request.authorization)
155
+
156
+ try:
157
+ response = http_client.init_browser(request)
158
+ return response
159
+ finally:
160
+ # Always close the HTTP client to release resources
161
+ http_client.close()
162
+
163
+
164
+ async def init_browser_async(
165
+ self,
166
+ request: InitBrowserRequest,
167
+ ) -> InitBrowserResponse:
168
+ """
169
+ Async version of init_browser using HTTP client
170
+ """
171
+ if not request.authorization:
172
+ raise ValueError("authorization is required")
173
+
174
+ # Get HTTP client and make async request
175
+ http_client = self._get_http_client(request.authorization)
176
+
177
+ try:
178
+ response = await http_client.init_browser_async(request)
179
+ return response
180
+ finally:
181
+ # Always close the HTTP client to release resources
182
+ http_client.close()
183
+
184
+ def close(self):
185
+ """Close HTTP client and clean up resources"""
186
+ # No need to manage long-lived HTTP client anymore
187
+ # Each request creates a new client that gets cleaned up automatically
188
+ pass
189
+
190
+ async def call_api_async_with_requests(url, method="GET", headers=None, params=None, data=None, json=None, timeout=30):
191
+ """
192
+ 使用 aiohttp 实现异步 HTTP 请求,模拟 requests 的用法。
193
+ """
194
+ async with aiohttp.ClientSession() as session:
195
+ req_method = getattr(session, method.lower())
196
+ async with req_method(
197
+ url,
198
+ headers=headers,
199
+ params=params,
200
+ data=data,
201
+ json=json,
202
+ timeout=timeout
203
+ ) as resp:
204
+ resp_data = await resp.text()
205
+ # 你可以根据需要返回 resp.json() 或 resp.read()
206
+ return {
207
+ "status_code": resp.status,
208
+ "headers": dict(resp.headers),
209
+ "body": resp_data
210
+ }
211
+
212
+
213
+ def get_link(
214
+ self,
215
+ request: GetLinkRequest,
216
+ ) -> GetLinkResponse:
217
+ """
218
+ Get session link using HTTP client
219
+ """
220
+ if not request.authorization:
221
+ raise ValueError("authorization is required")
222
+
223
+ if not request.session_id:
224
+ raise ValueError("session_id is required")
225
+
226
+ # Get HTTP client and make request directly with the input request
227
+ http_client = self._get_http_client(request.authorization)
228
+
229
+ try:
230
+ response = http_client.get_link(request)
231
+ return response
232
+ finally:
233
+ # Always close the HTTP client to release resources
234
+ http_client.close()
235
+
236
+ async def get_link_async(
237
+ self,
238
+ request: GetLinkRequest,
239
+ ) -> GetLinkResponse:
240
+ """
241
+ Async version of get_link using HTTP client
242
+ """
243
+ if not request.authorization:
244
+ raise ValueError("authorization is required")
245
+
246
+ if not request.session_id:
247
+ raise ValueError("session_id is required")
248
+
249
+ # Get HTTP client and make async request
250
+ http_client = self._get_http_client(request.authorization)
251
+
252
+ try:
253
+ response = await http_client.get_link_async(request)
254
+ return response
255
+ finally:
256
+ # Always close the HTTP client to release resources
257
+ http_client.close()