aip-agents-binary 0.5.22__py3-none-macosx_13_0_arm64.whl → 0.5.23__py3-none-macosx_13_0_arm64.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.
@@ -6,11 +6,12 @@ Authors:
6
6
 
7
7
  import asyncio
8
8
 
9
- from aip_agents.agent import LangGraphAgent
10
- from aip_agents.tools import GL_CONNECTORS_AUTOMATED_TOOLS
11
9
  from dotenv import load_dotenv
12
10
  from langchain_openai import ChatOpenAI
13
11
 
12
+ from aip_agents.agent import LangGraphAgent
13
+ from aip_agents.tools import GL_CONNECTORS_AUTOMATED_TOOLS
14
+
14
15
  load_dotenv(override=True)
15
16
 
16
17
 
@@ -20,13 +20,14 @@ import threading
20
20
  import time
21
21
 
22
22
  import uvicorn
23
- from aip_agents.examples.hello_world_langgraph import langgraph_example
24
- from aip_agents.sentry import setup_telemetry
25
- from aip_agents.utils.logger import get_logger
26
23
  from fastapi import FastAPI
27
24
  from fastapi.responses import JSONResponse
28
25
  from fastapi.staticfiles import StaticFiles
29
26
 
27
+ from aip_agents.examples.hello_world_langgraph import langgraph_example
28
+ from aip_agents.sentry import setup_telemetry
29
+ from aip_agents.utils.logger import get_logger
30
+
30
31
  logger = get_logger(__name__)
31
32
 
32
33
  BASE_URL = "http://localhost:8585"
@@ -49,9 +50,7 @@ def fetch_endpoints():
49
50
  # Because using request.get() will trigger sentry, but the ep name is not saved.
50
51
  # Instead of GET /langgraph-hello-world, sentry will save it as GET
51
52
  url = f"http://127.0.0.1:8585{ep}"
52
- result = subprocess.run(
53
- ["curl", "-i", url], capture_output=True, text=True, check=False
54
- )
53
+ result = subprocess.run(["curl", "-i", url], capture_output=True, text=True, check=False)
55
54
  logger.info(result.stdout)
56
55
  except Exception as e:
57
56
  logger.error(f"Error fetching {ep}: {e}")
@@ -45,6 +45,19 @@ DEFAULT_TIMEOUT: float = 30.0
45
45
  """Default connection timeout in seconds."""
46
46
 
47
47
 
48
+ def _sanitize_headers(config: MCPConfiguration) -> dict[str, str]:
49
+ """Remove headers with None values to avoid invalid HTTP headers.
50
+
51
+ Args:
52
+ config (MCPConfiguration): Transport configuration containing optional headers.
53
+
54
+ Returns:
55
+ dict[str, str]: Filtered headers with None values removed.
56
+ """
57
+ headers = config.get("headers", {}) or {}
58
+ return {key: value for key, value in headers.items() if value is not None}
59
+
60
+
48
61
  class TransportType(StrEnum):
49
62
  """Enum for supported MCP transport types."""
50
63
 
@@ -115,7 +128,7 @@ class SSETransport(Transport):
115
128
 
116
129
  url = f"{base_url}/sse" if not base_url.endswith("/sse") else base_url
117
130
  timeout = self.config.get("timeout", DEFAULT_TIMEOUT)
118
- headers = self.config.get("headers", {})
131
+ headers = _sanitize_headers(self.config)
119
132
  logger.debug(f"Attempting SSE connection to {url} with headers: {list(headers.keys())}")
120
133
  try:
121
134
  self.ctx = sse_client(url=url, timeout=timeout, sse_read_timeout=300.0, headers=headers)
@@ -147,7 +160,7 @@ class HTTPTransport(Transport):
147
160
 
148
161
  url = f"{base_url}/mcp" if not base_url.endswith("/mcp") else base_url
149
162
  timeout = self.config.get("timeout", DEFAULT_TIMEOUT)
150
- headers = self.config.get("headers", {})
163
+ headers = _sanitize_headers(self.config)
151
164
  logger.debug(f"Attempting streamable HTTP connection to {url} with headers: {list(headers.keys())}")
152
165
  try:
153
166
  self.ctx = streamablehttp_client(url=url, timeout=timeout, headers=headers)
@@ -7,8 +7,6 @@ Authors:
7
7
  import inspect
8
8
  import os
9
9
 
10
- from aip_agents.agent import BaseAgent, GoogleADKAgent, LangChainAgent, LangGraphAgent
11
- from aip_agents.utils.logger import get_logger
12
10
  from bosa_core.telemetry import (
13
11
  FastAPIConfig,
14
12
  OpenTelemetryConfig,
@@ -22,6 +20,9 @@ from bosa_core.telemetry.opentelemetry.instrument.functions import (
22
20
  from dotenv import load_dotenv
23
21
  from fastapi import FastAPI
24
22
 
23
+ from aip_agents.agent import BaseAgent, GoogleADKAgent, LangChainAgent, LangGraphAgent
24
+ from aip_agents.utils.logger import get_logger
25
+
25
26
  load_dotenv()
26
27
 
27
28
 
@@ -55,11 +56,7 @@ def get_all_methods(cls: type) -> list:
55
56
  for name, member in inspect.getmembers(cls):
56
57
  if name.startswith("_"):
57
58
  continue # skip dunder and private
58
- if (
59
- inspect.isfunction(member)
60
- or inspect.ismethod(member)
61
- or inspect.iscoroutinefunction(member)
62
- ):
59
+ if inspect.isfunction(member) or inspect.ismethod(member) or inspect.iscoroutinefunction(member):
63
60
  methods.append(member)
64
61
  return methods
65
62
 
@@ -114,9 +111,7 @@ def setup_sentry_with_open_telemetry(app: FastAPI) -> None:
114
111
 
115
112
  telemetry_config = TelemetryConfig(sentry_config=sentry_config)
116
113
  init_telemetry(telemetry_config)
117
- logger.info(
118
- f"Telemetry initialized with OpenTelemetry for environment: {SENTRY_ENVIRONMENT}"
119
- )
114
+ logger.info(f"Telemetry initialized with OpenTelemetry for environment: {SENTRY_ENVIRONMENT}")
120
115
  except Exception as e:
121
116
  logger.error(f"Failed to initialize telemetry with OpenTelemetry: {e}")
122
117
 
@@ -135,9 +130,7 @@ def setup_sentry_only() -> None:
135
130
 
136
131
  telemetry_config = TelemetryConfig(sentry_config=sentry_config)
137
132
  init_telemetry(telemetry_config)
138
- logger.info(
139
- f"Telemetry initialized with Sentry only for environment: {SENTRY_ENVIRONMENT}"
140
- )
133
+ logger.info(f"Telemetry initialized with Sentry only for environment: {SENTRY_ENVIRONMENT}")
141
134
  except Exception as e:
142
135
  logger.error(f"Failed to initialize telemetry with Sentry only: {e}")
143
136
 
@@ -3,8 +3,8 @@
3
3
  from importlib import import_module
4
4
  from typing import TYPE_CHECKING
5
5
 
6
- from aip_agents.tools.gl_connector_tools import GL_CONNECTORS_AUTOMATED_TOOLS, BOSA_AUTOMATED_TOOLS
7
6
  from aip_agents.tools.gl_connector import GLConnectorTool
7
+ from aip_agents.tools.gl_connector_tools import BOSA_AUTOMATED_TOOLS, GL_CONNECTORS_AUTOMATED_TOOLS
8
8
  from aip_agents.tools.time_tool import TimeTool
9
9
  from aip_agents.tools.web_search import GoogleSerperTool
10
10
  from aip_agents.utils.logger import get_logger
@@ -11,12 +11,10 @@ from http import HTTPStatus
11
11
  from typing import Any
12
12
 
13
13
  import requests
14
+ from e2b_code_interpreter import Sandbox
14
15
  from gllm_inference.schema import Attachment
15
- from gllm_tools.code_interpreter.code_sandbox.e2b_cloud_sandbox import E2BCloudSandbox
16
- from gllm_tools.code_interpreter.code_sandbox.models import (
17
- ExecutionResult,
18
- ExecutionStatus,
19
- )
16
+ from gllm_tools.code_interpreter.code_sandbox.e2b_sandbox import E2BSandbox
17
+ from gllm_tools.code_interpreter.code_sandbox.models import ExecutionResult, ExecutionStatus
20
18
  from gllm_tools.code_interpreter.code_sandbox.utils import calculate_duration_ms
21
19
 
22
20
  from aip_agents.tools.code_sandbox.constant import DATA_FILE_PATH
@@ -123,19 +121,57 @@ class SandboxFileWatcher:
123
121
  return self._created_files.copy()
124
122
 
125
123
 
126
- class MyE2BCloudSandbox(E2BCloudSandbox):
127
- """Extended E2B Cloud Sandbox with filesystem monitoring capabilities."""
124
+ class MyE2BCloudSandbox(E2BSandbox):
125
+ """Extended E2B sandbox with filesystem monitoring capabilities.
128
126
 
129
- def __init__(self, *args, **kwargs):
130
- """Initialize the sandbox with monitoring capabilities.
127
+ Use `create()` in production to build a fully initialized sandbox wrapper.
128
+ Direct construction is intentionally blocked to prevent partially initialized
129
+ instances that lack the underlying E2B sandbox clients.
130
+ """
131
+
132
+ def __init__(self, language: str = "python", *, _unsafe_allow_init: bool = False) -> None:
133
+ """Initialize the sandbox wrapper.
131
134
 
132
135
  Args:
133
- *args: Positional arguments forwarded to ``E2BCloudSandbox``.
134
- **kwargs: Keyword arguments forwarded to ``E2BCloudSandbox``.
136
+ language (str): Language to execute inside the sandbox.
137
+ _unsafe_allow_init (bool): Escape hatch for tests/mocks only.
138
+
139
+ Raises:
140
+ RuntimeError: When instantiated directly without `create()`.
135
141
  """
136
- super().__init__(*args, **kwargs)
142
+ if not _unsafe_allow_init:
143
+ raise RuntimeError("Use MyE2BCloudSandbox.create(...) to initialize a sandbox instance.")
144
+ super().__init__(language=language)
137
145
  self.file_watcher: SandboxFileWatcher | None = None
138
146
 
147
+ @classmethod
148
+ async def create(
149
+ cls,
150
+ api_key: str,
151
+ domain: str | None = None,
152
+ template: str | None = None,
153
+ language: str = "python",
154
+ additional_packages: list[str] | None = None,
155
+ **kwargs: Any,
156
+ ) -> "MyE2BCloudSandbox":
157
+ """Create a fully initialized sandbox wrapper.
158
+
159
+ This is the supported construction path for production usage. It wires
160
+ the E2B sandbox instance and its filesystem/command clients, then
161
+ installs language dependencies.
162
+ """
163
+ sandbox = Sandbox.create(api_key=api_key, domain=domain, template=template, **kwargs)
164
+
165
+ instance = cls(language=language, _unsafe_allow_init=True)
166
+ instance.sandbox = sandbox
167
+ instance.files = sandbox.files
168
+ instance.commands = sandbox.commands
169
+ instance.additional_packages = additional_packages or []
170
+
171
+ instance._install_language_dependencies()
172
+
173
+ return instance
174
+
139
175
  async def execute_code(
140
176
  self,
141
177
  code: str,
@@ -161,11 +197,10 @@ class MyE2BCloudSandbox(E2BCloudSandbox):
161
197
  Raises:
162
198
  RuntimeError: If sandbox is not initialized.
163
199
  """
164
- if not self.sandbox:
200
+ if not self.sandbox or not self.files or not self.commands:
165
201
  raise RuntimeError("Sandbox is not initialized")
166
202
 
167
203
  start_time = time.time()
168
-
169
204
  try:
170
205
  # Initialize filesystem monitoring
171
206
  self.file_watcher = SandboxFileWatcher(self.sandbox)
@@ -176,8 +211,12 @@ class MyE2BCloudSandbox(E2BCloudSandbox):
176
211
  # Pre-populate the variable `df` for direct use in the code
177
212
  if files:
178
213
  logger.info("Pre-populating the variable `df` with the data from the file.")
179
- self.sandbox.run_code(f"import pandas as pd; df = pd.read_csv('{DATA_FILE_PATH}')", timeout=timeout)
180
- execution = self.sandbox.run_code(code, timeout=timeout)
214
+ self.sandbox.run_code(
215
+ f"import pandas as pd; df = pd.read_csv('{DATA_FILE_PATH}')",
216
+ language=self.language,
217
+ timeout=timeout,
218
+ )
219
+ execution = self.sandbox.run_code(code, language=self.language, timeout=timeout)
181
220
  duration_ms = calculate_duration_ms(start_time)
182
221
  status = ExecutionStatus.ERROR if execution.error else ExecutionStatus.SUCCESS
183
222
 
@@ -191,8 +230,8 @@ class MyE2BCloudSandbox(E2BCloudSandbox):
191
230
  return ExecutionResult.create(
192
231
  status=status,
193
232
  code=code,
194
- stdout=(execution.logs.stdout[0] if execution.logs and execution.logs.stdout else ""),
195
- stderr=(execution.logs.stderr[0] if execution.logs and execution.logs.stderr else ""),
233
+ stdout=("\n".join(execution.logs.stdout) if execution.logs and execution.logs.stdout else ""),
234
+ stderr=("\n".join(execution.logs.stderr) if execution.logs and execution.logs.stderr else ""),
196
235
  error=(str(execution.error) if execution.error else ""), # Convert to string here
197
236
  duration_ms=duration_ms,
198
237
  )
@@ -218,8 +257,8 @@ class MyE2BCloudSandbox(E2BCloudSandbox):
218
257
  def download_file(self, file_path: str) -> bytes | None:
219
258
  """Download file content from the sandbox.
220
259
 
221
- Uses download_url method to get a direct URL and downloads via HTTP,
222
- which avoids the binary corruption issue with files.read().
260
+ Uses download_url when available to avoid binary corruption issues.
261
+ Falls back to the filesystem API when download_url fails or is unavailable.
223
262
 
224
263
  Args:
225
264
  file_path (str): Path to the file in the sandbox.
@@ -237,18 +276,28 @@ class MyE2BCloudSandbox(E2BCloudSandbox):
237
276
  if hasattr(self.sandbox, "download_url"):
238
277
  logger.info(f"Downloading {file_path} via download_url method")
239
278
 
240
- # Get the download URL
241
- url = self.sandbox.download_url(file_path)
242
- logger.debug(f"Got download URL: {url}")
243
-
244
- response = requests.get(url, timeout=30)
245
-
246
- if response.status_code == HTTPStatus.OK:
247
- content = response.content
248
- logger.info(f"Successfully downloaded {len(content)} bytes via URL")
249
- return content
279
+ try:
280
+ url = self.sandbox.download_url(file_path)
281
+ except Exception as e:
282
+ logger.warning(f"Failed to get download URL: {str(e)}")
250
283
  else:
251
- logger.warning(f"URL download failed with status {response.status_code}")
284
+ logger.debug(f"Got download URL: {url}")
285
+
286
+ try:
287
+ response = requests.get(url, timeout=30)
288
+ except Exception as e:
289
+ logger.warning(f"URL download failed with error: {str(e)}")
290
+ else:
291
+ if response.status_code == HTTPStatus.OK:
292
+ content = response.content
293
+ logger.info(f"Successfully downloaded {len(content)} bytes via URL")
294
+ return content
295
+ logger.warning(f"URL download failed with status {response.status_code}")
296
+
297
+ if self.files:
298
+ logger.info(f"Downloading {file_path} via filesystem API")
299
+ content = self.files.read(file_path, format="bytes")
300
+ return bytes(content)
252
301
 
253
302
  return None
254
303
 
@@ -2,7 +2,7 @@ from _typeshed import Incomplete
2
2
  from aip_agents.tools.code_sandbox.constant import DATA_FILE_PATH as DATA_FILE_PATH
3
3
  from aip_agents.utils.logger import get_logger as get_logger
4
4
  from gllm_inference.schema import Attachment as Attachment
5
- from gllm_tools.code_interpreter.code_sandbox.e2b_cloud_sandbox import E2BCloudSandbox
5
+ from gllm_tools.code_interpreter.code_sandbox.e2b_sandbox import E2BSandbox
6
6
  from gllm_tools.code_interpreter.code_sandbox.models import ExecutionResult
7
7
  from typing import Any
8
8
 
@@ -34,15 +34,31 @@ class SandboxFileWatcher:
34
34
  list[str]: List of file paths that were created.
35
35
  """
36
36
 
37
- class MyE2BCloudSandbox(E2BCloudSandbox):
38
- """Extended E2B Cloud Sandbox with filesystem monitoring capabilities."""
37
+ class MyE2BCloudSandbox(E2BSandbox):
38
+ """Extended E2B sandbox with filesystem monitoring capabilities.
39
+
40
+ Use `create()` in production to build a fully initialized sandbox wrapper.
41
+ Direct construction is intentionally blocked to prevent partially initialized
42
+ instances that lack the underlying E2B sandbox clients.
43
+ """
39
44
  file_watcher: SandboxFileWatcher | None
40
- def __init__(self, *args, **kwargs) -> None:
41
- """Initialize the sandbox with monitoring capabilities.
45
+ def __init__(self, language: str = 'python', *, _unsafe_allow_init: bool = False) -> None:
46
+ """Initialize the sandbox wrapper.
42
47
 
43
48
  Args:
44
- *args: Positional arguments forwarded to ``E2BCloudSandbox``.
45
- **kwargs: Keyword arguments forwarded to ``E2BCloudSandbox``.
49
+ language (str): Language to execute inside the sandbox.
50
+ _unsafe_allow_init (bool): Escape hatch for tests/mocks only.
51
+
52
+ Raises:
53
+ RuntimeError: When instantiated directly without `create()`.
54
+ """
55
+ @classmethod
56
+ async def create(cls, api_key: str, domain: str | None = None, template: str | None = None, language: str = 'python', additional_packages: list[str] | None = None, **kwargs: Any) -> MyE2BCloudSandbox:
57
+ """Create a fully initialized sandbox wrapper.
58
+
59
+ This is the supported construction path for production usage. It wires
60
+ the E2B sandbox instance and its filesystem/command clients, then
61
+ installs language dependencies.
46
62
  """
47
63
  async def execute_code(self, code: str, timeout: int = 30, files: list[Attachment] | None = None, **kwargs: Any) -> ExecutionResult:
48
64
  """Execute code in the E2B Cloud sandbox with filesystem monitoring.
@@ -72,8 +88,8 @@ class MyE2BCloudSandbox(E2BCloudSandbox):
72
88
  def download_file(self, file_path: str) -> bytes | None:
73
89
  """Download file content from the sandbox.
74
90
 
75
- Uses download_url method to get a direct URL and downloads via HTTP,
76
- which avoids the binary corruption issue with files.read().
91
+ Uses download_url when available to avoid binary corruption issues.
92
+ Falls back to the filesystem API when download_url fails or is unavailable.
77
93
 
78
94
  Args:
79
95
  file_path (str): Path to the file in the sandbox.
@@ -12,7 +12,7 @@ from typing import Any
12
12
 
13
13
  import pandas as pd
14
14
  from gllm_inference.schema import Attachment
15
- from gllm_tools.code_interpreter.code_sandbox.e2b_cloud_sandbox import E2BCloudSandbox
15
+ from gllm_tools.code_interpreter.code_sandbox.sandbox import BaseSandbox
16
16
  from langchain_core.tools import BaseTool
17
17
  from langgraph.types import Command
18
18
  from pydantic import BaseModel, Field
@@ -260,7 +260,7 @@ class E2BCodeSandboxTool(BaseTool):
260
260
  """
261
261
  unique_packages = self._prepare_packages(additional_packages)
262
262
 
263
- return MyE2BCloudSandbox(
263
+ return await MyE2BCloudSandbox.create(
264
264
  api_key=self.api_key,
265
265
  language=language,
266
266
  additional_packages=unique_packages,
@@ -304,11 +304,11 @@ class E2BCodeSandboxTool(BaseTool):
304
304
  except Exception as e:
305
305
  logger.warning(f"Error terminating sandbox: {str(e)}")
306
306
 
307
- def _create_artifacts_from_files(self, sandbox: E2BCloudSandbox, file_paths: list[str]) -> list[dict[str, Any]]:
307
+ def _create_artifacts_from_files(self, sandbox: BaseSandbox, file_paths: list[str]) -> list[dict[str, Any]]:
308
308
  """Create artifacts from a list of file paths using ArtifactHandler.
309
309
 
310
310
  Args:
311
- sandbox (E2BCloudSandbox): The active sandbox instance.
311
+ sandbox (BaseSandbox): The active sandbox instance.
312
312
  file_paths (list[str]): List of file paths to download.
313
313
 
314
314
  Returns:
@@ -358,11 +358,11 @@ class E2BCodeSandboxTool(BaseTool):
358
358
 
359
359
  return execution_summary
360
360
 
361
- def _download_and_create_artifact(self, sandbox: E2BCloudSandbox, file_path: str) -> dict[str, Any] | None:
361
+ def _download_and_create_artifact(self, sandbox: BaseSandbox, file_path: str) -> dict[str, Any] | None:
362
362
  """Download a single file and create an artifact using ArtifactHandler.
363
363
 
364
364
  Args:
365
- sandbox (E2BCloudSandbox): The active sandbox instance.
365
+ sandbox (BaseSandbox): The active sandbox instance.
366
366
  file_path (str): Path to the file in the sandbox.
367
367
 
368
368
  Returns:
@@ -15,9 +15,7 @@ GL_CONNECTORS_BASE_URL = (
15
15
  )
16
16
  GL_CONNECTORS_API_KEY = os.getenv("GL_CONNECTORS_API_KEY") or os.getenv("BOSA_API_KEY")
17
17
  GL_CONNECTORS_FETCH_MAX_RETRIES = int(
18
- os.getenv("GL_CONNECTORS_FETCH_MAX_RETRIES")
19
- or os.getenv("BOSA_FETCH_MAX_RETRIES")
20
- or 3
18
+ os.getenv("GL_CONNECTORS_FETCH_MAX_RETRIES") or os.getenv("BOSA_FETCH_MAX_RETRIES") or 3
21
19
  )
22
20
 
23
21
  # For backward compatibility
@@ -14,12 +14,13 @@ import os
14
14
  from collections.abc import Iterable
15
15
  from typing import Any
16
16
 
17
- from aip_agents.tools.constants import ToolType
18
17
  from bosa_connectors import BosaConnector, BOSAConnectorToolGenerator
19
18
  from langchain_core.runnables import RunnableConfig
20
19
  from langchain_core.tools import BaseTool
21
20
  from pydantic import ConfigDict, PrivateAttr
22
21
 
22
+ from aip_agents.tools.constants import ToolType
23
+
23
24
  _REQUIRED_ENV_VARS: tuple[str, ...] = (
24
25
  "GL_CONNECTORS_BASE_URL",
25
26
  "GL_CONNECTORS_API_KEY",
@@ -66,9 +67,7 @@ class _InjectedTool(BaseTool):
66
67
  Returns:
67
68
  None
68
69
  """
69
- base_fields = {
70
- field: getattr(base_tool, field) for field in BaseTool.model_fields
71
- }
70
+ base_fields = {field: getattr(base_tool, field) for field in BaseTool.model_fields}
72
71
  super().__init__(**base_fields)
73
72
  self._base_tool = base_tool
74
73
  self._token = token
@@ -98,9 +97,7 @@ class _InjectedTool(BaseTool):
98
97
  """
99
98
  return await self._base_tool._arun(*args, **kwargs)
100
99
 
101
- def invoke(
102
- self, input: Any, config: RunnableConfig | None = None, **kwargs: Any
103
- ) -> Any:
100
+ def invoke(self, input: Any, config: RunnableConfig | None = None, **kwargs: Any) -> Any:
104
101
  """Invoke the tool with token and optional identifier injected.
105
102
 
106
103
  Args:
@@ -114,9 +111,7 @@ class _InjectedTool(BaseTool):
114
111
  injected = _inject_params(input, self._token, self._identifier, self._base_tool)
115
112
  return super().invoke(injected, config=config, **kwargs)
116
113
 
117
- async def ainvoke(
118
- self, input: Any, config: RunnableConfig | None = None, **kwargs: Any
119
- ) -> Any:
114
+ async def ainvoke(self, input: Any, config: RunnableConfig | None = None, **kwargs: Any) -> Any:
120
115
  """Invoke the tool asynchronously with token and optional identifier injected.
121
116
 
122
117
  Args:
@@ -140,9 +135,7 @@ class _InjectedTool(BaseTool):
140
135
  Returns:
141
136
  The result of running the tool with injected parameters.
142
137
  """
143
- injected = _inject_params(
144
- tool_input, self._token, self._identifier, self._base_tool
145
- )
138
+ injected = _inject_params(tool_input, self._token, self._identifier, self._base_tool)
146
139
  return super().run(injected, **kwargs)
147
140
 
148
141
  async def arun(self, tool_input: Any, **kwargs: Any) -> Any:
@@ -155,9 +148,7 @@ class _InjectedTool(BaseTool):
155
148
  Returns:
156
149
  The result of running the tool with injected parameters.
157
150
  """
158
- injected = _inject_params(
159
- tool_input, self._token, self._identifier, self._base_tool
160
- )
151
+ injected = _inject_params(tool_input, self._token, self._identifier, self._base_tool)
161
152
  return await super().arun(injected, **kwargs)
162
153
 
163
154
 
@@ -200,9 +191,7 @@ def GLConnectorTool(
200
191
  if not matching:
201
192
  raise ValueError(f"Tool '{tool_name}' not found in module '{module_name}'")
202
193
  if len(matching) > 1:
203
- raise ValueError(
204
- f"Multiple tools named '{tool_name}' found in module '{module_name}'"
205
- )
194
+ raise ValueError(f"Multiple tools named '{tool_name}' found in module '{module_name}'")
206
195
 
207
196
  token = _create_token(
208
197
  connector,
@@ -249,9 +238,7 @@ def _load_env(*, api_key: str | None, identifier: str | None) -> dict[str, str]:
249
238
  for m in missing:
250
239
  preferred = _ENV_VAR_MAPPING[m][0]
251
240
  friendly_missing.append(preferred)
252
- raise ValueError(
253
- f"Missing required environment variables: {', '.join(friendly_missing)}"
254
- )
241
+ raise ValueError(f"Missing required environment variables: {', '.join(friendly_missing)}")
255
242
 
256
243
  return {k: v for k, v in env.items() if v is not None}
257
244
 
@@ -291,21 +278,13 @@ def _resolve_module(tool_name: str, modules: Iterable[str]) -> str:
291
278
  Raises:
292
279
  ValueError: If no matching module is found or multiple ambiguous matches exist.
293
280
  """
294
- candidates = [
295
- module
296
- for module in modules
297
- if tool_name == module or tool_name.startswith(f"{module}_")
298
- ]
281
+ candidates = [module for module in modules if tool_name == module or tool_name.startswith(f"{module}_")]
299
282
  if not candidates:
300
- raise ValueError(
301
- f"Unable to resolve module for tool '{tool_name}'. Available modules: {', '.join(modules)}"
302
- )
283
+ raise ValueError(f"Unable to resolve module for tool '{tool_name}'. Available modules: {', '.join(modules)}")
303
284
 
304
285
  candidates.sort(key=len, reverse=True)
305
286
  if len(candidates) > 1 and len(candidates[0]) == len(candidates[1]):
306
- raise ValueError(
307
- f"Ambiguous module match for tool '{tool_name}'. Matches: {', '.join(candidates)}"
308
- )
287
+ raise ValueError(f"Ambiguous module match for tool '{tool_name}'. Matches: {', '.join(candidates)}")
309
288
  return candidates[0]
310
289
 
311
290
 
@@ -334,9 +313,7 @@ def _create_token(connector: BosaConnector, username: str, password: str) -> str
334
313
  return token
335
314
 
336
315
 
337
- def _inject_params(
338
- tool_input: Any, token: str, identifier: str | None, base_tool: BaseTool
339
- ) -> dict[str, Any]:
316
+ def _inject_params(tool_input: Any, token: str, identifier: str | None, base_tool: BaseTool) -> dict[str, Any]:
340
317
  """Inject token and optional identifier into tool input.
341
318
 
342
319
  Args:
@@ -374,9 +351,7 @@ def _inject_params(
374
351
  return injected
375
352
 
376
353
 
377
- def _wrap_request_if_needed(
378
- tool_input: dict[str, Any], base_tool: BaseTool
379
- ) -> dict[str, Any]:
354
+ def _wrap_request_if_needed(tool_input: dict[str, Any], base_tool: BaseTool) -> dict[str, Any]:
380
355
  """Wrap flat inputs into a 'request' payload when required by schema.
381
356
 
382
357
  Args:
@@ -387,9 +362,7 @@ def _wrap_request_if_needed(
387
362
  Dictionary with inputs wrapped in 'request' key if needed, otherwise unchanged.
388
363
  """
389
364
  args_schema = getattr(base_tool, "args_schema", None)
390
- if not (
391
- isinstance(args_schema, dict) and "request" in args_schema.get("properties", {})
392
- ):
365
+ if not (isinstance(args_schema, dict) and "request" in args_schema.get("properties", {})):
393
366
  return tool_input
394
367
 
395
368
  request_payload = {}
@@ -4,6 +4,9 @@ Authors:
4
4
  Saul Sayers (saul.sayers@gdplabs.id)
5
5
  """
6
6
 
7
+ from bosa_connectors import BosaConnector, BOSAConnectorToolGenerator
8
+ from langchain_core.tools import BaseTool
9
+
7
10
  from aip_agents.tools.constants import (
8
11
  GL_CONNECTORS_API_KEY,
9
12
  GL_CONNECTORS_BASE_URL,
@@ -11,8 +14,6 @@ from aip_agents.tools.constants import (
11
14
  ToolType,
12
15
  )
13
16
  from aip_agents.utils.logger import get_logger
14
- from bosa_connectors import BosaConnector, BOSAConnectorToolGenerator
15
- from langchain_core.tools import BaseTool
16
17
 
17
18
  logger = get_logger(__name__)
18
19
 
@@ -24,14 +25,10 @@ def get_gl_connector_modules_with_retry() -> list[str]:
24
25
  List of available modules.
25
26
  """
26
27
  if not GL_CONNECTORS_BASE_URL or not GL_CONNECTORS_API_KEY:
27
- logger.warning(
28
- "GL Connectors credentials missing (base_url or api_key); returning empty modules list"
29
- )
28
+ logger.warning("GL Connectors credentials missing (base_url or api_key); returning empty modules list")
30
29
  return []
31
30
 
32
- connector = BosaConnector(
33
- api_base_url=GL_CONNECTORS_BASE_URL, api_key=GL_CONNECTORS_API_KEY
34
- )
31
+ connector = BosaConnector(api_base_url=GL_CONNECTORS_BASE_URL, api_key=GL_CONNECTORS_API_KEY)
35
32
  modules = []
36
33
  for attempt in range(GL_CONNECTORS_FETCH_MAX_RETRIES):
37
34
  try:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aip-agents-binary
3
- Version: 0.5.22
3
+ Version: 0.5.23
4
4
  Summary: A library for managing agents in Gen AI applications.
5
5
  Author-email: Raymond Christopher <raymond.christopher@gdplabs.id>
6
6
  Requires-Python: <3.13,>=3.11
@@ -15,7 +15,7 @@ Requires-Dist: deprecated<2.0.0,>=1.2.18
15
15
  Requires-Dist: fastapi<0.121.0,>=0.120.0
16
16
  Requires-Dist: gllm-core-binary<0.4.0,>=0.3.18
17
17
  Requires-Dist: gllm-inference-binary[anthropic,bedrock,google-genai,google-vertexai,openai]<0.6.0,>=0.5.90
18
- Requires-Dist: gllm-tools-binary<0.2.0,>=0.1.3
18
+ Requires-Dist: gllm-tools-binary<0.2.0,>=0.1.5
19
19
  Requires-Dist: google-adk<0.6.0,>=0.5.0
20
20
  Requires-Dist: langchain<0.4.0,>=0.3.0
21
21
  Requires-Dist: langchain-openai<0.4.0,>=0.3.17
@@ -34,7 +34,7 @@ Requires-Dist: gllm-privacy-binary<0.5.0,>=0.4.12; extra == "privacy"
34
34
  Provides-Extra: gl-connector
35
35
  Requires-Dist: bosa-connectors-binary<0.4.0,>=0.3.1; extra == "gl-connector"
36
36
  Provides-Extra: local
37
- Requires-Dist: e2b<2.0.0,>=1.5.4; extra == "local"
37
+ Requires-Dist: e2b<3.0.0,>=2.3.0; extra == "local"
38
38
  Requires-Dist: browser-use==0.5.9; extra == "local"
39
39
  Requires-Dist: steel-sdk>=0.7.0; extra == "local"
40
40
  Requires-Dist: json-repair>=0.52.3; extra == "local"
@@ -179,7 +179,7 @@ aip_agents/examples/hello_world_langflow_agent.py,sha256=NFgI_jJ0mDpeRpVhw5TESRS
179
179
  aip_agents/examples/hello_world_langflow_agent.pyi,sha256=sl3sF36CcY_s_zQ61gvneRyTiHmPV8p9xzPL2IeB1zo,1251
180
180
  aip_agents/examples/hello_world_langgraph.py,sha256=CSlZzVOtwbnCxw3uci_qGFzO7eYZe0uzs-4kmGNih7A,1248
181
181
  aip_agents/examples/hello_world_langgraph.pyi,sha256=ix3j1wqj79wkMMuOjEQUhZA4Isr5JSFEJw49WJ2NmM4,251
182
- aip_agents/examples/hello_world_langgraph_gl_connector_twitter.py,sha256=rZ517PtYjE9azJjtSOXkZgyc_g7pHejVS7Ij4gz-BqE,1327
182
+ aip_agents/examples/hello_world_langgraph_gl_connector_twitter.py,sha256=cOvEKKr1cvyl1R3CX5c6JFUpwnOWYmAWtbJuqvwUsWA,1328
183
183
  aip_agents/examples/hello_world_langgraph_gl_connector_twitter.pyi,sha256=zvvkXFJw8NV4W7B8XB4b4F3s-HKixsasSMfJAuteqoQ,264
184
184
  aip_agents/examples/hello_world_langgraph_mcp_http.py,sha256=BmfwomTLNbGH3jpMMxGmSV_waYuvfbtdYdeKqVVzEPw,1008
185
185
  aip_agents/examples/hello_world_langgraph_mcp_http.pyi,sha256=8Ye3T6ip-_qykUEsYz5oPrJ99Td89OvFYwTdiP6-hAk,264
@@ -209,7 +209,7 @@ aip_agents/examples/hello_world_multi_agent_langgraph_lm_invoker.py,sha256=sppLD
209
209
  aip_agents/examples/hello_world_multi_agent_langgraph_lm_invoker.pyi,sha256=zFNns9i-sPY8aBy4HaVS6tXDWTsU7f-ApLD8gDtb1uk,252
210
210
  aip_agents/examples/hello_world_pii_logger.py,sha256=pm9LK-doZwQL-VdJYFHziuvwH562c8wwAwmKZeAWQS0,584
211
211
  aip_agents/examples/hello_world_pii_logger.pyi,sha256=5gOVrvhQV2DxIwr7ocL1-oTUXsO8XSyl5WN70c7rl_w,134
212
- aip_agents/examples/hello_world_sentry.py,sha256=UI9bkfoui4SjhHWSllJGUdaup3kYzGtoomZkY1zhcXg,4005
212
+ aip_agents/examples/hello_world_sentry.py,sha256=Gccr6WCevSnMFZYT5M6uJ_22EvLmAMUE8Et_H510_kM,3976
213
213
  aip_agents/examples/hello_world_sentry.pyi,sha256=k0wFj46QqXEjBU0CEERmiEf2bovVFkupzuzQzeD6h00,671
214
214
  aip_agents/examples/hello_world_step_limits.py,sha256=hRV9XERlq2qW_yZ4kHAe1hTgukrSZjThIrsY9NuTdJg,9809
215
215
  aip_agents/examples/hello_world_step_limits.pyi,sha256=V9KP6r5OEVq2lNUfnqMeCGH1nsNewDsRL1Uo9up8xis,1060
@@ -292,7 +292,7 @@ aip_agents/mcp/client/persistent_session.py,sha256=I8JjHe_ZqU1PPB2OF6hUX4kcrraVW
292
292
  aip_agents/mcp/client/persistent_session.pyi,sha256=in3TgtC-eHD6j2payC9_TryuOLOmqoeDKfl7UaZ_kBA,3908
293
293
  aip_agents/mcp/client/session_pool.py,sha256=_J8WduSo3HAfhE5n4u67IQQ71m_L2aPUY1NOgX5e7yA,12617
294
294
  aip_agents/mcp/client/session_pool.pyi,sha256=RtzN-5QDLS5MFAlnR5TiarY1xW4xo780n7lQL0sQbRU,3579
295
- aip_agents/mcp/client/transports.py,sha256=1HqmFqpmktFhR-vKU85xuvWcwogkIjQIh9se0qo4LEM,8172
295
+ aip_agents/mcp/client/transports.py,sha256=i7cJ1_vUUrE4yVTq-nFf0AnfKv0j9k1d8EcVZBykTUI,8624
296
296
  aip_agents/mcp/client/transports.pyi,sha256=_V6ZaQyKtTMhHzNQH943asy0O6z2bHTOX8hc-lZlV2s,4576
297
297
  aip_agents/mcp/client/google_adk/__init__.py,sha256=ZJZE7IusoWFzEwaWsHOk-8VMdR-LUmWacepw_HHuOlI,280
298
298
  aip_agents/mcp/client/google_adk/__init__.pyi,sha256=TAbiDbysxbCtHQSASGdko6JIaZEnPbCmUqt4Jf966cs,127
@@ -350,7 +350,7 @@ aip_agents/schema/storage.py,sha256=Tl0RFT32GWdsc5y8bTvpWMOU8hmQxXIlrMub3qdwx8Y,
350
350
  aip_agents/schema/storage.pyi,sha256=exaZAS49PYSuhYSPYukIRCRHIGRyCpBGucrdpnhV4zE,557
351
351
  aip_agents/sentry/__init__.py,sha256=l3nI-3pjBNNmcOZ7xPeIis1n_wkPpvnk2aHxQYWsflU,261
352
352
  aip_agents/sentry/__init__.pyi,sha256=OGxzQzEvbnqf02zXdBJUrHeKfjfBgvnOL7p-0gNvP8k,103
353
- aip_agents/sentry/sentry.py,sha256=DgYaIph-VQp0b36sUFvBqxe9lmbBZv1i1edg0Tx5nbE,4595
353
+ aip_agents/sentry/sentry.py,sha256=zyhIZCnA2mcLuf5w-ezE_7ZWOjPdGt3A5g7AcJrPgxY,4504
354
354
  aip_agents/sentry/sentry.pyi,sha256=fTFb71FfeByBUIACjkp8qaHsWe6C52Y4tPTfizAPVv0,1432
355
355
  aip_agents/storage/__init__.py,sha256=cN4L2whui-DOm81tsYV88Wx5g5-cUjeLVyp55RtbJJU,1219
356
356
  aip_agents/storage/__init__.pyi,sha256=wn8VuS7x4WSzKuV01WrjPkZfnh3GylNum9FlPiaSdsA,901
@@ -370,11 +370,11 @@ aip_agents/storage/providers/memory.py,sha256=81E9yT_oRkH7tMVr5azZv1oXIaeTzTAtTu
370
370
  aip_agents/storage/providers/memory.pyi,sha256=r0meeCsjKyE9FHJqCysfQ5ZH_FWRRv2SWg-NOHCUD-g,2109
371
371
  aip_agents/storage/providers/object_storage.py,sha256=Q268Sn8Y09gS-ilP6fe4CR0YIOvdbunh3V6SmuQsAVs,6413
372
372
  aip_agents/storage/providers/object_storage.pyi,sha256=nBIKJjUAWkIKhYhBFmFcypjoOieIYOghy55f62x7AX8,2878
373
- aip_agents/tools/__init__.py,sha256=HWj2nsjMBadA6aJdPlBQBSBxbvDAYmMKe3PJeNyDRbA,1935
373
+ aip_agents/tools/__init__.py,sha256=J0wDdcKre3Qc_z1hhzKfPiaa1m2OPOXyHjl6uwOkApU,1935
374
374
  aip_agents/tools/__init__.pyi,sha256=w1gRWL5iRdLtuBNXBhmD87uzfvIRrXwq3uZ1meku8js,888
375
- aip_agents/tools/constants.py,sha256=S8-ctDo4FXp5Si8dFCJ2FORPPS1SKD2OQT5II3j1yak,5731
375
+ aip_agents/tools/constants.py,sha256=AabnuPQG_mc2sVdr9jV23_6bFempAsxQv3kdCR_ztLA,5723
376
376
  aip_agents/tools/constants.pyi,sha256=kFY8dKgRqHKGvPqG3QUyuEc8C5yRaDj7DYQDH88K2T0,3552
377
- aip_agents/tools/gl_connector_tools.py,sha256=yXLuGTTAUxCYpOG808pKcHEZIntNI_x-JUq3OqzxexI,3971
377
+ aip_agents/tools/gl_connector_tools.py,sha256=bxl_3VQYZDv3lFn6Y3kDVVRFwH4cntOLz3f74YzDcic,3936
378
378
  aip_agents/tools/gl_connector_tools.pyi,sha256=2ATn_MW_FRg5Uv7dLI_ToBOtlgTSfj0zgDQpN1N-cJs,1366
379
379
  aip_agents/tools/memory_search_tool.py,sha256=gqTTb_Fao_Hl-SavfaFsqPDhnFQUjzIQUkzizSD2L0A,653
380
380
  aip_agents/tools/memory_search_tool.pyi,sha256=BCVFEEgH3pETnI9b6vRlMm2_PnnIeBe_vuMlL9y3LpU,453
@@ -412,9 +412,9 @@ aip_agents/tools/code_sandbox/__init__.py,sha256=QPUBMzmSDGjvjnLRRJRHK7shP16LH6K
412
412
  aip_agents/tools/code_sandbox/__init__.pyi,sha256=hBgsW_ZdnGonBx7PtCwz3ohs8RuZcDpQMU6iK_VZIYg,134
413
413
  aip_agents/tools/code_sandbox/constant.py,sha256=55uM9sHOISdT40O3pVdLfL88YU-1Npy7CXkYxlLFzug,679
414
414
  aip_agents/tools/code_sandbox/constant.pyi,sha256=3bwRpYOZZNVYjBBvPXnTR_cq6dH-pALsy_5yCbxdPQU,81
415
- aip_agents/tools/code_sandbox/e2b_cloud_sandbox_extended.py,sha256=H91-1a3keS6Si5hLzQ7Z1tHM7ZWEv6p2fJQ6HSqttw0,9821
416
- aip_agents/tools/code_sandbox/e2b_cloud_sandbox_extended.pyi,sha256=ZnW7S-Wla5J-ZD8P92-sXuzTGZ2-z5zyv4aH97FlshI,3554
417
- aip_agents/tools/code_sandbox/e2b_sandbox_tool.py,sha256=sTFTQd9Li9lB2iXgsARfpJEfFz9O2_sdFYFh8gFvC6A,16954
415
+ aip_agents/tools/code_sandbox/e2b_cloud_sandbox_extended.py,sha256=78mrQ4Tfv0HQst92NugC2_ogw7LU0ubx8XKFAbW53vY,12013
416
+ aip_agents/tools/code_sandbox/e2b_cloud_sandbox_extended.pyi,sha256=W2NIk0b9dK-HTResYt1ppQdAcBLp2963D94XHX9hq6E,4366
417
+ aip_agents/tools/code_sandbox/e2b_sandbox_tool.py,sha256=TChVXscOR1WXmpjelwcZ_Ws4pYtuYhLALbvPXYz6iSU,16937
418
418
  aip_agents/tools/code_sandbox/e2b_sandbox_tool.pyi,sha256=ylrBQaqNBa1Jppld-mHjaskSa9SOjsQD8TdcK2bnl4s,1165
419
419
  aip_agents/tools/document_loader/__init__.py,sha256=rnQFLYJqvit5eegGIWGdjOUoEJiyOh3nW0mLSRd7xfE,1375
420
420
  aip_agents/tools/document_loader/__init__.pyi,sha256=RYZb-EdfR1btPxFBUwfmrOWs51aVgwJuw9epguxnVgQ,650
@@ -430,7 +430,7 @@ aip_agents/tools/document_loader/pdf_splitter.py,sha256=-QyrlnN4AXDqY_dxeUzxcgCV
430
430
  aip_agents/tools/document_loader/pdf_splitter.pyi,sha256=3IiRDQWDz8QR5LH_sni9O-N2qJFheFjKOQMAhiWxB7E,716
431
431
  aip_agents/tools/gl_connector/__init__.py,sha256=f8F4mdBFj0ulxewCQwG5qN2SDzlgI2jA0Kfj31A5iD0,137
432
432
  aip_agents/tools/gl_connector/__init__.pyi,sha256=96wtNkB3VUSI66aRlYxVrzMiPtqOYviRMKviRgX3_fc,113
433
- aip_agents/tools/gl_connector/tool.py,sha256=EmfAYU5ZEtG93osWPWd4ElfXIhBe7mqz0yIH6Ntc9sE,13343
433
+ aip_agents/tools/gl_connector/tool.py,sha256=jzT8XmTfFQC9ZcQplVcRs2VmCtKewH9FzT7wSFtUJac,13106
434
434
  aip_agents/tools/gl_connector/tool.pyi,sha256=a5l0MHSOe_iWDvjMRzYtcbMdX0bFlK1m7Hl52HQ__iQ,2770
435
435
  aip_agents/tools/memory_search/__init__.py,sha256=YSsObYlHjdZEbJj4MVYy3Ht8JPlo42YhjnkI-yFNWV0,608
436
436
  aip_agents/tools/memory_search/__init__.pyi,sha256=NG0g94OoC_xw66yIqiPViqTnpj01QdnRsVBGzkSxJFI,554
@@ -540,7 +540,7 @@ aip_agents/utils/pii/pii_helper.py,sha256=g0yRzakfA2AA6vjUNLWHqFlcxyLql6MXQ90NN3
540
540
  aip_agents/utils/pii/pii_helper.pyi,sha256=dulZs150ikbAL3Bw2YLcz3_g4DsGmL3lciwf8mKxEjI,2939
541
541
  aip_agents/utils/pii/uuid_deanonymizer_mapping.py,sha256=Gks8l8t0cuS9pzoQnrpiK1CaLmWYksjOnTeiHh3_7EE,7348
542
542
  aip_agents/utils/pii/uuid_deanonymizer_mapping.pyi,sha256=gnWfD1rWZh_tloJjgKiZ6f6iNUuBaHpKqCSiP0d-9bs,3084
543
- aip_agents_binary-0.5.22.dist-info/METADATA,sha256=CocpyU4hmWP-sT9ti3uGtzFDcULe2Vr2sJVH-Ae2ftM,22214
544
- aip_agents_binary-0.5.22.dist-info/WHEEL,sha256=PaP4PvkDyiSc4C6Dhw6ccQmfxsWFrSv-lJQjBshu0hw,105
545
- aip_agents_binary-0.5.22.dist-info/top_level.txt,sha256=PEz8vcwC1bH4UrkhF0LkIYCNfXGWZUHdSklbvkBe25E,11
546
- aip_agents_binary-0.5.22.dist-info/RECORD,,
543
+ aip_agents_binary-0.5.23.dist-info/METADATA,sha256=VlQHX0DiRpcp68ytt7zTHreClijm1LVv-IKEoz23044,22214
544
+ aip_agents_binary-0.5.23.dist-info/WHEEL,sha256=PaP4PvkDyiSc4C6Dhw6ccQmfxsWFrSv-lJQjBshu0hw,105
545
+ aip_agents_binary-0.5.23.dist-info/top_level.txt,sha256=PEz8vcwC1bH4UrkhF0LkIYCNfXGWZUHdSklbvkBe25E,11
546
+ aip_agents_binary-0.5.23.dist-info/RECORD,,