diff --git a/.github/workflows/agentex-tutorials-test.yml b/.github/workflows/agentex-tutorials-test.yml index 7f2c762dd..19cfc8468 100644 --- a/.github/workflows/agentex-tutorials-test.yml +++ b/.github/workflows/agentex-tutorials-test.yml @@ -124,10 +124,13 @@ jobs: - name: Build AgentEx SDK run: | - echo "🔨 Building AgentEx SDK wheel..." - uv build - echo "✅ SDK built successfully" + echo "🔨 Building slim agentex-sdk-client wheel (root)..." + uv build --wheel + echo "🔨 Building heavy agentex-sdk wheel (adk/)..." + (cd adk && uv build --wheel) + echo "✅ Both SDK wheels built successfully" ls -la dist/ + ls -la adk/dist/ - name: Test Tutorial id: run-test diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b29d7f3ab..5d25b41d9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,11 +32,14 @@ jobs: RYE_INSTALL_OPTION: '--yes' - name: Install dependencies - run: rye sync --all-features + run: ./scripts/bootstrap - name: Run lints run: ./scripts/lint + - name: Check slim dependency set + run: ./scripts/check-slim-deps + build: if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') timeout-minutes: 10 @@ -57,10 +60,19 @@ jobs: RYE_INSTALL_OPTION: '--yes' - name: Install dependencies - run: rye sync --all-features + run: ./scripts/bootstrap + + - name: Run build (slim agentex-sdk-client) + # --wheel only: avoid the sdist intermediate step, which would + # otherwise force the heavy build below to resolve cross-directory + # paths from inside a sdist tarball. + run: rye build --wheel - - name: Run build - run: rye build + - name: Run build (ADK overlay agentex-sdk) + # Heavy wheel uses hatchling force-include to pull + # ../src/agentex/lib into agentex/lib. Building --wheel directly + # (vs sdist-then-wheel) keeps the relative path resolvable. + run: (cd adk && rye build --wheel) - name: Get GitHub OIDC Token if: |- diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index 864901da6..adbf317d4 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -28,4 +28,11 @@ jobs: run: | bash ./bin/publish-pypi env: + # Heavy `agentex-sdk` package token (existing PyPI name). + AGENTEX_PYPI_TOKEN: ${{ secrets.AGENTEX_PYPI_TOKEN }} + # Slim `agentex-sdk-client` package token (new PyPI name; needs + # to be added to repo secrets when the slim is registered). + AGENTEX_SDK_CLIENT_PYPI_TOKEN: ${{ secrets.AGENTEX_SDK_CLIENT_PYPI_TOKEN }} + # Back-compat fallback — used by bin/publish-pypi when the + # dedicated tokens above are unset. PYPI_TOKEN: ${{ secrets.AGENTEX_PYPI_TOKEN || secrets.PYPI_TOKEN }} diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 5a36a4131..e31704a76 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,4 @@ { - ".": "0.11.5" + ".": "0.11.5", + "adk": "0.11.5" } diff --git a/adk/README.md b/adk/README.md new file mode 100644 index 000000000..d3f5e6d8f --- /dev/null +++ b/adk/README.md @@ -0,0 +1,32 @@ +# agentex-sdk + +The Agent Development Kit (ADK) overlay for the Agentex API. + +## What's in here + +This package ships everything under `agentex.lib.*`: + +- **ACP server** (`agentex.lib.sdk.fastacp`) — FastAPI-based agent control plane. +- **Temporal workflows** (`agentex.lib.core.temporal`) — durable agent execution. +- **CLI** (`agentex.lib.cli`) — `agentex init`, `agentex run`, deploy helpers. +- **LLM provider integrations** (`agentex.lib.adk.providers`, `agentex.lib.core.temporal.plugins`) — OpenAI Agents, Claude Agent SDK, pydantic-ai, langgraph, litellm. +- **Observability** (`agentex.lib.core.tracing`, `agentex.lib.core.observability`) — SGP, Datadog, OpenTelemetry tracing processors. + +## Installation + +```sh +pip install agentex-sdk +``` + +This automatically pulls in [`agentex-sdk-client`](../) (the slim Stainless-generated REST client) so `from agentex import Agentex, AsyncAgentex` works the same as before. + +## When to use this vs `agentex-sdk-client` + +- **`agentex-sdk`** — you're authoring agents. Pulls everything: ACP server, Temporal, MCP, LLM providers, observability, CLI. ~37 deps. +- **`agentex-sdk-client`** — you only need to call the Agentex REST API. No agent authoring, no Temporal workflows, no FastACP server, no provider integrations. 6 deps. + +The two packages contribute disjoint files to the `agentex.*` namespace — `agentex/lib/*` ships only from `agentex-sdk`. + +## Repo layout + +This package is hand-authored and lives at `adk/` inside [scaleapi/scale-agentex-python](https://github.com/scaleapi/scale-agentex-python). The Stainless generator preserves `adk/**` via `keep_files` so its codegen never touches anything here. The sibling `agentex-sdk-client` package lives at the repo root and IS Stainless-generated. diff --git a/adk/hatch_build.py b/adk/hatch_build.py new file mode 100644 index 000000000..8baadfe5b --- /dev/null +++ b/adk/hatch_build.py @@ -0,0 +1,41 @@ +"""Builds the agentex/lib force-include map per-file so test files can be pruned +— force-include ignores `exclude` (hatchling #1395).""" + +from __future__ import annotations + +import os + +from hatchling.builders.hooks.plugin.interface import BuildHookInterface + +_SKIP_DIRS = {"__pycache__", "tests"} +_SKIP_NAMES = {"conftest.py", "pytest.ini", "run_tests.py"} +# Floor below the ~333 shippable files: a collapse means the walk broke — fail +# loud rather than ship a near-empty wheel. +_MIN_FILES = 320 + + +def _is_test_file(name: str) -> bool: + return name in _SKIP_NAMES or (name.startswith("test_") and name.endswith(".py")) + + +class CustomBuildHook(BuildHookInterface): + PLUGIN_NAME = "custom" + + def initialize(self, version: str, build_data: dict) -> None: # noqa: ARG002 + lib_root = os.path.normpath(os.path.join(self.root, "..", "src", "agentex", "lib")) + force_include = build_data.setdefault("force_include", {}) + collected = 0 + for dirpath, dirnames, filenames in os.walk(lib_root): + dirnames[:] = [d for d in dirnames if d not in _SKIP_DIRS] + for name in filenames: + if _is_test_file(name): + continue + src = os.path.join(dirpath, name) + rel = os.path.relpath(src, lib_root) + force_include[src] = os.path.join("agentex", "lib", rel) + collected += 1 + if collected < _MIN_FILES: + raise RuntimeError( + f"agentex/lib force-include collected only {collected} files " + f"(expected >= {_MIN_FILES}); aborting build." + ) diff --git a/adk/pyproject.toml b/adk/pyproject.toml new file mode 100644 index 000000000..48f238295 --- /dev/null +++ b/adk/pyproject.toml @@ -0,0 +1,105 @@ +[project] +# Hand-authored ADK overlay for agentex. This package contributes only +# `agentex/lib/*` to the agentex.* namespace; the REST client surface +# (agentex/{__init__.py, _*.py, types/, resources/}) ships from the slim +# sibling package `agentex-sdk-client` which is pinned as a runtime dep. +# +# This entire `adk/` directory must be preserved across Stainless codegen +# via `keep_files: ["adk/**"]` in the Stainless dashboard config. +name = "agentex-sdk" +version = "0.11.5" +description = "Agent Development Kit (ADK) overlay for the Agentex API — FastACP server, Temporal workflows, LLM provider integrations, observability" +license = "Apache-2.0" +authors = [ + { name = "Agentex", email = "roxanne.farhad@scale.com" }, +] +readme = "README.md" + +dependencies = [ + # Co-released in lockstep; floor-only by design — a ceiling would + # eventually exclude the co-versioned slim (release-please can't bump it). + "agentex-sdk-client>=0.11.5", + # CLI surface (agentex.lib.cli.*, agentex.lib.sdk.config.*) + "typer>=0.16,<0.17", + "questionary>=2.0.1,<3", + "rich>=13.9.2,<14", + "yaspin>=3.1.0", + "pyyaml>=6.0.2,<7", + "python-on-whales>=0.73.0,<0.74", + "kubernetes>=25.0.0,<36.0.0", + "jsonref>=1.1.0,<2", + "jsonschema>=4.23.0,<5", + "jinja2>=3.1.3,<4", + "watchfiles>=0.24.0,<1.0", + # ACP server (FastAPI app surface) + "fastapi>=0.115.0", + "starlette>=0.49.1", + "uvicorn>=0.31.1", + "aiohttp>=3.10.10,<4", + # Temporal workflows + "temporalio>=1.26.0,<2", + "cloudpickle>=3.1.1", + # Async streaming infra + "redis>=5.2.0,<8", + # LLM provider integrations + "litellm>=1.83.7,<2", + "openai-agents>=0.14.3,<0.15", + "openai>=2.2,<3", # Required by openai-agents; litellm now supports openai 2.x (issue #13711 resolved: https://github.com/BerriAI/litellm/issues/13711) + "claude-agent-sdk>=0.1.0", + "pydantic-ai-slim>=1.0,<2", + "langgraph-checkpoint>=2.0.0", + "scale-gp>=0.1.0a59", + "scale-gp-beta>=0.2.0", + "mcp>=1.4.1", + # Observability + "ddtrace>=3.13.0", + "opentelemetry-api>=1.20.0", + "opentelemetry-sdk>=1.20.0", + "json_log_formatter>=1.1.1", +] + +# agentex/lib/* uses `from typing import override` (3.12+) in 19 files. +# The slim agentex-sdk-client keeps 3.11 support. +requires-python = ">= 3.12,<4" +classifiers = [ + "Typing :: Typed", + "Intended Audience :: Developers", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + "Operating System :: OS Independent", + "Topic :: Software Development :: Libraries :: Python Modules", + "License :: OSI Approved :: Apache Software License", +] + +[project.urls] +Homepage = "https://github.com/scaleapi/scale-agentex-python" +Repository = "https://github.com/scaleapi/scale-agentex-python" + +[project.scripts] +agentex = "agentex.lib.cli.commands.main:app" + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +# Ship only agentex/lib/*, pulled in from the parent repo's `src/agentex/lib` +# tree. The rest of agentex.* (the Stainless-generated client) ships from the +# sibling agentex-sdk-client package, which this package pins as a runtime dep. +# Stainless explicitly preserves `src/agentex/lib/` across codegen (per +# CONTRIBUTING.md), so it's safe to keep the source where it is. +[tool.hatch.build.targets.wheel] +bypass-selection = true + +# Builds the ../src/agentex/lib force-include map per-file (see hatch_build.py) +# so test files can be pruned — force-include ignores `exclude` (hatchling #1395). +[tool.hatch.build.targets.wheel.hooks.custom] +path = "hatch_build.py" + +# Sdist deferred: hatchling can't represent the wheel's ../src/agentex/lib +# force-include in an sdist include list. CI + bin/publish-pypi pass --wheel. +[tool.hatch.build.targets.sdist] +include = [ + "/pyproject.toml", + "/README.md", +] diff --git a/bin/check-release-environment b/bin/check-release-environment index b845b0f4c..7b8488507 100644 --- a/bin/check-release-environment +++ b/bin/check-release-environment @@ -1,9 +1,21 @@ #!/usr/bin/env bash +# This script is run by Release Doctor to validate the release environment. +# After the dual-package split (slim agentex-sdk-client + heavy agentex-sdk), +# both PyPI tokens must be present — one for each package name. If only +# PYPI_TOKEN is set, fall back to using it for both (back-compat for legacy +# single-token setups, which forces an account-scoped token). + errors=() -if [ -z "${PYPI_TOKEN}" ]; then - errors+=("The PYPI_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets.") +# Heavy `agentex-sdk` token (existing PyPI name). +if [ -z "${AGENTEX_PYPI_TOKEN}" ] && [ -z "${PYPI_TOKEN}" ]; then + errors+=("The AGENTEX_PYPI_TOKEN secret has not been set (and no fallback PYPI_TOKEN). Add it in repo secrets so the heavy 'agentex-sdk' package can be published.") +fi + +# Slim `agentex-sdk-client` token (new PyPI name). +if [ -z "${AGENTEX_SDK_CLIENT_PYPI_TOKEN}" ] && [ -z "${PYPI_TOKEN}" ]; then + errors+=("The AGENTEX_SDK_CLIENT_PYPI_TOKEN secret has not been set (and no fallback PYPI_TOKEN). Add it in repo secrets so the slim 'agentex-sdk-client' package can be published. Falling back to PYPI_TOKEN requires an account-scoped token.") fi lenErrors=${#errors[@]} diff --git a/bin/publish-pypi b/bin/publish-pypi index 826054e92..5a3486a32 100644 --- a/bin/publish-pypi +++ b/bin/publish-pypi @@ -1,6 +1,35 @@ #!/usr/bin/env bash +# Publish both the Stainless-managed slim `agentex-sdk-client` package (built +# from this repo's root) and the hand-authored `agentex-sdk` ADK overlay +# (built from adk/). The two packages contribute disjoint files to the same +# `agentex.*` namespace; `agentex-sdk` pins `agentex-sdk-client` as a runtime +# dep so installing `agentex-sdk` transitively pulls in the slim client. +# +# Publish ORDER matters: slim first, then heavy. Heavy's dep on slim means +# anyone installing `agentex-sdk` hits resolver failure if slim hasn't shipped +# yet. Failing the slim publish before the heavy gives us a chance to abort +# without leaving an inconsistent registry state. +# +# Tokens: +# - $AGENTEX_SDK_CLIENT_PYPI_TOKEN — auths publishing the slim client +# - $AGENTEX_PYPI_TOKEN (alias $PYPI_TOKEN for back-compat) — auths +# publishing the heavy ADK overlay (which continues to claim the +# `agentex-sdk` name on PyPI) + set -eux + +# Slim Stainless-managed client (root) — `agentex-sdk-client` on PyPI. mkdir -p dist -rye build --clean -rye publish --yes --token=$PYPI_TOKEN +rye build --clean --wheel # --wheel: sdist deferred (see adk/pyproject.toml). +rye publish --yes --skip-existing --token="${AGENTEX_SDK_CLIENT_PYPI_TOKEN:-$PYPI_TOKEN}" + +# Heavy ADK overlay (adk/) — `agentex-sdk` on PyPI; pins the slim above. +( + cd adk + mkdir -p dist + # --wheel load-bearing: rye's sdist-then-wheel default can't resolve + # the force-include of ../src/agentex/lib → silent empty wheel. + rye build --clean --wheel + rye publish --yes --skip-existing --token="${AGENTEX_PYPI_TOKEN:-$PYPI_TOKEN}" +) diff --git a/examples/tutorials/run_agent_test.sh b/examples/tutorials/run_agent_test.sh index c6fd17960..8d0912d6f 100755 --- a/examples/tutorials/run_agent_test.sh +++ b/examples/tutorials/run_agent_test.sh @@ -126,18 +126,26 @@ start_agent() { if [ "$BUILD_CLI" = true ]; then - # Use wheel from dist directory at repo root - local wheel_file=$(ls /home/runner/work/*/*/dist/agentex_sdk-*.whl 2>/dev/null | head -n1) - if [[ -z "$wheel_file" ]]; then - echo -e "${RED}❌ No built wheel found in dist/agentex_sdk-*.whl${NC}" - echo -e "${YELLOW}💡 Please build the local SDK first by running: uv build${NC}" - echo -e "${YELLOW}💡 From the repo root directory${NC}" + # Heavy ADK wheel ships from adk/dist/; slim client wheel ships from dist/. + # We need both: heavy pins agentex-sdk-client which isn't on PyPI yet, + # so uv must resolve both from local wheels rather than the registry. + local heavy_wheel=$(ls /home/runner/work/*/*/adk/dist/agentex_sdk-*.whl 2>/dev/null | head -n1) + local slim_wheel=$(ls /home/runner/work/*/*/dist/agentex_sdk_client-*.whl 2>/dev/null | head -n1) + if [[ -z "$heavy_wheel" ]]; then + echo -e "${RED}❌ No built heavy wheel found in adk/dist/agentex_sdk-*.whl${NC}" + echo -e "${YELLOW}💡 Build it first: (cd adk && uv build --wheel)${NC}" + cd "$original_dir" + return 1 + fi + if [[ -z "$slim_wheel" ]]; then + echo -e "${RED}❌ No built slim wheel found in dist/agentex_sdk_client-*.whl${NC}" + echo -e "${YELLOW}💡 Build it first: uv build --wheel${NC}" cd "$original_dir" return 1 fi - # Use the built wheel - uv run --with "$wheel_file" agentex agents run --manifest "$manifest_path" > "$logfile" 2>&1 & + # Pass both wheels so the local heavy resolves its slim dep locally + uv run --with "$heavy_wheel" --with "$slim_wheel" agentex agents run --manifest "$manifest_path" > "$logfile" 2>&1 & else uv run agentex agents run --manifest manifest.yaml > "$logfile" 2>&1 & fi @@ -269,14 +277,20 @@ run_test() { # robust across all tutorials regardless of how each declares test deps. local -a pytest_cmd=("uv" "run" "--with" "pytest" "--with" "pytest-asyncio" "pytest") if [ "$BUILD_CLI" = true ]; then - local wheel_file - wheel_file=$(ls /home/runner/work/*/*/dist/agentex_sdk-*.whl 2>/dev/null | head -n1) - if [[ -z "$wheel_file" ]]; then - wheel_file=$(ls "${SCRIPT_DIR}/../../dist/agentex_sdk-*.whl" 2>/dev/null | head -n1) + local heavy_wheel slim_wheel + heavy_wheel=$(ls /home/runner/work/*/*/adk/dist/agentex_sdk-*.whl 2>/dev/null | head -n1) + if [[ -z "$heavy_wheel" ]]; then + heavy_wheel=$(ls "${SCRIPT_DIR}"/../../adk/dist/agentex_sdk-*.whl 2>/dev/null | head -n1) + fi + slim_wheel=$(ls /home/runner/work/*/*/dist/agentex_sdk_client-*.whl 2>/dev/null | head -n1) + if [[ -z "$slim_wheel" ]]; then + slim_wheel=$(ls "${SCRIPT_DIR}"/../../dist/agentex_sdk_client-*.whl 2>/dev/null | head -n1) fi - if [[ -n "$wheel_file" ]]; then - pytest_cmd=("uv" "run" "--with" "$wheel_file" "--with" "pytest" "--with" "pytest-asyncio" "pytest") + if [[ -z "$heavy_wheel" || -z "$slim_wheel" ]]; then + echo -e "${RED}❌ BUILD_CLI=true but a wheel is missing (heavy='${heavy_wheel}' slim='${slim_wheel}'); refusing to test against the pre-installed SDK${NC}" + return 1 fi + pytest_cmd=("uv" "run" "--with" "$heavy_wheel" "--with" "$slim_wheel" "--with" "pytest" "--with" "pytest-asyncio" "pytest") fi local max_retries=5 @@ -350,7 +364,7 @@ execute_tutorial_test() { fi } -# Function to check if built wheel is available +# Function to check if both built wheels are available check_built_wheel() { # Navigate to the repo root (two levels up from examples/tutorials) @@ -362,19 +376,26 @@ check_built_wheel() { return 1 } - # Check if wheel exists in dist directory at repo root - local wheel_file=$(ls /home/runner/work/*/*/dist/agentex_sdk-*.whl 2>/dev/null | head -n1) - if [[ -z "$wheel_file" ]]; then - echo -e "${RED}❌ No built wheel found in dist/agentex_sdk-*.whl${NC}" - echo -e "${YELLOW}💡 Please build the local SDK first by running: uv build${NC}" - echo -e "${YELLOW}💡 From the repo root directory${NC}" + # Heavy ADK wheel + slim client wheel — we need both because heavy pins + # agentex-sdk-client which isn't on PyPI yet. + local heavy_wheel=$(ls /home/runner/work/*/*/adk/dist/agentex_sdk-*.whl 2>/dev/null | head -n1) + local slim_wheel=$(ls /home/runner/work/*/*/dist/agentex_sdk_client-*.whl 2>/dev/null | head -n1) + if [[ -z "$heavy_wheel" ]]; then + echo -e "${RED}❌ No built heavy wheel found in adk/dist/agentex_sdk-*.whl${NC}" + echo -e "${YELLOW}💡 Build it first: (cd adk && uv build --wheel)${NC}" + cd "$original_dir" + return 1 + fi + if [[ -z "$slim_wheel" ]]; then + echo -e "${RED}❌ No built slim wheel found in dist/agentex_sdk_client-*.whl${NC}" + echo -e "${YELLOW}💡 Build it first: uv build --wheel${NC}" cd "$original_dir" return 1 fi - # Test the wheel by running agentex --help - if ! uv run --with "$wheel_file" agentex --help >/dev/null 2>&1; then - echo -e "${RED}❌ Failed to run agentex with built wheel${NC}" + # Test the heavy wheel by running agentex --help (uses both wheels for resolution) + if ! uv run --with "$heavy_wheel" --with "$slim_wheel" agentex --help >/dev/null 2>&1; then + echo -e "${RED}❌ Failed to run agentex with built wheels${NC}" cd "$original_dir" return 1 fi diff --git a/pyproject.toml b/pyproject.toml index 7cf27ff4e..ac85b3701 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,15 @@ [project] -name = "agentex-sdk" +# This is the Stainless-generated REST client. The hand-authored ADK +# overlay (formerly `src/agentex/lib/*`) now lives in `adk/` and ships +# as the sibling `agentex-sdk` package — see `adk/pyproject.toml`. +# +# Stainless dashboard config must: +# - Rename `package_name` from `agentex-sdk` to `agentex-sdk-client` +# - Reduce the dep list to the 6 bare-client deps below +# - Add `adk/**` to `keep_files` so the ADK overlay persists across codegen +name = "agentex-sdk-client" version = "0.11.5" -description = "The official Python library for the agentex API" +description = "The official Python REST client for the Agentex API" dynamic = ["readme"] license = "Apache-2.0" authors = [ @@ -15,37 +23,6 @@ dependencies = [ "anyio>=3.5.0, <5", "distro>=1.7.0, <2", "sniffio", - "typer>=0.16,<0.17", - "questionary>=2.0.1,<3", - "rich>=13.9.2,<14", - "fastapi>=0.115.0", - "starlette>=0.49.1", - "uvicorn>=0.31.1", - "watchfiles>=0.24.0,<1.0", - "python-on-whales>=0.73.0,<0.74", - "pyyaml>=6.0.2,<7", - "jsonschema>=4.23.0,<5", - "jsonref>=1.1.0,<2", - "temporalio>=1.26.0,<2", - "aiohttp>=3.10.10,<4", - "redis>=5.2.0,<8", - "litellm>=1.83.7,<2", - "kubernetes>=25.0.0,<36.0.0", - "jinja2>=3.1.3,<4", - "mcp>=1.4.1", - "scale-gp>=0.1.0a59", - "openai-agents>=0.14.3,<0.15", - "pydantic-ai-slim>=1.0,<2", - "json_log_formatter>=1.1.1", - "scale-gp-beta>=0.2.0", - "openai>=2.2,<3", # Required by openai-agents; litellm now supports openai 2.x (issue #13711 resolved: https://github.com/BerriAI/litellm/issues/13711) - "cloudpickle>=3.1.1", - "ddtrace>=3.13.0", - "yaspin>=3.1.0", - "claude-agent-sdk>=0.1.0", - "langgraph-checkpoint>=2.0.0", - "opentelemetry-sdk>=1.20.0", - "opentelemetry-api>=1.20.0", ] requires-python = ">= 3.11,<4" @@ -75,8 +52,14 @@ dev = [ "ruff>=0.3.4", ] -[project.scripts] -agentex = "agentex.lib.cli.commands.main:app" +# The `agentex` CLI entry point ships from the ADK package — see +# `adk/pyproject.toml`. The slim client has no CLI surface. + +[tool.rye.workspace] +# `rye sync` from the root installs both this slim package and the ADK +# overlay editably. `src/agentex/lib/` is shared source: the slim wheel +# excludes it; the heavy wheel includes it via hatchling force-include. +members = ["adk"] [tool.rye] managed = true @@ -144,13 +127,11 @@ include = [ [tool.hatch.build.targets.wheel] packages = ["src/agentex"] -# Don't ship internal test files in the wheel. `lib/cli/templates/**/test_agent.py.j2` -# is intentionally kept — those render into user projects. +# agentex/lib/* ships from the sibling agentex-sdk package (see adk/pyproject.toml). +# Excluding it here keeps the slim wheel disjoint from the heavy wheel so both +# can install into the same site-packages/agentex/ without file conflicts. exclude = [ - "src/agentex/lib/**/tests/**", - "src/agentex/lib/**/test_*.py", - "src/agentex/lib/**/conftest.py", - "src/agentex/lib/**/pytest.ini", + "src/agentex/lib/**", ] [tool.hatch.build.targets.sdist] @@ -205,6 +186,8 @@ exclude = [ # Exclude autogenerated Stainless code from type checking "src/agentex/resources", "src/agentex/types", + # Build-time hook; imports hatchling, a build dep absent from the synced env. + "adk/hatch_build.py", ] reportImplicitOverride = true diff --git a/release-please-config.json b/release-please-config.json index 52d4c546d..d49b37199 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -1,10 +1,18 @@ { "packages": { - ".": {} + ".": { + "component": "agentex-sdk-client", + "extra-files": [ + "src/agentex/_version.py" + ] + }, + "adk": { + "component": "agentex-sdk" + } }, "$schema": "https://raw.githubusercontent.com/stainless-api/release-please/main/schemas/config.json", "include-v-in-tag": true, - "include-component-in-tag": false, + "include-component-in-tag": true, "versioning": "prerelease", "prerelease": true, "bump-minor-pre-major": true, @@ -59,8 +67,5 @@ "hidden": true } ], - "release-type": "python", - "extra-files": [ - "src/agentex/_version.py" - ] + "release-type": "python" } \ No newline at end of file diff --git a/requirements-dev.lock b/requirements-dev.lock index dc7ee59b8..f1473c57d 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -10,10 +10,13 @@ # universal: false -e file:. + # via agentex-sdk +-e file:adk aiohappyeyeballs==2.6.1 # via aiohttp aiohttp==3.13.5 # via agentex-sdk + # via agentex-sdk-client # via httpx-aiohttp # via litellm aiosignal==1.4.0 @@ -22,11 +25,8 @@ annotated-doc==0.0.4 # via fastapi annotated-types==0.7.0 # via pydantic -anthropic==0.86.0 - # via agentex-sdk anyio==4.12.1 - # via agentex-sdk - # via anthropic + # via agentex-sdk-client # via claude-agent-sdk # via httpx # via mcp @@ -36,12 +36,8 @@ anyio==4.12.1 # via sse-starlette # via starlette # via watchfiles -appnope==0.1.4 - # via ipykernel argcomplete==3.6.3 # via nox -asttokens==3.0.1 - # via stack-data attrs==25.4.0 # via aiohttp # via jsonschema @@ -68,38 +64,26 @@ cloudpickle==3.1.2 # via agentex-sdk colorlog==6.10.1 # via nox -comm==0.2.3 - # via ipykernel cryptography==46.0.6 # via google-auth # via pyjwt -datadog==0.52.1 - # via agentex-sdk ddtrace==4.6.4 # via agentex-sdk debugpy==1.8.20 - # via ipykernel -decorator==5.2.1 - # via ipython dependency-groups==1.3.1 # via nox dirty-equals==0.11 distlib==0.4.0 # via virtualenv distro==1.9.0 - # via agentex-sdk - # via anthropic + # via agentex-sdk-client # via openai # via scale-gp # via scale-gp-beta -docstring-parser==0.17.0 - # via anthropic envier==0.6.1 # via ddtrace execnet==2.1.2 # via pytest-xdist -executing==2.2.1 - # via stack-data fastapi==0.136.1 # via agentex-sdk fastuuid==0.14.0 @@ -127,8 +111,7 @@ hf-xet==1.4.2 httpcore==1.0.9 # via httpx httpx==0.28.1 - # via agentex-sdk - # via anthropic + # via agentex-sdk-client # via genai-prices # via httpx-aiohttp # via huggingface-hub @@ -142,7 +125,7 @@ httpx==0.28.1 # via scale-gp # via scale-gp-beta httpx-aiohttp==0.1.12 - # via agentex-sdk + # via agentex-sdk-client httpx-sse==0.4.3 # via mcp huggingface-hub==1.8.0 @@ -159,19 +142,10 @@ importlib-metadata==8.5.0 # via opentelemetry-api iniconfig==2.1.0 # via pytest -ipykernel==7.2.0 - # via agentex-sdk -ipython==9.12.0 - # via ipykernel -ipython-pygments-lexers==1.1.1 - # via ipython -jedi==0.19.2 - # via ipython jinja2==3.1.6 # via agentex-sdk # via litellm jiter==0.13.0 - # via anthropic # via openai json-log-formatter==1.1.1 # via agentex-sdk @@ -187,11 +161,6 @@ jsonschema==4.23.0 # via mcp jsonschema-specifications==2025.9.1 # via jsonschema -jupyter-client==8.8.0 - # via ipykernel -jupyter-core==5.9.1 - # via ipykernel - # via jupyter-client kubernetes==28.1.0 # via agentex-sdk langchain-core==1.2.23 @@ -208,9 +177,6 @@ markdown-it-py==3.0.0 # via rich markupsafe==3.0.3 # via jinja2 -matplotlib-inline==0.2.1 - # via ipykernel - # via ipython mcp==1.26.0 # via agentex-sdk # via claude-agent-sdk @@ -224,7 +190,6 @@ mypy==1.17.0 mypy-extensions==1.1.0 # via mypy nest-asyncio==1.6.0 - # via ipykernel nexus-rpc==1.4.0 # via temporalio nodeenv==1.10.0 @@ -256,36 +221,23 @@ ormsgpack==1.12.2 packaging==25.0 # via dependency-groups # via huggingface-hub - # via ipykernel # via langchain-core # via langsmith # via nox # via pytest -parso==0.8.6 - # via jedi pathspec==1.0.3 # via mypy -pexpect==4.9.0 - # via ipython platformdirs==4.4.0 - # via jupyter-core # via virtualenv pluggy==1.6.0 # via pytest prompt-toolkit==3.0.52 - # via ipython # via questionary propcache==0.4.1 # via aiohttp # via yarl protobuf==6.33.6 # via temporalio -psutil==7.2.2 - # via ipykernel -ptyprocess==0.7.0 - # via pexpect -pure-eval==0.2.3 - # via stack-data pyasn1==0.6.3 # via pyasn1-modules pyasn1-modules==0.4.2 @@ -293,8 +245,7 @@ pyasn1-modules==0.4.2 pycparser==3.0 # via cffi pydantic==2.12.5 - # via agentex-sdk - # via anthropic + # via agentex-sdk-client # via fastapi # via genai-prices # via langchain-core @@ -318,27 +269,21 @@ pydantic-graph==1.101.0 pydantic-settings==2.13.1 # via mcp pygments==2.19.2 - # via ipython - # via ipython-pygments-lexers # via pytest # via rich pyjwt==2.12.1 # via mcp pyright==1.1.399 pytest==8.4.2 - # via agentex-sdk # via pytest-asyncio # via pytest-xdist pytest-asyncio==1.2.0 - # via agentex-sdk pytest-xdist==3.8.0 python-dateutil==2.9.0.post0 - # via jupyter-client # via kubernetes # via time-machine python-dotenv==1.0.1 # via litellm - # via mcp # via pydantic-settings python-multipart==0.0.22 # via mcp @@ -349,9 +294,6 @@ pyyaml==6.0.3 # via huggingface-hub # via kubernetes # via langchain-core -pyzmq==27.1.0 - # via ipykernel - # via jupyter-client questionary==2.1.1 # via agentex-sdk redis==7.4.0 @@ -362,7 +304,6 @@ referencing==0.37.0 regex==2026.3.32 # via tiktoken requests==2.33.1 - # via datadog # via kubernetes # via langsmith # via openai-agents @@ -382,7 +323,7 @@ rpds-py==0.30.0 # via jsonschema # via referencing ruff==0.14.13 - # via agentex-sdk + # via agentex-sdk-client scale-gp==0.1.0a61 # via agentex-sdk scale-gp-beta==0.2.0 @@ -393,15 +334,12 @@ six==1.17.0 # via kubernetes # via python-dateutil sniffio==1.3.1 - # via agentex-sdk - # via anthropic + # via agentex-sdk-client # via openai # via scale-gp # via scale-gp-beta sse-starlette==3.0.3 # via mcp -stack-data==0.6.3 - # via ipython starlette==1.0.0 # via agentex-sdk # via fastapi @@ -417,24 +355,13 @@ tiktoken==0.12.0 time-machine==2.19.0 tokenizers==0.22.2 # via litellm -tornado==6.5.5 - # via agentex-sdk - # via ipykernel - # via jupyter-client tqdm==4.67.3 # via huggingface-hub # via openai # via python-on-whales -traitlets==5.14.3 - # via ipykernel - # via ipython - # via jupyter-client - # via jupyter-core - # via matplotlib-inline typer==0.16.1 # via agentex-sdk # via huggingface-hub - # via mcp # via python-on-whales types-protobuf==6.32.1.20260221 # via temporalio @@ -443,9 +370,8 @@ types-requests==2.31.0.6 types-urllib3==1.26.25.14 # via types-requests typing-extensions==4.15.0 - # via agentex-sdk + # via agentex-sdk-client # via aiosignal - # via anthropic # via anyio # via fastapi # via huggingface-hub @@ -477,10 +403,6 @@ typing-inspection==0.4.2 # via pydantic-ai-slim # via pydantic-graph # via pydantic-settings -tzdata==2025.3 - # via agentex-sdk -tzlocal==5.3.1 - # via agentex-sdk urllib3==1.26.20 # via kubernetes # via requests diff --git a/requirements.lock b/requirements.lock index daea78837..3be4368aa 100644 --- a/requirements.lock +++ b/requirements.lock @@ -10,10 +10,13 @@ # universal: false -e file:. + # via agentex-sdk +-e file:adk aiohappyeyeballs==2.6.1 # via aiohttp aiohttp==3.13.5 # via agentex-sdk + # via agentex-sdk-client # via httpx-aiohttp # via litellm aiosignal==1.4.0 @@ -22,11 +25,8 @@ annotated-doc==0.0.4 # via fastapi annotated-types==0.7.0 # via pydantic -anthropic==0.86.0 - # via agentex-sdk anyio==4.12.1 - # via agentex-sdk - # via anthropic + # via agentex-sdk-client # via claude-agent-sdk # via httpx # via mcp @@ -36,10 +36,6 @@ anyio==4.12.1 # via sse-starlette # via starlette # via watchfiles -appnope==0.1.4 - # via ipykernel -asttokens==3.0.1 - # via stack-data attrs==25.4.0 # via aiohttp # via jsonschema @@ -63,31 +59,18 @@ click==8.1.8 # via uvicorn cloudpickle==3.1.2 # via agentex-sdk -comm==0.2.3 - # via ipykernel cryptography==46.0.6 # via google-auth # via pyjwt -datadog==0.52.1 - # via agentex-sdk ddtrace==4.6.4 # via agentex-sdk -debugpy==1.8.20 - # via ipykernel -decorator==5.2.1 - # via ipython distro==1.9.0 - # via agentex-sdk - # via anthropic + # via agentex-sdk-client # via openai # via scale-gp # via scale-gp-beta -docstring-parser==0.17.0 - # via anthropic envier==0.6.1 # via ddtrace -executing==2.2.1 - # via stack-data fastapi==0.136.1 # via agentex-sdk fastuuid==0.14.0 @@ -114,8 +97,7 @@ hf-xet==1.4.2 httpcore==1.0.9 # via httpx httpx==0.28.1 - # via agentex-sdk - # via anthropic + # via agentex-sdk-client # via genai-prices # via httpx-aiohttp # via huggingface-hub @@ -128,7 +110,7 @@ httpx==0.28.1 # via scale-gp # via scale-gp-beta httpx-aiohttp==0.1.12 - # via agentex-sdk + # via agentex-sdk-client httpx-sse==0.4.3 # via mcp huggingface-hub==1.8.0 @@ -141,21 +123,10 @@ idna==3.11 importlib-metadata==8.5.0 # via litellm # via opentelemetry-api -iniconfig==2.3.0 - # via pytest -ipykernel==7.2.0 - # via agentex-sdk -ipython==9.12.0 - # via ipykernel -ipython-pygments-lexers==1.1.1 - # via ipython -jedi==0.19.2 - # via ipython jinja2==3.1.6 # via agentex-sdk # via litellm jiter==0.13.0 - # via anthropic # via openai json-log-formatter==1.1.1 # via agentex-sdk @@ -171,11 +142,6 @@ jsonschema==4.23.0 # via mcp jsonschema-specifications==2025.9.1 # via jsonschema -jupyter-client==8.8.0 - # via ipykernel -jupyter-core==5.9.1 - # via ipykernel - # via jupyter-client kubernetes==28.1.0 # via agentex-sdk langchain-core==1.2.23 @@ -192,9 +158,6 @@ markdown-it-py==4.0.0 # via rich markupsafe==3.0.3 # via jinja2 -matplotlib-inline==0.2.1 - # via ipykernel - # via ipython mcp==1.26.0 # via agentex-sdk # via claude-agent-sdk @@ -204,8 +167,6 @@ mdurl==0.1.2 multidict==6.7.0 # via aiohttp # via yarl -nest-asyncio==1.6.0 - # via ipykernel nexus-rpc==1.4.0 # via temporalio oauthlib==3.3.1 @@ -233,32 +194,15 @@ ormsgpack==1.12.2 # via langgraph-checkpoint packaging==26.0 # via huggingface-hub - # via ipykernel # via langchain-core # via langsmith - # via pytest -parso==0.8.6 - # via jedi -pexpect==4.9.0 - # via ipython -platformdirs==4.9.4 - # via jupyter-core -pluggy==1.6.0 - # via pytest prompt-toolkit==3.0.52 - # via ipython # via questionary propcache==0.4.1 # via aiohttp # via yarl protobuf==6.33.6 # via temporalio -psutil==7.2.2 - # via ipykernel -ptyprocess==0.7.0 - # via pexpect -pure-eval==0.2.3 - # via stack-data pyasn1==0.6.3 # via pyasn1-modules pyasn1-modules==0.4.2 @@ -266,8 +210,7 @@ pyasn1-modules==0.4.2 pycparser==3.0 # via cffi pydantic==2.12.5 - # via agentex-sdk - # via anthropic + # via agentex-sdk-client # via fastapi # via genai-prices # via langchain-core @@ -291,23 +234,13 @@ pydantic-graph==1.101.0 pydantic-settings==2.13.1 # via mcp pygments==2.20.0 - # via ipython - # via ipython-pygments-lexers - # via pytest # via rich pyjwt==2.12.1 # via mcp -pytest==9.0.2 - # via agentex-sdk - # via pytest-asyncio -pytest-asyncio==1.3.0 - # via agentex-sdk python-dateutil==2.9.0.post0 - # via jupyter-client # via kubernetes python-dotenv==1.0.1 # via litellm - # via mcp # via pydantic-settings python-multipart==0.0.22 # via mcp @@ -318,9 +251,6 @@ pyyaml==6.0.3 # via huggingface-hub # via kubernetes # via langchain-core -pyzmq==27.1.0 - # via ipykernel - # via jupyter-client questionary==2.1.1 # via agentex-sdk redis==7.4.0 @@ -331,7 +261,6 @@ referencing==0.37.0 regex==2026.3.32 # via tiktoken requests==2.33.1 - # via datadog # via kubernetes # via langsmith # via openai-agents @@ -350,7 +279,7 @@ rpds-py==0.30.0 # via jsonschema # via referencing ruff==0.15.8 - # via agentex-sdk + # via agentex-sdk-client scale-gp==0.1.0a61 # via agentex-sdk scale-gp-beta==0.2.0 @@ -361,15 +290,12 @@ six==1.17.0 # via kubernetes # via python-dateutil sniffio==1.3.1 - # via agentex-sdk - # via anthropic + # via agentex-sdk-client # via openai # via scale-gp # via scale-gp-beta sse-starlette==3.0.3 # via mcp -stack-data==0.6.3 - # via ipython starlette==1.0.0 # via agentex-sdk # via fastapi @@ -384,24 +310,13 @@ tiktoken==0.12.0 # via litellm tokenizers==0.22.2 # via litellm -tornado==6.5.5 - # via agentex-sdk - # via ipykernel - # via jupyter-client tqdm==4.67.3 # via huggingface-hub # via openai # via python-on-whales -traitlets==5.14.3 - # via ipykernel - # via ipython - # via jupyter-client - # via jupyter-core - # via matplotlib-inline typer==0.16.1 # via agentex-sdk # via huggingface-hub - # via mcp # via python-on-whales types-protobuf==6.32.1.20260221 # via temporalio @@ -410,9 +325,8 @@ types-requests==2.31.0.6 types-urllib3==1.26.25.14 # via types-requests typing-extensions==4.15.0 - # via agentex-sdk + # via agentex-sdk-client # via aiosignal - # via anthropic # via anyio # via fastapi # via huggingface-hub @@ -426,7 +340,6 @@ typing-extensions==4.15.0 # via opentelemetry-semantic-conventions # via pydantic # via pydantic-core - # via pytest-asyncio # via python-on-whales # via referencing # via scale-gp @@ -442,10 +355,6 @@ typing-inspection==0.4.2 # via pydantic-ai-slim # via pydantic-graph # via pydantic-settings -tzdata==2025.3 - # via agentex-sdk -tzlocal==5.3.1 - # via agentex-sdk urllib3==1.26.20 # via kubernetes # via requests diff --git a/scripts/check-slim-deps b/scripts/check-slim-deps new file mode 100755 index 000000000..8727c7fab --- /dev/null +++ b/scripts/check-slim-deps @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# Guardrail: the slim agentex-sdk-client must keep exactly its 6 bare-client deps +# — Stainless re-emitting an un-trimmed dashboard dep-list would break the split. + +set -e + +cd "$(dirname "$0")/.." + +python3 - <<'PY' +import re +import sys +import tomllib + +EXPECTED = {"httpx", "pydantic", "typing-extensions", "anyio", "distro", "sniffio"} + + +def norm(name: str) -> str: + return re.sub(r"[-_.]+", "-", name.strip().lower()) + + +with open("pyproject.toml", "rb") as f: + deps = tomllib.load(f)["project"]["dependencies"] + +got = {norm(re.split(r"[<>=!~ \[;]", d, maxsplit=1)[0]) for d in deps} +expected = {norm(n) for n in EXPECTED} + +if got != expected: + print("slim dependency drift in root pyproject.toml!", file=sys.stderr) + print(f" expected ({len(expected)}): {sorted(expected)}", file=sys.stderr) + print(f" got ({len(got)}): {sorted(got)}", file=sys.stderr) + print( + "If Stainless re-added ADK deps, trim the dashboard dep-list " + "(ADK deps belong in adk/pyproject.toml).", + file=sys.stderr, + ) + sys.exit(1) + +print(f"slim deps OK ({len(got)}): {sorted(got)}") +PY diff --git a/tests/test_function_tool.py b/tests/test_function_tool.py index 91312e227..484ce8af2 100644 --- a/tests/test_function_tool.py +++ b/tests/test_function_tool.py @@ -6,7 +6,7 @@ import pytest from pydantic import ValidationError -from src.agentex.lib.core.temporal.activities.adk.providers.openai_activities import ( # type: ignore[import-untyped] +from agentex.lib.core.temporal.activities.adk.providers.openai_activities import ( FunctionTool, )