Skip to content

Commit d0ad8af

Browse files
committed
remove files
1 parent a9c33c9 commit d0ad8af

File tree

9 files changed

+225
-15
lines changed

9 files changed

+225
-15
lines changed

packages/toolbox-core/README.md

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -300,15 +300,24 @@ that fresh credentials or header values can be used.
300300

301301
### Configuration
302302

303-
You can configure these dynamic headers as seen below:
303+
You can configure these dynamic headers in two ways:
304304

305-
```python
306-
from toolbox_core import ToolboxClient
305+
1. **During Client Initialization**
307306

308-
async with ToolboxClient("toolbox-url", client_headers={"header1": header1_getter, "header2": header2_getter, ...}) as client:
309-
# Use client
310-
pass
311-
```
307+
```python
308+
from toolbox_core import ToolboxClient
309+
310+
async with ToolboxClient("toolbox-url", client_headers={"header1": header1_getter, "header2": header2_getter, ...}) as client:
311+
```
312+
313+
1. **After Client Initialization**
314+
315+
```python
316+
from toolbox_core import ToolboxClient
317+
318+
async with ToolboxClient("toolbox-url") as client:
319+
client.add_headers({"header1": header1_getter, "header2": header2_getter, ...})
320+
```
312321

313322
### Authenticating with Google Cloud Servers
314323

packages/toolbox-core/src/toolbox_core/client.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,3 +330,27 @@ async def load_toolset(
330330
)
331331

332332
return tools
333+
334+
def add_headers(
335+
self,
336+
headers: Mapping[str, Union[Callable[[], str], Callable[[], Awaitable[str]]]],
337+
) -> None:
338+
"""
339+
Add headers to be included in each request sent through this client.
340+
341+
Args:
342+
headers: Headers to include in each request sent through this client.
343+
344+
Raises:
345+
ValueError: If any of the headers are already registered in the client.
346+
"""
347+
existing_headers = self.__client_headers.keys()
348+
incoming_headers = headers.keys()
349+
duplicates = existing_headers & incoming_headers
350+
if duplicates:
351+
raise ValueError(
352+
f"Client header(s) `{', '.join(duplicates)}` already registered in the client."
353+
)
354+
355+
merged_headers = {**self.__client_headers, **headers}
356+
self.__client_headers = merged_headers

packages/toolbox-core/src/toolbox_core/sync_client.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,23 @@ def load_toolset(
153153
for async_tool in async_tools
154154
]
155155

156+
def add_headers(
157+
self,
158+
headers: Mapping[
159+
str, Union[Callable[[], str], Callable[[], Awaitable[str]], str]
160+
],
161+
) -> None:
162+
"""
163+
Add headers to be included in each request sent through this client.
164+
165+
Args:
166+
headers: Headers to include in each request sent through this client.
167+
168+
Raises:
169+
ValueError: If any of the headers are already registered in the client.
170+
"""
171+
self.__async_client.add_headers(headers)
172+
156173
def __enter__(self):
157174
"""Enter the runtime context related to this client instance."""
158175
return self

packages/toolbox-core/tests/test_client.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1417,6 +1417,50 @@ async def test_load_toolset_with_headers(
14171417
assert len(tools) == 1
14181418
assert tools[0].__name__ == tool_name
14191419

1420+
@pytest.mark.asyncio
1421+
async def test_add_headers_success(
1422+
self, aioresponses, test_tool_str, static_header
1423+
):
1424+
"""Tests adding headers after client initialization."""
1425+
tool_name = "tool_after_add_headers"
1426+
manifest = ManifestSchema(
1427+
serverVersion="0.0.0", tools={tool_name: test_tool_str}
1428+
)
1429+
expected_payload = {"result": "added_ok"}
1430+
1431+
get_callback = self.create_callback_factory(
1432+
expected_header=static_header,
1433+
callback_payload=manifest.model_dump(),
1434+
)
1435+
aioresponses.get(f"{TEST_BASE_URL}/api/tool/{tool_name}", callback=get_callback)
1436+
1437+
post_callback = self.create_callback_factory(
1438+
expected_header=static_header,
1439+
callback_payload=expected_payload,
1440+
)
1441+
aioresponses.post(
1442+
f"{TEST_BASE_URL}/api/tool/{tool_name}/invoke", callback=post_callback
1443+
)
1444+
1445+
async with ToolboxClient(TEST_BASE_URL) as client:
1446+
client.add_headers(static_header)
1447+
assert client._ToolboxClient__client_headers == static_header
1448+
1449+
tool = await client.load_tool(tool_name)
1450+
result = await tool(param1="test")
1451+
assert result == expected_payload["result"]
1452+
1453+
@pytest.mark.asyncio
1454+
async def test_add_headers_duplicate_fail(self, static_header):
1455+
"""Tests that adding a duplicate header via add_headers raises
1456+
ValueError."""
1457+
async with ToolboxClient(TEST_BASE_URL, client_headers=static_header) as client:
1458+
with pytest.raises(
1459+
ValueError,
1460+
match=f"Client header\\(s\\) `X-Static-Header` already registered",
1461+
):
1462+
await client.add_headers(static_header)
1463+
14201464
@pytest.mark.asyncio
14211465
async def test_client_header_auth_token_conflict_fail(
14221466
self, aioresponses, test_tool_auth

packages/toolbox-core/tests/test_sync_client.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,72 @@ def test_load_tool_raises_if_loop_or_thread_none(self):
350350
# sync_client_environment will handle the final cleanup of original_class_loop/thread.
351351

352352

353+
class TestSyncClientHeaders:
354+
"""Additive tests for client header functionality specific to ToolboxSyncClient if any,
355+
or counterparts to async client header tests."""
356+
357+
def test_sync_add_headers_success(
358+
self, aioresponses, test_tool_str_schema, sync_client
359+
):
360+
tool_name = "tool_after_add_headers_sync"
361+
manifest = ManifestSchema(
362+
serverVersion="0.0.0", tools={tool_name: test_tool_str_schema}
363+
)
364+
expected_payload = {"result": "added_sync_ok"}
365+
headers_to_add = {"X-Custom-SyncHeader": "sync_value"}
366+
367+
def get_callback(url, **kwargs):
368+
# The sync_client might have default headers. Check ours are present.
369+
assert kwargs.get("headers") is not None
370+
for key, value in headers_to_add.items():
371+
assert kwargs["headers"].get(key) == value
372+
return CallbackResult(status=200, payload=manifest.model_dump())
373+
374+
aioresponses.get(f"{TEST_BASE_URL}/api/tool/{tool_name}", callback=get_callback)
375+
376+
def post_callback(url, **kwargs):
377+
assert kwargs.get("headers") is not None
378+
for key, value in headers_to_add.items():
379+
assert kwargs["headers"].get(key) == value
380+
return CallbackResult(status=200, payload=expected_payload)
381+
382+
aioresponses.post(
383+
f"{TEST_BASE_URL}/api/tool/{tool_name}/invoke", callback=post_callback
384+
)
385+
386+
sync_client.add_headers(headers_to_add)
387+
tool = sync_client.load_tool(tool_name)
388+
result = tool(param1="test")
389+
assert result == expected_payload["result"]
390+
391+
def test_sync_add_headers_duplicate_fail(self):
392+
"""Tests that adding a duplicate header via add_headers raises ValueError (from async client)."""
393+
initial_headers = {"X-Initial-Header": "initial_value"}
394+
mock_async_client = AsyncMock(spec=ToolboxClient)
395+
396+
# Configure add_headers to simulate the ValueError from ToolboxClient
397+
def mock_add_headers(headers):
398+
# Simulate ToolboxClient's check
399+
if "X-Initial-Header" in headers:
400+
raise ValueError(
401+
"Client header(s) `X-Initial-Header` already registered"
402+
)
403+
404+
mock_async_client.add_headers = Mock(side_effect=mock_add_headers)
405+
406+
with patch(
407+
"toolbox_core.sync_client.ToolboxClient", return_value=mock_async_client
408+
):
409+
with ToolboxSyncClient(
410+
TEST_BASE_URL, client_headers=initial_headers
411+
) as client:
412+
with pytest.raises(
413+
ValueError,
414+
match="Client header\\(s\\) `X-Initial-Header` already registered",
415+
):
416+
client.add_headers({"X-Initial-Header": "another_value"})
417+
418+
353419
class TestSyncAuth:
354420
@pytest.fixture
355421
def expected_header_token(self):

packages/toolbox-langchain/README.md

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -231,16 +231,27 @@ that fresh credentials or header values can be used.
231231

232232
### Configuration
233233

234-
You can configure these dynamic headers as follows:
234+
You can configure these dynamic headers in two ways:
235235

236-
```python
237-
from toolbox_langchain import ToolboxClient
236+
1. **During Client Initialization**
238237

239-
client = ToolboxClient(
240-
"toolbox-url",
241-
client_headers={"header1": header1_getter, "header2": header2_getter, ...}
242-
)
243-
```
238+
```python
239+
from toolbox_langchain import ToolboxClient
240+
241+
client = ToolboxClient(
242+
"toolbox-url",
243+
client_headers={"header1": header1_getter, "header2": header2_getter, ...}
244+
)
245+
```
246+
247+
1. **After Client Initialization**
248+
249+
```python
250+
from toolbox_langchain import ToolboxClient
251+
252+
client = ToolboxClient("toolbox-url")
253+
client.add_headers({"header1": header1_getter, "header2": header2_getter, ...})
254+
```
244255

245256
### Authenticating with Google Cloud Servers
246257

packages/toolbox-langchain/src/toolbox_langchain/async_client.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,3 +190,18 @@ def load_toolset(
190190
strict: bool = False,
191191
) -> list[AsyncToolboxTool]:
192192
raise NotImplementedError("Synchronous methods not supported by async client.")
193+
194+
def add_headers(
195+
self,
196+
headers: Mapping[str, Union[Callable[[], str], Callable[[], Awaitable[str]]]],
197+
) -> None:
198+
"""
199+
Add headers to be included in each request sent through this client.
200+
201+
Args:
202+
headers: Headers to include in each request sent through this client.
203+
204+
Raises:
205+
ValueError: If any of the headers are already registered in the client.
206+
"""
207+
self.__core_client.add_headers(headers)

packages/toolbox-langchain/src/toolbox_langchain/client.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,3 +291,18 @@ def load_toolset(
291291
for core_sync_tool in core_sync_tools:
292292
tools.append(ToolboxTool(core_tool=core_sync_tool))
293293
return tools
294+
295+
def add_headers(
296+
self,
297+
headers: Mapping[str, Union[Callable[[], str], Callable[[], Awaitable[str]]]],
298+
) -> None:
299+
"""
300+
Add headers to be included in each request sent through this client.
301+
302+
Args:
303+
headers: Headers to include in each request sent through this client.
304+
305+
Raises:
306+
ValueError: If any of the headers are already registered in the client.
307+
"""
308+
self.__core_client.add_headers(headers)

packages/toolbox-langchain/tests/test_client.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,3 +429,12 @@ def test_init_with_client_headers(self, mock_core_client_constructor):
429429
mock_core_client_constructor.assert_called_once_with(
430430
url=URL, client_headers=headers
431431
)
432+
433+
@patch("toolbox_langchain.client.ToolboxCoreSyncClient")
434+
def test_add_headers(self, mock_core_client_constructor):
435+
"""Tests that add_headers calls the core client's add_headers."""
436+
mock_core_instance = mock_core_client_constructor.return_value
437+
client = ToolboxClient(URL)
438+
headers = {"X-Another-Header": "dynamic_value"}
439+
client.add_headers(headers)
440+
mock_core_instance.add_headers.assert_called_once_with(headers)

0 commit comments

Comments
 (0)