Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 7d09b32

Browse filesBrowse files
authored
add get accounts from cloud + lint (#39)
* add linting CI * lint with ruff and fix types * lint both projects * use uv workspaces to handle deps * uv.lock using dev deps * update pyright command * fix types
1 parent a58d05a commit 7d09b32
Copy full SHA for 7d09b32

File tree

Expand file treeCollapse file tree

14 files changed

+9861
-43
lines changed
Filter options
Expand file treeCollapse file tree

14 files changed

+9861
-43
lines changed

‎.github/workflows/lint.yml

Copy file name to clipboard
+54Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
name: Linter
2+
3+
on: [push, pull_request]
4+
5+
env:
6+
PYTHON_VERSION: "3.12"
7+
UV_VERSION: "0.7.8"
8+
UV_PROJECT_ENVIRONMENT: .venv
9+
RUFF_VERSION: "0.11.2"
10+
11+
concurrency:
12+
group: ${{ github.workflow }}-${{ github.ref }}
13+
cancel-in-progress: true
14+
15+
jobs:
16+
check:
17+
strategy:
18+
fail-fast: true
19+
matrix:
20+
project:
21+
- thirdweb-ai
22+
- thirdweb-mcp
23+
24+
name: Linter (${{ matrix.project }})
25+
runs-on: ubuntu-latest
26+
defaults:
27+
run:
28+
working-directory: ./python/${{ matrix.project }}
29+
steps:
30+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
31+
32+
- name: Install uv
33+
uses: astral-sh/setup-uv@22695119d769bdb6f7032ad67b9bca0ef8c4a174 # v5
34+
35+
- name: Install Python
36+
uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
37+
with:
38+
cache: true
39+
cache-dependency-path: "python/${{ matrix.project }}/uv.lock" # Update cache if uv.lock changes
40+
41+
- name: Install the project
42+
run: uv sync --group dev --all-extras
43+
44+
- name: Configure path
45+
run: echo "$PWD/.venv/bin" >> $GITHUB_PATH
46+
47+
- name: Run Ruff linter
48+
run: uv run ruff check --output-format=github .
49+
50+
- name: Run Ruff formatter
51+
run: uv run ruff format --check .
52+
53+
- name: Run Pyright check
54+
run: uv run pyright src/

‎python/pyproject.toml

Copy file name to clipboard
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[tool.uv.workspace]
2+
members = ["thirdweb-ai", "thirdweb-mcp"]

‎python/thirdweb-ai/pyproject.toml

Copy file name to clipboardExpand all lines: python/thirdweb-ai/pyproject.toml
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ dev = [
4949
"pytest-mock>=3.12.0,<4",
5050
"pytest-cov>=4.1.0,<5",
5151
"ipython>=8.34.0",
52+
"thirdweb-mcp",
5253
]
5354

5455
[tool.hatch.build.targets.sdist]
@@ -88,6 +89,9 @@ vcs = "git"
8889
style = "semver"
8990
format = "{base}"
9091

92+
[tool.uv.sources]
93+
thirdweb-mcp = { workspace = true }
94+
9195
[tool.pyright]
9296
include = ["src"]
9397
typeCheckingMode = "strict"

‎python/thirdweb-ai/src/thirdweb_ai/adapters/google_adk/google_adk.py

Copy file name to clipboardExpand all lines: python/thirdweb-ai/src/thirdweb_ai/adapters/google_adk/google_adk.py
+24-28Lines changed: 24 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1+
import copy
12
from typing import Any
23

34
from google.adk.tools import BaseTool, ToolContext
45
from google.genai import types
56

67
from thirdweb_ai.tools.tool import Tool, ToolSchema
78

8-
from pydantic import BaseModel
99

1010
class GoogleAdkTool(BaseTool):
1111
"""Adapter for Thirdweb Tool to Google ADK BaseTool.
@@ -40,38 +40,34 @@ def _get_declaration(self) -> types.FunctionDeclaration:
4040
Returns:
4141
A FunctionDeclaration for Google ADK
4242
"""
43-
# Deep copy the parameters to avoid modifying the original
44-
import copy
45-
parameters = copy.deepcopy(self.tool.schema["parameters"])
46-
47-
if "additionalProperties" in parameters:
48-
del parameters["additionalProperties"]
49-
50-
def remove_additional_properties(obj: dict[str, Any]):
51-
if "additionalProperties" in obj:
52-
del obj["additionalProperties"]
53-
54-
if "items" in obj and isinstance(obj["items"], dict):
55-
remove_additional_properties(obj["items"])
56-
57-
if "properties" in obj and isinstance(obj["properties"], dict):
58-
for prop in obj["properties"].values():
59-
if isinstance(prop, dict):
60-
remove_additional_properties(prop)
61-
62-
if "properties" in parameters:
63-
for prop in parameters["properties"].values():
64-
remove_additional_properties(prop)
65-
43+
44+
if "parameters" not in self.tool.schema:
45+
raise ValueError("Tool schema must contain 'parameters'.")
46+
47+
# Create a clean parameters dict without additionalProperties
48+
parameters = copy.deepcopy(dict(self.tool.schema["parameters"]))
49+
50+
# Remove additionalProperties recursively from the entire schema
51+
def clean_schema(obj: dict[str, Any]) -> dict[str, Any]:
52+
cleaned = {k: v for k, v in obj.items() if k != "additionalProperties"}
53+
54+
for key, value in cleaned.items():
55+
if isinstance(value, dict):
56+
cleaned[key] = clean_schema(value)
57+
elif isinstance(value, list):
58+
cleaned[key] = [clean_schema(item) if isinstance(item, dict) else item for item in value]
59+
60+
return cleaned
61+
62+
clean_parameters = clean_schema(parameters)
63+
6664
return types.FunctionDeclaration(
6765
name=self.name,
6866
description=self.description,
69-
parameters=parameters,
67+
parameters=types.Schema(**clean_parameters),
7068
)
7169

72-
# Override the method with the expected signature based on the error message
73-
# and adapting from the reference implementation
74-
async def run_async(self, args: dict[str, Any], tool_context: ToolContext) -> Any:
70+
async def run_async(self, *, args: dict[str, Any], tool_context: ToolContext) -> Any:
7571
"""Execute the tool asynchronously.
7672
7773
This method adapts the Thirdweb tool to work with Google ADK's async execution.

‎python/thirdweb-ai/src/thirdweb_ai/adapters/mcp/mcp.py

Copy file name to clipboardExpand all lines: python/thirdweb-ai/src/thirdweb_ai/adapters/mcp/mcp.py
+6-3Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Any, cast
2+
13
import mcp.types as types
24
from mcp.server.fastmcp import FastMCP
35
from mcp.server.fastmcp.tools.base import Tool as FastMCPTool
@@ -11,26 +13,27 @@ def get_fastmcp_tools(tools: list[Tool]) -> list[FastMCPTool]:
1113
fn=lambda _t=tool, **kwargs: _t.run_json(kwargs),
1214
name=tool.name,
1315
description=tool.description,
14-
parameters=tool.schema.get("parameters"),
16+
parameters=cast(dict[str, Any], tool.schema.get("parameters") or {}),
1517
fn_metadata=func_metadata(tool._func_definition, skip_names=["self"]), # noqa: SLF001
1618
is_async=False,
1719
context_kwarg=None,
20+
annotations=tool.annotations,
1821
)
1922
for tool in tools
2023
]
2124

2225

2326
def add_fastmcp_tools(fastmcp: FastMCP, tools: list[Tool]):
2427
for tool in get_fastmcp_tools(tools):
25-
fastmcp._tool_manager._tools[tool.name] = tool # noqa: SLF001
28+
fastmcp._tool_manager._tools[tool.name] = tool # type: ignore[reportPrivateUsage] # noqa: SLF001
2629

2730

2831
def get_mcp_tools(tools: list[Tool]) -> list[types.Tool]:
2932
return [
3033
types.Tool(
3134
name=tool.name,
3235
description=tool.description,
33-
inputSchema=tool.schema.get("parameters"),
36+
inputSchema=cast(dict[str, Any], tool.schema.get("parameters") or {}),
3437
)
3538
for tool in tools
3639
]

‎python/thirdweb-ai/src/thirdweb_ai/adapters/openai/agents.py

Copy file name to clipboardExpand all lines: python/thirdweb-ai/src/thirdweb_ai/adapters/openai/agents.py
+3-3Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,15 @@ def _get_openai_schema(schema: Any):
2929

3030
def get_agents_tools(tools: list[Tool]):
3131
def _get_tool(tool: Tool):
32-
async def _invoke_tool(ctx: RunContextWrapper[Any], tool_input: str, _t: Tool = tool) -> str: # type: ignore # noqa: PGH003
32+
async def _invoke_tool(ctx: RunContextWrapper[Any], tool_input: str, _t: Tool = tool) -> str:
3333
input_args = json.loads(tool_input) if tool_input else {}
3434
return _t.return_value_as_string(_t.run_json(input_args))
3535

3636
schema = _get_openai_schema(tool.schema.get("parameters"))
37-
return FunctionTool( # type: ignore # noqa: PGH003
37+
return FunctionTool(
3838
name=tool.name,
3939
description=tool.description,
40-
params_json_schema=schema,
40+
params_json_schema=schema or {},
4141
on_invoke_tool=_invoke_tool,
4242
)
4343

‎python/thirdweb-ai/src/thirdweb_ai/adapters/pydantic_ai/pydantic_ai.py

Copy file name to clipboardExpand all lines: python/thirdweb-ai/src/thirdweb_ai/adapters/pydantic_ai/pydantic_ai.py
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ async def execute(**kwargs: Any) -> Any:
1212
return tool.run_json(kwargs)
1313

1414
async def prepare(ctx: RunContext, tool_def: ToolDefinition) -> ToolDefinition:
15+
if "parameters" not in tool.schema:
16+
raise ValueError("Tool schema must contain 'parameters'.")
1517
tool_def.parameters_json_schema = tool.schema["parameters"]
1618
return tool_def
1719

‎python/thirdweb-ai/src/thirdweb_ai/services/__init__.py

Copy file name to clipboardExpand all lines: python/thirdweb-ai/src/thirdweb_ai/services/__init__.py
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55
from thirdweb_ai.services.service import Service
66
from thirdweb_ai.services.storage import Storage
77

8-
__all__ = ["Engine", "EngineCloud", "Insight", "Nebula", "Service", "Storage"]
8+
__all__ = ["Engine", "EngineCloud", "Insight", "Nebula", "Service", "Storage"]

‎python/thirdweb-ai/src/thirdweb_ai/services/engine_cloud.py

Copy file name to clipboardExpand all lines: python/thirdweb-ai/src/thirdweb_ai/services/engine_cloud.py
+11Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,3 +218,14 @@ def search_transactions(
218218
}
219219

220220
return self._post("transactions/search", payload)
221+
222+
@tool(
223+
description="List all engine server wallets for the current project. Returns an array of EOA addresses with their corresponding predicted smart account addresses."
224+
)
225+
def get_accounts(self) -> dict[str, Any]:
226+
"""Get all engine server wallets for the current project.
227+
228+
Returns:
229+
dict containing list of account objects with address and smartAccountAddress
230+
"""
231+
return self._get("accounts")

‎python/thirdweb-ai/src/thirdweb_ai/tools/tool.py

Copy file name to clipboardExpand all lines: python/thirdweb-ai/src/thirdweb_ai/tools/tool.py
+3-6Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ def args_base_model_from_signature(name: str, sig: inspect.Signature) -> type[Ba
7272
fields: dict[str, tuple[type[Any], Any]] = {}
7373
for param_name, param in sig.parameters.items():
7474
# This is handled externally
75-
if param_name == "cancellation_token" or param_name == "self":
75+
if param_name in ["cancellation_token", "self"]:
7676
continue
7777

7878
if param.annotation is inspect.Parameter.empty:
@@ -196,10 +196,7 @@ def return_type(self) -> type[Any]:
196196
return self._return_type
197197

198198
def return_value_as_string(self, value: Any) -> str:
199-
if isinstance(value, BaseModel):
200-
return value.model_dump_json()
201-
202-
return str(value)
199+
return value.model_dump_json() if isinstance(value, BaseModel) else str(value)
203200

204201
@abstractmethod
205202
def run(self, args: ArgsT | None = None) -> ReturnT: ...
@@ -225,7 +222,7 @@ def __init__(
225222
if isinstance(func_definition, functools.partial)
226223
else name or func_definition.__name__
227224
)
228-
args_model = args_base_model_from_signature(func_name + "args", self._signature)
225+
args_model = args_base_model_from_signature(f"{func_name}args", self._signature)
229226
return_type = self._signature.return_annotation
230227
super().__init__(args_model, return_type, func_name, description, strict)
231228

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.