Skip to content

feat: bundle google-cloud-aiplatform for Vertex partner models#2910

Open
abguymon wants to merge 5 commits into
OpenHands:mainfrom
trustle:adam/install-aiplatform-for-vertex-ai
Open

feat: bundle google-cloud-aiplatform for Vertex partner models#2910
abguymon wants to merge 5 commits into
OpenHands:mainfrom
trustle:adam/install-aiplatform-for-vertex-ai

Conversation

@abguymon

@abguymon abguymon commented Apr 21, 2026

Copy link
Copy Markdown

Summary

Adds google-cloud-aiplatform as a required dep of openhands-sdk so LiteLLM's vertex_ai_partner_models handler can route to Vertex AI Model Garden MaaS endpoints — MiniMax, Qwen, Kimi, etc.

Without this dep, requests like vertex_ai/minimaxai/minimax-m2-maas, vertex_ai/qwen/qwen3-coder-*-maas, vertex_ai/moonshotai/kimi-k2-thinking-maas fail at the first completion call with:

litellm.BadRequestError: Vertex_aiException BadRequestError -
vertexai import failed please run `pip install -U "google-cloud-aiplatform>=1.38"`.
Got error: No module named 'vertexai'

What changed

  • openhands-sdk/pyproject.toml: add "google-cloud-aiplatform>=1.38"
  • uv.lock: regenerated (+7 transitive packages)
  • openhands-agent-server/openhands/agent_server/agent-server.spec: use collect_all(...) for the Vertex SDK and its google.cloud.* namespace siblings (vertexai, google.cloud.aiplatform, aiplatform_v1, aiplatform_v1beta1, bigquery, storage, resourcemanager, google.api_core, google.auth, google.rpc, google.genai, proto, grpc_status). Also pin google.rpc.status_pb2 explicitly (gRPC proto stub imported dynamically) and copy google-cloud-aiplatform metadata.

Why collect_all and not just collect_submodules

I first tried the same minimal change as #2373 (just pyproject.toml), then added collect_submodules("vertexai") etc. to the spec. Neither worked at runtime — the bundled binary still failed with ModuleNotFoundError: No module named 'vertexai', then with No module named 'google.rpc.status_pb2' after I hand-injected vertexai.

Root cause: google.cloud.* is a PEP-420 implicit namespace package shared across many distributions. PyInstaller's collect_submodules("google.cloud.aiplatform") doesn't traverse PEP-420 namespaces correctly — the package's dist-info ends up in the bundle (via copy_metadata), but the actual module files do not. collect_all(...) walks the installed package directory directly and pulls in modules + datas + binaries, which is what the bundle needs.

I verified by inspecting the extracted _MEI*/ directory after running the freshly-built binary:

  • _MEI*/vertexai/__init__.py
  • _MEI*/google/cloud/aiplatform/
  • _MEI*/google/rpc/status_pb2.py

Background

This supersedes #2373 (closed without merging earlier this year). That PR only touched pyproject.toml, which would have shipped the dep into the build env but not into the PyInstaller bundle.

Checklist

  • If the PR is changing/adding functionality, are there tests to reflect this? — no new tests; this is a dependency + bundle config change. Existing tests pass.
  • Is the github CI passing?

Test plan

  • CI builds the -python image with vertexai actually present in the bundle (not just the dist-info)
  • Smoke-test a completion against vertex_ai/minimaxai/minimax-m2-maas (or another partner MaaS model) in a deployment with Vertex credentials

@abguymon abguymon force-pushed the adam/install-aiplatform-for-vertex-ai branch from 6bd2f19 to 8c21f2d Compare April 21, 2026 19:42
@all-hands-bot all-hands-bot requested a review from neubig April 23, 2026 12:37
@all-hands-bot

Copy link
Copy Markdown
Collaborator

[Automatic Post]: I have assigned @neubig as a reviewer based on the repository MAINTAINERS file. Thanks in advance for the help!

@neubig

neubig commented Apr 23, 2026

Copy link
Copy Markdown
Member

@OpenHands audit the packages in this PR and make sure that they're appropriate (and don't, e.g. pose supply chain risks). Then consider the increase in total package size based on this. We may want to consider accepting this PR as-is, or we may want to make this an optional dependency. In the latter case, propose an implementation that would allow us to do that.

@openhands-ai

openhands-ai Bot commented Apr 23, 2026

Copy link
Copy Markdown

I'm on it! neubig can track my progress at all-hands.dev

neubig commented Apr 23, 2026

Copy link
Copy Markdown
Member

I audited the dependency changes in this PR.

Packages added / changed

New packages in uv.lock:

  • google-cloud-aiplatform==1.147.0
  • google-cloud-bigquery==3.41.0
  • google-cloud-core==2.5.1
  • google-cloud-resource-manager==1.17.0
  • google-cloud-storage==3.4.1
  • google-crc32c==1.8.0
  • google-resumable-media==2.8.2
  • grpc-google-iam-v1==0.14.4
  • grpcio-status==1.76.0

Existing dependency changes pulled in by the resolver:

  • google-auth 2.41.1 -> 2.49.2
  • google-genai 1.52.0 -> 1.73.0
  • rsa dropped from the lockfile

Supply-chain / package audit

I don't see obvious supply-chain red flags here:

  • the new packages are all in the well-known Google / googleapis / gRPC package families
  • the installed metadata points back to Google-maintained repos / publisher emails (googleapis-packages@google.com, googleapis-publisher@google.com, etc.) or the gRPC project
  • licenses are Apache 2.0 across the set (except google-crc32c, which doesn't surface the string in pip show, but still points at the official googleapis/python-crc32c repo)

I also ran a known-vulnerability check against the exact versions added/updated by this PR:

uvx --with pip-audit pip-audit -r /tmp/pr2910-reqs.txt

That came back clean.

So from a provenance / vuln perspective, this looks acceptable to me. The real tradeoff is footprint, not package trust.

Size impact

I measured this two ways.

Python install footprint (exact wheel versions in this PR):

  • about +10.6 MiB of compressed wheel downloads net
  • about +92 MiB of installed site-packages footprint net
  • the dominant contributor is google-cloud-aiplatform itself at about 8.0 MiB compressed / 80.3 MiB installed

PyInstaller binary footprint:

  • main: dist/openhands-agent-server = 86 MiB
  • this branch: dist/openhands-agent-server = 148 MiB
  • delta: about +62 MiB (~72% larger)

So this is a fairly meaningful size increase for a provider-specific feature.

Recommendation

My take:

  • security/supply-chain: acceptable as-is
  • size/footprint: large enough that I would lean toward making it optional for the SDK unless we explicitly want Vertex partner-model support in every default install

If we make it optional, here's the implementation I'd suggest

This can follow the existing boto3 pattern pretty closely:

  1. Move the dependency out of the base SDK deps and into an extra, e.g.

    [project.optional-dependencies]
    boto3 = ["boto3>=1.35.0"]
    vertex = ["google-cloud-aiplatform>=1.38"]
  2. Add a small preflight in the LLM transport path:

    • if infer_litellm_provider(...) == "vertex_ai"
    • and importlib.util.find_spec("vertexai") is None
    • raise a friendly LLMBadRequestError (or similar) with an install hint like:
      Vertex AI partner models require the Vertex SDK. Install with: openhands-sdk[vertex]
      

    That gives users a clearer error than LiteLLM's raw import failure.

  3. Make the PyInstaller Vertex collection conditional:

    • only run the collect_all(...) block when vertexai is actually installed
    • that way local builds without the extra still succeed
  4. For the published agent-server image / binary, choose one of two policies:

    • bundle it anyway by installing --extra vertex during the server build, so the runtime remains full-featured while the Python SDK stays lean, or
    • add a separate vertex build variant/tag if we also want the default runtime artifact smaller

Given the numbers above, my preference would be:

  • optional in openhands-sdk
  • then separately decide whether the default agent-server artifact should still include the extra

That keeps the base Python install much leaner while preserving a straightforward path for users who do need Vertex partner models.

AI note: this comment was generated by an AI assistant (OpenHands) on behalf of the user.

@OpenHands OpenHands deleted a comment from openhands-ai Bot Apr 23, 2026

@neubig neubig left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @abguymon , thanks for the contribution! Could you please take a look at the comments above and add these as optional dependencies instead of main dependencies, then re-request my review?

@all-hands-bot

Copy link
Copy Markdown
Collaborator

[Automatic Post]: It has been a while since there was any activity on this PR. @abguymon, are you still working on it? If so, please go ahead, if not then please request review, close it, or request that someone else follow up.

@all-hands-bot

Copy link
Copy Markdown
Collaborator

[Automatic Post]: This PR seems to be currently waiting for review. @neubig, could you please take a look when you have a chance?

abguymon added 2 commits May 6, 2026 11:25
Adds google-cloud-aiplatform as a required dep so LiteLLM's
vertex_ai_partner_models handler can route to Vertex AI Model Garden
MaaS endpoints (MiniMax, Qwen, Kimi, etc.). Without it, requests like
vertex_ai/minimaxai/minimax-m2-maas, vertex_ai/qwen/qwen3-coder-*-maas,
vertex_ai/moonshotai/kimi-k2-thinking-maas fail with:

    litellm.BadRequestError: Vertex_aiException BadRequestError -
    vertexai import failed please run
    `pip install -U "google-cloud-aiplatform>=1.38"`.
    Got error: No module named 'vertexai'

Changes:
- openhands-sdk/pyproject.toml: add "google-cloud-aiplatform>=1.38"
- uv.lock: regenerated (+7 transitive packages)
- agent-server.spec: collect vertexai/google.cloud.aiplatform/google.api_core
  submodules as hidden imports (the vertexai import is deferred inside
  litellm function bodies, so PyInstaller static analysis misses it),
  pin google.rpc.status_pb2 (dynamic gRPC proto stub), and copy package
  metadata.

Supersedes OpenHands#2373 (closed without merging) and lands the spec-side
changes needed for the bundled binary to actually work at runtime.
Addresses review feedback on OpenHands#2910. Mirrors the existing `boto3` extra
pattern so the base `openhands-sdk` install stays lean (the Vertex SDK
adds ~80 MiB to site-packages and ~62 MiB to the PyInstaller binary).

- pyproject.toml: move `google-cloud-aiplatform` from base deps into
  `[project.optional-dependencies]` as `vertex`.
- llm.py / vertex_preflight.py: when the inferred provider is
  `vertex_ai` and `vertexai` is not importable, raise a friendly
  `LLMBadRequestError` with an install hint
  (`pip install "openhands-sdk[vertex]"`) instead of letting LiteLLM
  crash with a low-level `ModuleNotFoundError`.
- agent-server.spec: skip the Vertex `collect_all` block (and the
  `google.rpc.status_pb2` pin) when `vertexai` is not installed, so
  local PyInstaller builds without the extra still succeed.
- docker/Dockerfile: add `--extra vertex` to both `uv sync` calls so
  the published agent-server binary continues to bundle the Vertex SDK
  by default.
- tests: cover the preflight raise/no-op paths.
- uv.lock: regenerated.
@abguymon abguymon force-pushed the adam/install-aiplatform-for-vertex-ai branch from 8c21f2d to 8120d23 Compare May 6, 2026 17:32
@abguymon

abguymon commented May 6, 2026

Copy link
Copy Markdown
Author

Thanks for the audit @neubig — agreed on the size tradeoff. I've pushed a follow-up commit that makes Vertex an optional extra along the lines you suggested:

  • openhands-sdk/pyproject.toml: google-cloud-aiplatform moved out of base deps and into [project.optional-dependencies] as vertex (mirrors the existing boto3 extra).
  • Preflight: in the LLM transport path, when the inferred provider is vertex_ai and vertexai is not importable, we now raise LLMBadRequestError with the hint pip install "openhands-sdk[vertex]" instead of letting LiteLLM crash with a low-level ModuleNotFoundError. New unit tests cover both the raise and no-op paths.
  • PyInstaller spec: the Vertex collect_all block (and the google.rpc.status_pb2 pin) is now guarded on importlib.util.find_spec("vertexai"), so local builds without the extra still succeed.
  • Agent-server Dockerfile: both uv sync invocations gained --extra vertex, so the published agent-server binary continues to bundle the Vertex SDK by default — i.e. base SDK install stays lean, runtime artifact stays full-featured.
  • Rebased onto main to clear the conflict; uv.lock regenerated.

Re-requesting your review.

@abguymon abguymon requested a review from neubig May 6, 2026 17:33

@neubig neubig left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, overall this looks very solid but I think we'd like to not bake this in to the default image, as we'd like to keep it lean. I think we could consider making a separate docker file with more of the optional dependencies backed in though.

Comment thread openhands-agent-server/openhands/agent_server/docker/Dockerfile Outdated
Comment thread openhands-agent-server/openhands/agent_server/docker/Dockerfile Outdated
Comment thread openhands-agent-server/openhands/agent_server/agent-server.spec
abguymon added 2 commits May 7, 2026 12:08
Per follow-up review on OpenHands#2910: don't bundle the Vertex SDK in the
default agent-server image — it's a ~62 MiB / ~72% size hit for
provider-specific support that most users won't need.

- Dockerfile: drop `--extra vertex` from both `uv sync` invocations,
  so the published image stays as lean as it was on `main`.
- agent-server.spec: rewrite the header comment to make the new
  policy explicit (off by default; opt in with
  `uv sync ... --extra vertex` before running pyinstaller). The
  `if _VERTEX_AVAILABLE:` guard already added in the previous commit
  is what makes the default build succeed without the extra
  installed; keep it as the supported opt-in path for downstream
  builds (e.g. fork-specific images) that do want Vertex bundled.
Closes the gap left by the previous commit: with the default Docker
build now lean, there was no way to opt back into a Vertex-bundled
binary without forking the Dockerfile. Adds `ENABLE_VERTEX` (default
0) so downstream builds can do:

    docker build --build-arg ENABLE_VERTEX=1 ...

ARG declared globally and re-imported into both the `builder` and
`binary-builder` stages, with a small inline shell conditional that
appends `--extra vertex` to each `uv sync` call. The PyInstaller
spec already auto-detects an installed `vertexai` and bundles
accordingly, so no further wiring is needed.

Spec header comment updated to point at this build arg as the
canonical opt-in path.
@abguymon

abguymon commented May 7, 2026

Copy link
Copy Markdown
Author

Thanks @neubig — pushed two follow-up commits (4ba82047, 1490482d):

  • Default image stays lean. Dropped --extra vertex from both uv sync calls in agent-server/docker/Dockerfile. The spec's if _VERTEX_AVAILABLE: guard from the previous round means the default build now naturally skips the Vertex bundle (and prints a one-line note saying so), without any spec-level changes.

  • Opt-in via build arg. Rather than a parallel Dockerfile, I added a single ENABLE_VERTEX=0 build arg threaded into both stages. To produce a Vertex-capable binary:

    docker build --build-arg ENABLE_VERTEX=1 ...
    

    Equivalent capability to a separate Dockerfile, but no parallel file to keep in sync, no extra CI job, no extra image tag — and it leaves room to add more ENABLE_* switches the same way later if other extras grow this need. Spec header comment now documents both Docker (--build-arg) and from-source (--extra vertex before pyinstaller) opt-in paths.

If you'd actually prefer a separate Dockerfile.full (or similar) over the build arg, happy to reshape it — let me know. Otherwise, ready for another look.

@all-hands-bot

Copy link
Copy Markdown
Collaborator

[Automatic Post]: It has been a while since there was any activity on this PR. @abguymon, are you still working on it? If so, please go ahead, if not then please request review, close it, or request that someone else follow up.

@all-hands-bot

Copy link
Copy Markdown
Collaborator

[Automatic Post]: It has been a while since there was any activity on this PR. @abguymon, are you still working on it? If so, please go ahead, if not then please request review, close it, or request that someone else follow up.

This comment was created by an AI agent (OpenHands) on behalf of the user.

@all-hands-bot

Copy link
Copy Markdown
Collaborator

[Automatic Post]: It has been a while since there was any activity on this PR. @abguymon, are you still working on it? If so, please go ahead, if not then please request review, close it, or request that someone else follow up.

This comment was generated by an AI agent (OpenHands) on behalf of the user.

@neubig neubig self-requested a review May 25, 2026 14:50
@all-hands-bot

Copy link
Copy Markdown
Collaborator

[Automatic Post]: It has been a while since there was any activity on this PR. @abguymon, are you still working on it? If so, please go ahead, if not then please request review, close it, or request that someone else follow up.

This comment was created by an AI agent (OpenHands) on behalf of the user.

# Conflicts:
#	openhands-agent-server/openhands/agent_server/agent-server.spec
#	uv.lock
@abguymon

abguymon commented Jun 5, 2026

Copy link
Copy Markdown
Author

@neubig I believe this PR is ready to merge

@all-hands-bot

Copy link
Copy Markdown
Collaborator

[Automatic Post]: It has been a while since there was any activity on this PR. @abguymon, are you still working on it? If so, please go ahead, if not then please request review, close it, or request that someone else follow up.

This comment was created by an AI agent (OpenHands) on behalf of the user.

1 similar comment
@all-hands-bot

Copy link
Copy Markdown
Collaborator

[Automatic Post]: It has been a while since there was any activity on this PR. @abguymon, are you still working on it? If so, please go ahead, if not then please request review, close it, or request that someone else follow up.

This comment was created by an AI agent (OpenHands) on behalf of the user.

@neubig neubig added the review-this This label triggers a PR review by OpenHands label Jun 16, 2026

all-hands-bot commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

Review complete.

This review was performed through OpenHands Cloud Automation. You can log in and view the conversation here.

@all-hands-bot all-hands-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review: feat: bundle google-cloud-aiplatform for Vertex partner models

🟡 Acceptable - Works but could be cleaner.

Overview

This PR adds an optional openhands-sdk[vertex] extra that bundles google-cloud-aiplatform for Vertex AI partner model support (MiniMax, Qwen, Kimi MaaS). The implementation is thoughtful: it uses conditional bundling in PyInstaller, adds a preflight check for missing SDK, and keeps the default image lean via --build-arg ENABLE_VERTEX=1.

[CRITICAL ISSUES]

  • None identified.

[IMPROVEMENT OPPORTUNITIES]

  • [agent-server.spec, Line 38] Special Case: The loop for _pkg in _vertex_pkgs with repeated unpacking (_d, _b, _h = collect_all(_pkg)) followed by += concatenation is harder to read. Consider using itertools.chain or flattening with a list comprehension.

  • [agent-server.spec, Lines 28-32] Comment Volume: The 23-line block comment explaining Vertex AI bundling is excessive for an internal build file. This could be a 3-4 line docstring or a comment inline with the conditional check. The explanation of PEP-420 namespace packages and collect_all vs collect_submodules belongs in a commit message or internal docs, not inline.

[TESTING GAPS]

  • The tests in test_vertex_preflight.py are clean and cover the key paths well. No issues.

[DEPENDENCY CHANGES]

  • uv.lock ⚠️ Dependency Downgrade: pypdf downgraded from 6.10.2 to 6.9.2. Was this intentional? Check if this introduces any regressions.
  • uv.lock ⚠️ Dependency Upgrade: google-auth upgraded from 2.41.1 to 2.49.2. This is a minor bump but worth verifying compatibility.
  • uv.lock ⚠️ Dependency Change: rsa package removed. This appears to be related to the google-auth upgrade (they replaced the rsa dependency with cryptography in newer versions). Confirm this is expected.

[RISK ASSESSMENT]

  • [Overall PR] ⚠️ Risk Assessment: 🟡 MEDIUM
    The core implementation is sound. The medium rating reflects the dependency changes in uv.lock (pypdf downgrade, rsa removal) which should be verified as intentional. The new google-cloud-aiplatform dependency is substantial but opt-in, so existing users are unaffected.

VERDICT:
Worth merging - Core logic is sound, minor improvements suggested

KEY INSIGHT:
The opt-in design (both for SDK users via pip extra and for Docker builds via ENABLE_VERTEX=1) is the right approach for a large optional dependency; the preflight check provides a good user experience.


Improve this review? If any feedback above seems incorrect or irrelevant to this repository, you can teach the reviewer to do better:

  1. Add a .agents/skills/custom-codereview-guide.md file to your branch (or edit it if one already exists) with the /codereview trigger and the context the reviewer is missing. See the customization docs for the required frontmatter format.
  2. Re-request a review - the reviewer reads guidelines from the PR branch, so your changes take effect immediately.
  3. When your PR is merged, the guideline file goes through normal code review by repository maintainers.

Resolve with AI? Install the iterate skill in your agent and run /iterate to automatically drive this PR through CI, review, and QA until it's merge-ready.

Was this review helpful? React with 👍 or 👎 to give feedback.


This review was generated by an AI agent (OpenHands) on behalf of the user through OpenHands Automation. View conversation

import importlib.util as _vertex_importlib_util

_VERTEX_AVAILABLE = _vertex_importlib_util.find_spec("vertexai") is not None

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: The loop with repeated unpacking and += concatenation could be clearer. Consider using itertools.chain to flatten the collected lists.

return importlib.util.find_spec("vertexai") is not None


def assert_vertex_sdk_available(provider: str | None) -> None:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: Consider adding a type hint for the provider parameter: def assert_vertex_sdk_available(provider: str | None) -> None:. This matches the actual usage.

@neubig

neubig commented Jun 17, 2026

Copy link
Copy Markdown
Member

I could not push to the original fork branch (trustle:adam/install-aiplatform-for-vertex-ai; GitHub returned 403), so I pushed the resolved branch to this repository and opened replacement PR #3776: #3776

That branch merges current main, resolves the conflict in the LLM transport path, keeps Vertex as an optional extra, applies the useful spec readability feedback, and regenerates uv.lock from current main so the unrelated pypdf/lxml downgrade concern is gone.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review-this This label triggers a PR review by OpenHands

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants