### Overview Major restructuring from monolithic `arcade-ai` package to modular library architecture with standardized uv-based dependency management.  ### New Package Structure - **`arcade-tdk`** - Lightweight toolkit development kit (core decorators, auth) - **`arcade-core`** - Core execution engine and catalog functionality - **`arcade-serve`** - FastAPI/MCP server components - **`arcade-ai`** - Meta package that includes CLI functionality. Optionally include evals via the `evals` extra. Optionally include all packages via the `all` extra. ### Key Benefits - **Lighter Dependencies**: Toolkits now depend only on `arcade-tdk` (~2 deps) vs full `arcade-ai` (~30+ deps) - **Faster Builds**: uv provides 10-100x faster dependency resolution and installation - **Better Modularity**: Clear separation of concerns, consumers import only what they need - **Standard Tooling**: Eliminates custom poetry scripts, uses standard Python packaging ### Migration Impact - All 20 toolkits converted from poetry → uv with `arcade-tdk` dependencies plus `arcade-ai[evals]` and `arcade-serve` dev dependencies. When developing locally, devs should install toolkits via `make install-local`. - Modern Python 3.10+ type hints throughout - Standardized build system with hatchling backend - Enhanced Makefile with robust toolkit management commands - Removed `arcade dev` CLI command - Reduce the number of files created by `arcade new` and add an option to not generate a tests and evals folder. This foundation enables faster development cycles and cleaner dependency chains for the growing toolkit ecosystem. ### Todo After this PR is merged - [ ] Post-merge workflow(s) (release & publish containers, etc) - [ ] Release order plan. @EricGustin suggests releasing in the following order: 1. `arcade-core` version 0.1.0 2. `arcade-serve` version 0.1.0 and `arcade-tdk` version 0.1.0 3. `arcade-ai` version 2.0.0 4. Patch release for all toolkits (all changes in toolkits are internal refactors) - [ ] [Update docs](https://github.com/ArcadeAI/docs/pull/318) --------- Co-authored-by: Eric Gustin <eric@arcade.dev> Co-authored-by: Eric Gustin <34000337+EricGustin@users.noreply.github.com>
110 lines
4.6 KiB
Python
110 lines
4.6 KiB
Python
import logging
|
|
from pathlib import Path
|
|
from typing import Union, get_args
|
|
|
|
from stripe_agent_toolkit.functions import * # noqa: F403
|
|
from stripe_agent_toolkit.prompts import * # noqa: F403
|
|
from stripe_agent_toolkit.schema import * # noqa: F403
|
|
from stripe_agent_toolkit.tools import tools
|
|
|
|
logging.basicConfig(level=logging.INFO)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def get_type_str(arg_type):
|
|
"""Extract type name, handling Optional/Union types."""
|
|
if hasattr(arg_type, "__origin__") and arg_type.__origin__ is Union:
|
|
non_none = [a for a in get_args(arg_type) if a is not type(None)]
|
|
if len(non_none) == 1:
|
|
return non_none[0].__name__
|
|
return arg_type.__name__ if hasattr(arg_type, "__name__") else str(arg_type)
|
|
|
|
|
|
def generate_stripe_tools(
|
|
output_file: Path = Path("arcade_stripe") / "tools" / "stripe.py",
|
|
) -> None:
|
|
"""
|
|
Generate the Arcade AI Stripe Toolkit file from the stripe agent toolkit definitions.
|
|
"""
|
|
logger.info("Generating stripe tools file at %s", output_file)
|
|
try:
|
|
output_file.touch(exist_ok=True)
|
|
with output_file.open("w") as f:
|
|
f.write("""import os
|
|
from typing import Annotated, Optional
|
|
|
|
from stripe_agent_toolkit.api import StripeAPI
|
|
|
|
from arcade_tdk import ToolContext, tool
|
|
|
|
def run_stripe_tool(context: ToolContext, method_name: str, params: dict) -> str:
|
|
\"\"\"
|
|
Helper function that retrieves the Stripe secret key, initializes the API,
|
|
and executes the specified method with the provided parameters.
|
|
\"\"\"
|
|
api_key = context.get_secret("STRIPE_SECRET_KEY")
|
|
stripe_api = StripeAPI(secret_key=api_key, context=None)
|
|
params = {k: v for k, v in params.items() if v is not None}
|
|
return stripe_api.run(method_name, **params) # type: ignore[no-any-return]
|
|
|
|
""")
|
|
# Generate each tool function from the stripe agent toolkit
|
|
for tool_info in tools:
|
|
method_name = tool_info["method"]
|
|
method = globals().get(method_name)
|
|
if not method:
|
|
logger.warning("Method %s not found.", method_name)
|
|
continue
|
|
|
|
args_schema = tool_info["args_schema"]
|
|
description = tool_info["description"].strip()
|
|
|
|
arg_names = list(args_schema.__annotations__.keys())
|
|
arg_types = [args_schema.__annotations__[field] for field in arg_names]
|
|
|
|
params_list = []
|
|
for name, arg_type in zip(arg_names, arg_types, strict=False):
|
|
field = args_schema.model_fields[name]
|
|
# Check if the type annotation already includes Optional (i.e. Union[..., None])
|
|
is_optional_type = (
|
|
hasattr(arg_type, "__origin__")
|
|
and arg_type.__origin__ is Union
|
|
and type(None) in get_args(arg_type)
|
|
)
|
|
if field.is_required:
|
|
if is_optional_type:
|
|
params_list.append(
|
|
f"{name}: Annotated[{get_type_str(arg_type)} | None, "
|
|
f'"{field.description}"] = None'
|
|
)
|
|
else:
|
|
params_list.append(
|
|
f"{name}: Annotated[{get_type_str(arg_type)}, "
|
|
f'"{field.description}"]'
|
|
)
|
|
else:
|
|
default_repr = "None" if field.default is None else repr(field.default)
|
|
params_list.append(
|
|
f"{name}: Annotated[Optional[{get_type_str(arg_type)}], "
|
|
f'"{field.description}"] = {default_repr}'
|
|
)
|
|
params_str = ", ".join(params_list)
|
|
dict_items = ", ".join([f'"{name}": {name}' for name in arg_names])
|
|
arcade_tool_code = (
|
|
f'@tool(requires_secrets=["STRIPE_SECRET_KEY"])\n'
|
|
f"def {method_name}(context: ToolContext, {params_str}) -> "
|
|
f'Annotated[str, "{description.splitlines()[0]}"]:\n'
|
|
f' """{description.splitlines()[0]}"""\n'
|
|
f' return run_stripe_tool(context, "{method_name}", '
|
|
+ "{"
|
|
+ dict_items
|
|
+ "})\n\n"
|
|
)
|
|
f.write(arcade_tool_code)
|
|
except Exception:
|
|
logger.exception("An error occurred while generating stripe tools")
|
|
raise
|
|
|
|
|
|
if __name__ == "__main__":
|
|
generate_stripe_tools()
|