From 266e1484d41e5d78e27813c5ca53b6b985f9389a Mon Sep 17 00:00:00 2001 From: Kurtis Van Gent <31518063+kurtisvg@users.noreply.github.com> Date: Mon, 24 Mar 2025 01:58:21 +0000 Subject: [PATCH 1/4] chore: create init module --- src/toolbox_base_sdk/__init__.py | 16 ++++++++++++++++ src/toolbox_base_sdk/requirements.txt | 0 2 files changed, 16 insertions(+) create mode 100644 src/toolbox_base_sdk/__init__.py create mode 100644 src/toolbox_base_sdk/requirements.txt diff --git a/src/toolbox_base_sdk/__init__.py b/src/toolbox_base_sdk/__init__.py new file mode 100644 index 00000000..81d66965 --- /dev/null +++ b/src/toolbox_base_sdk/__init__.py @@ -0,0 +1,16 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +__all__ = [] diff --git a/src/toolbox_base_sdk/requirements.txt b/src/toolbox_base_sdk/requirements.txt new file mode 100644 index 00000000..e69de29b From a647c5840d8719476f973118c4ac179889855c2a Mon Sep 17 00:00:00 2001 From: Kurtis Van Gent <31518063+kurtisvg@users.noreply.github.com> Date: Wed, 26 Mar 2025 02:16:11 +0000 Subject: [PATCH 2/4] feat: add basic toolbox-core implementation --- .gitignore | 8 + packages/toolbox-core/pyproject.toml | 8 +- packages/toolbox-core/requirements.txt | 2 + .../toolbox-core/src/toolbox_core/__init__.py | 4 +- .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 292 bytes .../__pycache__/client.cpython-313.pyc | Bin 0 -> 6285 bytes .../__pycache__/protocol.cpython-313.pyc | Bin 0 -> 2776 bytes .../test_client.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 11551 bytes .../__pycache__/tool.cpython-313.pyc | Bin 0 -> 3777 bytes .../toolbox-core/src/toolbox_core/client.py | 146 +++++++++++++++++- .../toolbox-core/src/toolbox_core/protocol.py | 72 +++++++++ .../toolbox-core/src/toolbox_core/py.typed | 13 ++ .../toolbox-core/src/toolbox_core/tool.py | 96 ++++++++++++ packages/toolbox-core/tests/test_client.py | 108 +++++++++++++ packages/toolbox-core/tests/test_core.py | 20 --- src/toolbox_base_sdk/__init__.py | 16 -- src/toolbox_base_sdk/requirements.txt | 0 17 files changed, 448 insertions(+), 45 deletions(-) create mode 100644 packages/toolbox-core/src/toolbox_core/__pycache__/__init__.cpython-313.pyc create mode 100644 packages/toolbox-core/src/toolbox_core/__pycache__/client.cpython-313.pyc create mode 100644 packages/toolbox-core/src/toolbox_core/__pycache__/protocol.cpython-313.pyc create mode 100644 packages/toolbox-core/src/toolbox_core/__pycache__/test_client.cpython-313-pytest-8.3.5.pyc create mode 100644 packages/toolbox-core/src/toolbox_core/__pycache__/tool.cpython-313.pyc create mode 100644 packages/toolbox-core/src/toolbox_core/protocol.py create mode 100644 packages/toolbox-core/src/toolbox_core/tool.py create mode 100644 packages/toolbox-core/tests/test_client.py delete mode 100644 packages/toolbox-core/tests/test_core.py delete mode 100644 src/toolbox_base_sdk/__init__.py delete mode 100644 src/toolbox_base_sdk/requirements.txt diff --git a/.gitignore b/.gitignore index 04fd2c0b..3705b174 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,11 @@ # vscode .vscode/ + +# python +env +venv +*.pyc +.python-version +**.egg-info/ + diff --git a/packages/toolbox-core/pyproject.toml b/packages/toolbox-core/pyproject.toml index 980360ae..f6931322 100644 --- a/packages/toolbox-core/pyproject.toml +++ b/packages/toolbox-core/pyproject.toml @@ -9,9 +9,10 @@ authors = [ {name = "Google LLC", email = "googleapis-packages@google.com"} ] -# TODO: Add deps -#dependencies = [ -#] +dependencies = [ + "pydantic>=2.7.0,<3.0.0", + "aiohttp>=3.8.6,<4.0.0", +] classifiers = [ "Intended Audience :: Developers", @@ -43,6 +44,7 @@ test = [ "isort==6.0.1", "mypy==1.15.0", "pytest==8.3.5", + "aioresponses==0.7.8" ] [build-system] requires = ["setuptools"] diff --git a/packages/toolbox-core/requirements.txt b/packages/toolbox-core/requirements.txt index e69de29b..439ae59e 100644 --- a/packages/toolbox-core/requirements.txt +++ b/packages/toolbox-core/requirements.txt @@ -0,0 +1,2 @@ +aiohttp==3.11.14 +pydantic==2.10.6 diff --git a/packages/toolbox-core/src/toolbox_core/__init__.py b/packages/toolbox-core/src/toolbox_core/__init__.py index 433463de..882b2046 100644 --- a/packages/toolbox-core/src/toolbox_core/__init__.py +++ b/packages/toolbox-core/src/toolbox_core/__init__.py @@ -12,6 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -from .client import DummyClass +from .client import ToolboxClient -__all__ = ["DummyClass"] +__all__ = ["ToolboxClient"] diff --git a/packages/toolbox-core/src/toolbox_core/__pycache__/__init__.cpython-313.pyc b/packages/toolbox-core/src/toolbox_core/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f33ac7fea17758b3d2ab83dac53833215c172998 GIT binary patch literal 292 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 H%^CVP*RfO% literal 0 HcmV?d00001 diff --git a/packages/toolbox-core/src/toolbox_core/__pycache__/protocol.cpython-313.pyc b/packages/toolbox-core/src/toolbox_core/__pycache__/protocol.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c688f3949593d71e12da8583032997119aeec1f4 GIT binary patch literal 2776 zcma)7%}*Og6rZ)%_IhmtDPIne=#oImmV(^ie+ z8n^_thbAYS0}4?OJ?EC5`fq?NHP#5JhqQ;>h#=~<@9lbRpo!>6`{wPN_h#Pw=6&p? zQZj*IeD^Z{ON@}0_!Epso!PU1*&rIxgb6Y(FhM~+G!YsPv+%gcMDIU55gCuNs6x`@ zG||K!qD8XN3t@i`7W1T0kj4&3B~L1WG=4xD&&z7!6&k7nkKZzwu|!?U)DV}ZN^Zfn zj3O6jR!S6?uNn@$Y3FFs^iT3>{aCX1Bm8fWEMbC1SV$9CcmP~yViug!!h^8tabkmu zAg4uHEGuQ>*$85QA85zwD{@xS0-5B=;@QOckS6z#??sk`tEAvB;r+E}N!FT=swv*u zxQAvg(k0_nC+xb~EBk-Sg3S~|ac#S(l!6P7$XvAG?p$=?vOJNaj>!rh0+T#pl-)(m zE;ExlJW_D!lEb5nx@BgW0&E=&5g0^c&jslQnI+S~%7IxEE`0+*5G7ihcV|62*?kGVYy#Nj@{Mk&vO;UkOi(aTBLWTL3Knj*^0pFab z+napAU8pk{2mNFoAI=+E?Cof_kaMBtiK;ZoqmIi8R-VTSmP_-L@yJ5aHe4RN0}oCO zi$@H`jFr_-XDxcaL`|3Glx3q>rk50adZpkfgT;d54k~xbuHrp5$|h}#uAZE=oN}pT zGcMZ(lX?NgMcki9bw83~uC04PC*MXl zt}|w{X95=;T3m1tOocCJ$_~pEZPO@b^0u8X(#)d0L^F4n^O+xPcGoExCe7rjWfX=n zuJ73Qhl_@lHy4e9HC$S87i}v8I(Lmcbuxj5X)~H}m>CFkPf%jEYnyg)q_o0iUC&e3 z_pawiQjIj%6mZgc0%VQU60Pf_k4878YC_p*?|L|1OPyGsdo;JHR#RsV2?ndFzMb~; z!yAV+x*tvi0@SFrwy&=~THS8#+v$3LOPQ&5&2F{L?zDGrioYhFCSIh6w$nq^^ze53 z$X0UXua@-Isq@cgexLho?)k*l$;;K2D_hc)J#dX2%2dn{FH>{CY>@dz7DJRZvJ$QQ zF&{8wx{-$s5#~j#`4@0OBx}NGxSyz@NvNQ*VOh3oU|m&3h7K^yDBgK%N*kLQo0`0G zL!X+~C$7(ZJvDue_vlA3Z$i#`!Oo8krr;J5M3Gm`c>0LZNU-uTny5h5$WCjzmTX%e zdo;F_?0k_N+D;DDx=%fM@c2Qs`|RtO*e351F)p(+p#6-fZCuiIERVX5F|lOl%0=Xp zx_+;06zeOky1r0gj$15P)UshYrt3M|)OFT{9>Q|-`VL63XfVW+^&r74VVL&}3%!rd zS>Sl5t~&^tsT(fjHl!s*^AAxA9rOTTSjaqrHN&|LLz1hF%JXN<(^jF29nmW98{o%!}j)CpO2UT%+S8Rz~6*jN!6P$OINJQRL z{~<8>N8)KFe2syRiDU08Ajfn;=y&x#=k>x2VexFt?|;zyg03j%W2(gE8OY~)8@mWw zuXg;mjg81G2}@k|0s#BB!;{#vrs=(Mfzh0Y)g;uYAy8uC2CCvftyfvQQR%NZkJUd!#Rm^f-wqyb zcr194taLtf5I`-$;?VeGyyd206&9%D)&aW;+pV5H2$;J_6+{#&q_R|3hTflYC|joN zHXsMN$s<@IojPjr|3Hn4xq|6>n7kD+yca`)9yyr1O~g97t6~?_%}TiPZKZWPp)_zp zgGp7!R_0)*@^XSlp;E`9E&?-%ScC}ZJs zuvG1a1wnYzON8D(N#Eb3cR#8Ka;30OVA}suhzWg_llugw{f;Q`b8iSt-gEv7683N7 literal 0 HcmV?d00001 diff --git a/packages/toolbox-core/src/toolbox_core/__pycache__/test_client.cpython-313-pytest-8.3.5.pyc b/packages/toolbox-core/src/toolbox_core/__pycache__/test_client.cpython-313-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f83c1efb163c9d8c3f77661d9aad93b52ab47266 GIT binary patch literal 11551 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 ToolboxTool: + """Internal helper to create a callable tool from its schema.""" + tool = ToolboxTool( + session=self.__session, + base_url=self.__base_url, + name=name, + desc=schema.description, + params=[p.to_param() for p in schema.parameters], + ) + return tool + + async def __aenter__(self): + """ + Enter the runtime context related to this client instance. + + Allows the client to be used as an asynchronous context manager + (e.g., `async with ToolboxClient(...) as client:`). + + Returns: + self: The client instance itself. + """ + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + """ + Exit the runtime context and close the internally managed session. + + Allows the client to be used as an asynchronous context manager + (e.g., `async with ToolboxClient(...) as client:`). + """ + await self.close() + + async def close(self): + """ + Asynchronously closes the underlying client session. Doing so will cause + any tools created by this Client to cease to function. + + If the session was provided externally during initialization, the caller + is responsible for its lifecycle, but calling close here will still + attempt to close it. + """ + await self.__session.close() + + async def load_tool( + self, + name: str, + ) -> ToolboxTool: + """ + Asynchronously loads a tool from the server. + + Retrieves the schema for the specified tool from the Toolbox server and + returns a callable object (`ToolboxTool`) that can be used to invoke the + tool remotely. + + Args: + name: The unique name or identifier of the tool to load. + + Returns: + ToolboxTool: A callable object representing the loaded tool, ready + for execution. The specific arguments and behavior of the callable + depend on the tool itself. + + """ + + # request the definition of the tool from the server + url = f"{self.__base_url}/api/tool/{name}" + async with self.__session.get(url) as response: + json = await response.json() + manifest: ManifestSchema = ManifestSchema(**json) + + # parse the provided definition to a tool + if name not in manifest.tools: + # TODO: Better exception + raise Exception(f"Tool '{name}' not found!") + tool = self.__parse_tool(name, manifest.tools[name]) + + return tool + + async def load_toolset( + self, + name: str, + ) -> list[ToolboxTool]: + """ + Asynchronously fetches a toolset and loads all tools defined within it. + + Args: + name: Name of the toolset to load tools. + + Returns: + list[ToolboxTool]: A list of callables, one for each tool defined + in the toolset. + """ + # Request the definition of the tool from the server + url = f"{self.__base_url}/api/toolset/{name}" + async with self.__session.get(url) as response: + json = await response.json() + manifest: ManifestSchema = ManifestSchema(**json) + + # parse each tools name and schema into a list of ToolboxTools + tools = [self.__parse_tool(n, s) for n, s in manifest.tools.items()] + return tools diff --git a/packages/toolbox-core/src/toolbox_core/protocol.py b/packages/toolbox-core/src/toolbox_core/protocol.py new file mode 100644 index 00000000..5f1cc86d --- /dev/null +++ b/packages/toolbox-core/src/toolbox_core/protocol.py @@ -0,0 +1,72 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from inspect import Parameter +from typing import Optional, Type + +from pydantic import BaseModel + + +class ParameterSchema(BaseModel): + """ + Schema for a tool parameter. + """ + + name: str + type: str + description: str + authSources: Optional[list[str]] = None + items: Optional["ParameterSchema"] = None + + def __get_type(self) -> Type: + if self.type == "string": + return str + elif self.type == "integer": + return int + elif self.type == "float": + return float + elif self.type == "boolean": + return bool + elif self.type == "array": + if self.items is None: + raise Exception("Unexpected value: type is 'list' but items is None") + return list[self._items.to_type()] # type: ignore + + raise ValueError(f"Unsupported schema type: {self.type}") + + def to_param(self) -> Parameter: + return Parameter( + self.name, + Parameter.POSITIONAL_OR_KEYWORD, + annotation=self.__get_type(), + ) + + +class ToolSchema(BaseModel): + """ + Schema for a tool. + """ + + description: str + parameters: list[ParameterSchema] + authRequired: list[str] = [] + + +class ManifestSchema(BaseModel): + """ + Schema for the Toolbox manifest. + """ + + serverVersion: str + tools: dict[str, ToolSchema] diff --git a/packages/toolbox-core/src/toolbox_core/py.typed b/packages/toolbox-core/src/toolbox_core/py.typed index e69de29b..0a2669d7 100644 --- a/packages/toolbox-core/src/toolbox_core/py.typed +++ b/packages/toolbox-core/src/toolbox_core/py.typed @@ -0,0 +1,13 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/packages/toolbox-core/src/toolbox_core/tool.py b/packages/toolbox-core/src/toolbox_core/tool.py new file mode 100644 index 00000000..48e4626c --- /dev/null +++ b/packages/toolbox-core/src/toolbox_core/tool.py @@ -0,0 +1,96 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +from inspect import Parameter, Signature +from typing import Any + +from aiohttp import ClientSession + + +class ToolboxTool: + """ + A callable proxy object representing a specific tool on a remote Toolbox server. + + Instances of this class behave like asynchronous functions. When called, they + send a request to the corresponding tool's endpoint on the Toolbox server with + the provided arguments. + + It utilizes Python's introspection features (`__name__`, `__doc__`, + `__signature__`, `__annotations__`) so that standard tools like `help()` + and `inspect` work as expected. + """ + + __url: str + __session: ClientSession + __signature__: Signature + + def __init__( + self, + session: ClientSession, + base_url: str, + name: str, + desc: str, + params: list[Parameter], + ): + """ + Initializes a callable that will trigger the tool invocation through the Toolbox server. + + Args: + session: The `aiohttp.ClientSession` used for making API requests. + base_url: The base URL of the Toolbox server API. + name: The name of the remote tool. + desc: The description of the remote tool (used as its docstring). + params: A list of `inspect.Parameter` objects defining the tool's + arguments and their types/defaults. + """ + + # used to invoke the toolbox API + self.__session = session + self.__url = f"{base_url}/api/tool/{name}/invoke" + + # the following properties are set to help anyone that might inspect it determine + self.__name__ = name + self.__doc__ = desc + self.__signature__ = Signature(parameters=params, return_annotation=str) + self.__annotations__ = {p.name: p.annotation for p in params} + # TODO: self.__qualname__ ?? + + async def __call__(self, *args: Any, **kwargs: Any) -> str: + """ + Asynchronously calls the remote tool with the provided arguments. + + Validates arguments against the tool's signature, then sends them + as a JSON payload in a POST request to the tool's invoke URL. + + Args: + *args: Positional arguments for the tool. + **kwargs: Keyword arguments for the tool. + + Returns: + The string result returned by the remote tool execution. + """ + all_args = self.__signature__.bind(*args, **kwargs) + all_args.apply_defaults() # Include default values if not provided + payload = all_args.arguments + + async with self.__session.post( + self.__url, + json=payload, + ) as resp: + ret = await resp.json() + if "error" in ret: + # TODO: better error + raise Exception(ret["error"]) + return ret.get("result", ret) diff --git a/packages/toolbox-core/tests/test_client.py b/packages/toolbox-core/tests/test_client.py new file mode 100644 index 00000000..b19c575b --- /dev/null +++ b/packages/toolbox-core/tests/test_client.py @@ -0,0 +1,108 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import inspect + +import pytest + +from toolbox_core import ToolboxClient +from toolbox_core.protocol import ManifestSchema, ParameterSchema, ToolSchema + +TEST_BASE_URL = "http://toolbox.example.com" + + +@pytest.fixture() +def test_tool_str(): + return ToolSchema( + description="Test Tool 1 Description", + parameters=[ + ParameterSchema( + name="param1", type="string", description="Description of Param1" + ) + ], + ) + + +@pytest.fixture() +def test_tool_int_bool(): + """Fixture for the second test tool schema.""" + return ToolSchema( + description="Test Tool 2 Description", + parameters=[ + ParameterSchema(name="argA", type="integer", description="Argument A"), + ParameterSchema(name="argB", type="boolean", description="Argument B"), + ], + ) + + +@pytest.mark.asyncio +async def test_load_tool_success(aioresponses, test_tool_str): + """ + Tests successfully loading a tool when the API returns a valid manifest. + """ + # Mock out responses from server + TOOL_NAME = "test_tool_1" + manifest = ManifestSchema(serverVersion="0.0.0", tools={TOOL_NAME: test_tool_str}) + aioresponses.get( + f"{TEST_BASE_URL}/api/tool/{TOOL_NAME}", + payload=manifest.model_dump(), + status=200, + ) + aioresponses.post( + f"{TEST_BASE_URL}/api/tool/{TOOL_NAME}/invoke", + payload={"result": "ok"}, + status=200, + ) + + async with ToolboxClient(TEST_BASE_URL) as client: + # Load a Tool + loaded_tool = await client.load_tool(TOOL_NAME) + + # Assertions + assert callable(loaded_tool) + # Assert introspection attributes are set correctly + assert loaded_tool.__name__ == TOOL_NAME + assert loaded_tool.__doc__ == test_tool_str.description + + # Assert signature inspection + sig = inspect.signature(loaded_tool) + assert list(sig.parameters.keys()) == [p.name for p in test_tool_str.parameters] + + assert await loaded_tool("some value") == "ok" + + +@pytest.mark.asyncio +async def test_load_toolset_success(aioresponses, test_tool_str, test_tool_int_bool): + """Tests successfully loading a toolset with multiple tools.""" + TOOLSET_NAME = "my_toolset" + TOOL1 = "tool1" + TOOL2 = "tool2" + manifest = ManifestSchema( + serverVersion="0.0.0", tools={TOOL1: test_tool_str, TOOL2: test_tool_int_bool} + ) + aioresponses.get( + f"{TEST_BASE_URL}/api/toolset/{TOOLSET_NAME}", + payload=manifest.model_dump(), + status=200, + ) + + async with ToolboxClient(TEST_BASE_URL) as client: + tools = await client.load_toolset(TOOLSET_NAME) + + assert isinstance(tools, list) + assert len(tools) == len(manifest.tools) + + # Check if tools were created correctly + assert {t.__name__ for t in tools} == manifest.tools.keys() diff --git a/packages/toolbox-core/tests/test_core.py b/packages/toolbox-core/tests/test_core.py deleted file mode 100644 index 76ce659f..00000000 --- a/packages/toolbox-core/tests/test_core.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright 2025 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from toolbox_core import DummyClass - - -class TestToolboxCore: - def test_dummy(self): - dummy_variable = DummyClass() - assert dummy_variable.val == "dummy value" diff --git a/src/toolbox_base_sdk/__init__.py b/src/toolbox_base_sdk/__init__.py deleted file mode 100644 index 81d66965..00000000 --- a/src/toolbox_base_sdk/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright 2025 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -__all__ = [] diff --git a/src/toolbox_base_sdk/requirements.txt b/src/toolbox_base_sdk/requirements.txt deleted file mode 100644 index e69de29b..00000000 From 553b73715314f279fd17f3aa567de3aaf71ac697 Mon Sep 17 00:00:00 2001 From: Kurtis Van Gent <31518063+kurtisvg@users.noreply.github.com> Date: Wed, 26 Mar 2025 02:19:14 +0000 Subject: [PATCH 3/4] chore: remove pycache --- .gitignore | 1 + .../__pycache__/__init__.cpython-313.pyc | Bin 292 -> 0 bytes .../__pycache__/client.cpython-313.pyc | Bin 6285 -> 0 bytes .../__pycache__/protocol.cpython-313.pyc | Bin 2776 -> 0 bytes .../test_client.cpython-313-pytest-8.3.5.pyc | Bin 11551 -> 0 bytes .../__pycache__/tool.cpython-313.pyc | Bin 3777 -> 0 bytes 6 files changed, 1 insertion(+) delete mode 100644 packages/toolbox-core/src/toolbox_core/__pycache__/__init__.cpython-313.pyc delete mode 100644 packages/toolbox-core/src/toolbox_core/__pycache__/client.cpython-313.pyc delete mode 100644 packages/toolbox-core/src/toolbox_core/__pycache__/protocol.cpython-313.pyc delete mode 100644 packages/toolbox-core/src/toolbox_core/__pycache__/test_client.cpython-313-pytest-8.3.5.pyc delete mode 100644 packages/toolbox-core/src/toolbox_core/__pycache__/tool.cpython-313.pyc diff --git a/.gitignore b/.gitignore index 3705b174..a5c5ce0f 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ venv *.pyc .python-version **.egg-info/ +__pycache__/** diff --git a/packages/toolbox-core/src/toolbox_core/__pycache__/__init__.cpython-313.pyc b/packages/toolbox-core/src/toolbox_core/__pycache__/__init__.cpython-313.pyc deleted file mode 100644 index f33ac7fea17758b3d2ab83dac53833215c172998..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 292 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 H%^CVP*RfO% diff --git a/packages/toolbox-core/src/toolbox_core/__pycache__/protocol.cpython-313.pyc b/packages/toolbox-core/src/toolbox_core/__pycache__/protocol.cpython-313.pyc deleted file mode 100644 index c688f3949593d71e12da8583032997119aeec1f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2776 zcma)7%}*Og6rZ)%_IhmtDPIne=#oImmV(^ie+ z8n^_thbAYS0}4?OJ?EC5`fq?NHP#5JhqQ;>h#=~<@9lbRpo!>6`{wPN_h#Pw=6&p? zQZj*IeD^Z{ON@}0_!Epso!PU1*&rIxgb6Y(FhM~+G!YsPv+%gcMDIU55gCuNs6x`@ zG||K!qD8XN3t@i`7W1T0kj4&3B~L1WG=4xD&&z7!6&k7nkKZzwu|!?U)DV}ZN^Zfn zj3O6jR!S6?uNn@$Y3FFs^iT3>{aCX1Bm8fWEMbC1SV$9CcmP~yViug!!h^8tabkmu zAg4uHEGuQ>*$85QA85zwD{@xS0-5B=;@QOckS6z#??sk`tEAvB;r+E}N!FT=swv*u zxQAvg(k0_nC+xb~EBk-Sg3S~|ac#S(l!6P7$XvAG?p$=?vOJNaj>!rh0+T#pl-)(m zE;ExlJW_D!lEb5nx@BgW0&E=&5g0^c&jslQnI+S~%7IxEE`0+*5G7ihcV|62*?kGVYy#Nj@{Mk&vO;UkOi(aTBLWTL3Knj*^0pFab z+napAU8pk{2mNFoAI=+E?Cof_kaMBtiK;ZoqmIi8R-VTSmP_-L@yJ5aHe4RN0}oCO zi$@H`jFr_-XDxcaL`|3Glx3q>rk50adZpkfgT;d54k~xbuHrp5$|h}#uAZE=oN}pT zGcMZ(lX?NgMcki9bw83~uC04PC*MXl zt}|w{X95=;T3m1tOocCJ$_~pEZPO@b^0u8X(#)d0L^F4n^O+xPcGoExCe7rjWfX=n zuJ73Qhl_@lHy4e9HC$S87i}v8I(Lmcbuxj5X)~H}m>CFkPf%jEYnyg)q_o0iUC&e3 z_pawiQjIj%6mZgc0%VQU60Pf_k4878YC_p*?|L|1OPyGsdo;JHR#RsV2?ndFzMb~; z!yAV+x*tvi0@SFrwy&=~THS8#+v$3LOPQ&5&2F{L?zDGrioYhFCSIh6w$nq^^ze53 z$X0UXua@-Isq@cgexLho?)k*l$;;K2D_hc)J#dX2%2dn{FH>{CY>@dz7DJRZvJ$QQ zF&{8wx{-$s5#~j#`4@0OBx}NGxSyz@NvNQ*VOh3oU|m&3h7K^yDBgK%N*kLQo0`0G zL!X+~C$7(ZJvDue_vlA3Z$i#`!Oo8krr;J5M3Gm`c>0LZNU-uTny5h5$WCjzmTX%e zdo;F_?0k_N+D;DDx=%fM@c2Qs`|RtO*e351F)p(+p#6-fZCuiIERVX5F|lOl%0=Xp zx_+;06zeOky1r0gj$15P)UshYrt3M|)OFT{9>Q|-`VL63XfVW+^&r74VVL&}3%!rd zS>Sl5t~&^tsT(fjHl!s*^AAxA9rOTTSjaqrHN&|LLz1hF%JXN<(^jF29nmW98{o%!}j)CpO2UT%+S8Rz~6*jN!6P$OINJQRL z{~<8>N8)KFe2syRiDU08Ajfn;=y&x#=k>x2VexFt?|;zyg03j%W2(gE8OY~)8@mWw zuXg;mjg81G2}@k|0s#BB!;{#vrs=(Mfzh0Y)g;uYAy8uC2CCvftyfvQQR%NZkJUd!#Rm^f-wqyb zcr194taLtf5I`-$;?VeGyyd206&9%D)&aW;+pV5H2$;J_6+{#&q_R|3hTflYC|joN zHXsMN$s<@IojPjr|3Hn4xq|6>n7kD+yca`)9yyr1O~g97t6~?_%}TiPZKZWPp)_zp zgGp7!R_0)*@^XSlp;E`9E&?-%ScC}ZJs zuvG1a1wnYzON8D(N#Eb3cR#8Ka;30OVA}suhzWg_llugw{f;Q`b8iSt-gEv7683N7 diff --git a/packages/toolbox-core/src/toolbox_core/__pycache__/test_client.cpython-313-pytest-8.3.5.pyc b/packages/toolbox-core/src/toolbox_core/__pycache__/test_client.cpython-313-pytest-8.3.5.pyc deleted file mode 100644 index f83c1efb163c9d8c3f77661d9aad93b52ab47266..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11551 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 Date: Wed, 26 Mar 2025 02:21:18 +0000 Subject: [PATCH 4/4] chore: correct aioresponses module --- packages/toolbox-core/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/toolbox-core/pyproject.toml b/packages/toolbox-core/pyproject.toml index f6931322..edc45a8a 100644 --- a/packages/toolbox-core/pyproject.toml +++ b/packages/toolbox-core/pyproject.toml @@ -44,7 +44,7 @@ test = [ "isort==6.0.1", "mypy==1.15.0", "pytest==8.3.5", - "aioresponses==0.7.8" + "pytest-aioresponses==0.3.0" ] [build-system] requires = ["setuptools"]