Bring Your Own Open-Weight Model (BYOM) to Vertex AI Model Garden — Managed Serving & Serverless Tuning (No GKE)
Disclaimer: Independent open-source reference project—not affiliated with, endorsed by, or supported by Google LLC, Alibaba Cloud, Hugging Face, or any third-party vendor; provided "AS IS" and you are responsible for all cloud billing charges (
DISCLAIMER.md).
Bring your own open-weight Hugging Face checkpoints (such as Qwen 3, Llama 3.1 / 3.3, Gemma 3, Mistral, or DeepSeek) into Vertex AI Model Garden, customize them with non-default hyperparameters via Serverless Managed Open Model Tuning, and serve both baseline and fine-tuned weights on fully managed Vertex AI Dedicated Endpoints—with zero Google Kubernetes Engine (GKE) clusters to operate. Run the entire end-to-end pipeline automatically from Google Cloud Shell via ./deploy/run_cloud_shell.sh, or follow the complete manual Console UI and CLI/SDK walkthrough below.
Documentation & LLM Context: Live GitHub Pages Site · LLM Context (docs/llms.txt) · Release Notes · Disclaimer
flowchart TD
HF["Hugging Face Hub (Qwen/Qwen3-4B)"] -->|"Stage weights & JSONL"| GCS_BASE["Cloud Storage: gs://.../hf-weights/Qwen--Qwen3-4B"]
GCS_BASE -->|"1. CustomModel(gcs_uri=...)"| EP1["Managed Dedicated Endpoint (As-Is Base Model · vLLM)"]
EP1 -->|"2. OpenAI SDK & endpoint.predict"| EVAL1["Baseline Domain Evaluation (0 / 8 Exact Match)"]
GCS_BASE -->|"3a. custom_base_model + train/val JSONL"| TUNE["Serverless Managed Open Model Tuning (FULL · epochs=5 · lr=5e-5)"]
TUNE -->|"Exports .safetensors"| GCS_FT["Cloud Storage: .../postprocess/node-0/checkpoints/final"]
GCS_FT -->|"3b. CustomModel(gcs_uri=...)"| EP2["Managed Dedicated Endpoint (Fine-Tuned Model · vLLM)"]
EP2 -->|"4. OpenAI SDK & endpoint.predict"| EVAL2["Post-Tuning Domain Evaluation & Comparison Report"]
- Core Capabilities & Repository Overview
- Before You Begin & Costs
- Quickstart
- Option A — Automated Execution in Google Cloud Shell
- Option B — Manual Step-by-Step Guide (Console UI + gcloud & Python SDK)
- Step 0 - Set Environment Variables
- Step 1 - Configure a Google Cloud Project & Enable APIs
- Step 2 - Check Enterprise Organization Policies (Optional)
- Step 3 - Create Cloud Storage Bucket & Grant Least-Privilege Service Agent IAM
- Step 4 - Clone the Repository & Install Python Dependencies
- Step 5 - Stage Hugging Face Weights & Training Data to Cloud Storage
- Step 6 - Deploy the Open-Weight Model As-Is (Managed Vertex AI — No GKE)
- Step 7 - Test the As-Is Base Model on Domain Data
- Step 8 - Fine-Tune the Model with New Hyperparameters (Serverless)
- Step 9 - Deploy the Fine-Tuned Checkpoint & Compare Results
- Security, Least-Privilege Permissions & Cost Controls
- Troubleshooting & Quota Reference
- Cleaning Up
- Testing & Development (100% Offline)
- Key Terms, Limitations & Additional Resources
- 100% Managed Serving (Zero GKE): Imports Hugging Face
.safetensorsdirectories directly from Cloud Storage viavertexai.preview.model_garden.CustomModel, pairing weights with Google-hardened vLLM or SGLang containers on managed Dedicated Endpoints (*.prediction.vertexai.goog). - Live GPU Stock Auto-Selection: Queries
CustomModel.list_deploy_options(available_machines=True, filter_by_user_quota=True)to automatically pick GPU SKUs with live regional capacity and project quota (for example,g4-standard-48with1x NVIDIA_RTX_PRO_6000deploys in ~9 minutes). - Serverless Open Model Tuning on Custom Weights: Launches
FULLorPEFT_ADAPTER(LoRA) supervised fine-tuning directly from custom GCS base weights with overridden hyperparameters (epochs=5,learning_rate=5e-5vs. defaultepochs=3,learning_rate=7.5e-5), exporting standard.safetensorsweights back to GCS with zero vendor lock-in. - Dual Inference Compatibility: Evaluates endpoints simultaneously via the OpenAI Python SDK (
/v1beta1/.../chat/completions) and native Vertex AIendpoint.predict(..., use_dedicated_endpoint=True). - One-Command Cloud Shell Automation + Dry-Run IAM:
./deploy/run_cloud_shell.shprovides read-only--preflight, non-mutating--emit-iamdry-run output, SHA-256 cached virtualenv setup, automatic lazy service-agent IAM retry, end-to-end--deploy, and one-command--cleanup.
| Feature / Module | Purpose | Output |
|---|---|---|
deploy/run_cloud_shell.sh |
Automated Cloud Shell orchestrator (--preflight, --emit-iam, --deploy, --cleanup) |
Provisioned resources, results/*.json, results/comparison_report.md |
deploy/preflight_check.sh |
Read-only check for CLIs, active gcloud auth, project access, enabled APIs, and bucket state |
Human-readable preflight status report |
src/generate_dataset.py |
Deterministic enterprise incident triage JSONL builder (<=25% validation byte ratio enforced) |
data/train.jsonl (120), data/val.jsonl (12), data/test_prompts.jsonl (8) |
src/stage_hf_weights.py |
Downloads Hugging Face .safetensors + tokenizer files and uploads weights & datasets to GCS |
gs://<BUCKET>/hf-weights/Qwen--Qwen3-4B & datasets/*.jsonl |
src/deploy_custom_weights.py |
Queries live GPU stock/quota and deploys GCS weights to a Managed Dedicated Endpoint | Active Vertex AI Endpoint + results/*_deploy.json |
src/evaluate_endpoint.py |
Evaluates Dedicated Endpoints via OpenAI SDK (chat.completions) and endpoint.predict() |
Accuracy & latency JSON (results/*_eval.json) |
src/finetune_open_model.py |
Submits & polls Serverless Managed Open Model Tuning (vertexai_sft or google_genai SDK) |
Exported .safetensors in GCS + results/tuning_job_completed.json |
src/compare_results.py |
Builds side-by-side accuracy, latency, and per-ticket triage comparison report | results/comparison_report.md |
src/cleanup.py |
Undeploys models and deletes Dedicated GPU Endpoints to stop hourly GPU billing | Terminated endpoints ($0/hr GPU compute) |
docs/ & scripts/sync_docs.sh |
Zero-build-step GitHub Pages site, JSON-LD metadata, llms.txt, and screenshot sync check |
Static documentation site & mirrored docs/assets/screenshots/ |
tests/test_offline_suite.py |
100% offline unit, CLI, dry-run, asset-parity, and repository hygiene test suite | Zero-cloud-secret CI verification |
- A billing-enabled Google Cloud project (Free Trial accounts generally lack GPU serving and Managed Open Model Tuning quotas).
- Vertex AI Administrator (
roles/aiplatform.admin) and Storage Admin (roles/storage.admin) on the target project. - Google Cloud Shell (recommended—
gcloud,python3, andgitare pre-installed) or a local terminal withgcloudand Python 3.10+. - (Optional) A Hugging Face token (
HF_TOKEN) only if substituting a gated model such as Llama 3.1 or Gemma 3.Qwen/Qwen3-4Bused here is Apache 2.0 licensed and ungated.
- Vertex AI Dedicated GPU Endpoint: Billed per replica-hour while deployed (for example,
g4-standard-48with1x NVIDIA_RTX_PRO_6000org2-standard-12with1x NVIDIA_L4). Always run--cleanupwhen finished testing, or enable Scale-to-Zero (min_replica_count=0, Preview). - Serverless Managed Open Model Tuning: Billed only during active serverless job execution; zero idle GPU cost once
JOB_STATE_SUCCEEDEDis reached. - Cloud Storage: Regional object storage for staged base weights (
~8.06 GB), JSONL splits (~58 KB), and exported fine-tuned.safetensorsweights (~8.06 GB).
Choose Option A to execute the complete workflow automatically in Google Cloud Shell, or Option B to perform every step manually via the Google Cloud Console UI, gcloud, and Python SDK.
Open Google Cloud Shell, clone this repository, inspect the non-mutating IAM dry-run, and launch the automated 4-stage pipeline:
git clone https://github.com/mbettan/AgentForge.git
cd AgentForge
export PROJECT_ID="<YOUR_PROJECT_ID>"
export REGION="us-central1"
# 1. Inspect exact API, bucket, and least-privilege IAM commands (read-only dry-run)
./deploy/run_cloud_shell.sh --emit-iam
# 2. Run read-only environment & project preflight checks
./deploy/run_cloud_shell.sh --preflight
# 3. Execute all 4 stages end-to-end (automatically tears down temporary base endpoint during tuning,
# and tears down the fine-tuned endpoint at the end when --auto-cleanup is passed)
./deploy/run_cloud_shell.sh --deploy --auto-cleanupTip
Omit --auto-cleanup if you want to keep the fine-tuned Dedicated Endpoint live for interactive testing in the Console or via the OpenAI SDK. When finished, run ./deploy/run_cloud_shell.sh --cleanup to delete any active GPU endpoints.
Replace <YOUR_PROJECT_ID> with your Google Cloud Project ID and <YOUR_GCP_ACCOUNT_EMAIL> with your Google Cloud user email:
export PROJECT_ID="<YOUR_PROJECT_ID>"
export GCP_USER_ACCOUNT="<YOUR_GCP_ACCOUNT_EMAIL>"
export REGION="us-central1"
export BUCKET_NAME="${PROJECT_ID}-mg-byom-demo"
export BUCKET_URI="gs://${BUCKET_NAME}"
export HF_REPO_ID="Qwen/Qwen3-4B"Note
Keep these environment variables set across all manual steps. Re-export them if your Cloud Shell session restarts after an idle timeout.
- (Optional) If creating a brand-new Google Cloud project, create it and link your billing account (skip if using an existing project):
export ORG_ID="$(gcloud organizations list --format='value(ID)' | head -n 1)"
export BILLING_ID="<YOUR_BILLING_ACCOUNT_ID>"
gcloud projects create "${PROJECT_ID}" --organization="${ORG_ID}"
gcloud billing projects link "${PROJECT_ID}" --billing-account="${BILLING_ID}"- Set your active
gcloudconfiguration and Application Default Credentials (ADC), then enable the required APIs:
gcloud config set project "${PROJECT_ID}"
gcloud config set compute/region "${REGION}"
gcloud auth login "${GCP_USER_ACCOUNT}"
gcloud auth application-default login
gcloud auth application-default set-quota-project "${PROJECT_ID}"
gcloud services enable \
aiplatform.googleapis.com \
compute.googleapis.com \
storage.googleapis.com \
logging.googleapis.com \
--project="${PROJECT_ID}"Tip
Unlike self-managed GKE clusters or Compute Engine VMs, Vertex AI Managed Dedicated Endpoints and Serverless Open Model Tuning run inside Google-managed tenant projects. Strict enterprise VM organization policies (constraints/compute.vmExternalIpAccess, constraints/compute.requireOsLogin, constraints/compute.requireShieldedVm, constraints/compute.restrictVpcPeering) do not block managed Model Garden serving or tuning.
Only verify constraints/gcp.resourceLocations if your organization restricts regional placement and excludes us-central1:
| Description | Constraint Identifier | Required Setting |
|---|---|---|
| Google Cloud Resource Locations | constraints/gcp.resourceLocations |
Allow in:us-central1-locations (or choose another supported region) |
Managed Custom Weights Serving and Serverless Open Model Tuning access your staging bucket using two Google-managed service agents:
- Vertex AI Service Agent:
service-${PROJECT_NUMBER}@gcp-sa-aiplatform.iam.gserviceaccount.com - Vertex AI Managed OSS Fine-Tuning Service Agent:
service-${PROJECT_NUMBER}@gcp-sa-vertex-moss-ft.iam.gserviceaccount.com
Create the regional Cloud Storage bucket with Uniform Bucket-Level Access and grant bucket-scoped roles/storage.objectUser (least privilege—read/write objects in this bucket only, no IAM admin permissions):
export PROJECT_NUMBER="$(gcloud projects describe "${PROJECT_ID}" --format='value(projectNumber)')"
# 1. Create regional bucket with uniform bucket-level access
gcloud storage buckets create "${BUCKET_URI}" \
--project="${PROJECT_ID}" \
--location="${REGION}" \
--uniform-bucket-level-access
# 2. Provision the Vertex AI service identity (if never used in this project)
gcloud beta services identity create \
--service=aiplatform.googleapis.com \
--project="${PROJECT_ID}"
# 3. Grant bucket-scoped Storage Object User to the Vertex AI Service Agent
gcloud storage buckets add-iam-policy-binding "${BUCKET_URI}" \
--member="serviceAccount:service-${PROJECT_NUMBER}@gcp-sa-aiplatform.iam.gserviceaccount.com" \
--role="roles/storage.objectUser"
# 4. Grant bucket-scoped Storage Object User to the Managed OSS Fine-Tuning Service Agent
gcloud storage buckets add-iam-policy-binding "${BUCKET_URI}" \
--member="serviceAccount:service-${PROJECT_NUMBER}@gcp-sa-vertex-moss-ft.iam.gserviceaccount.com" \
--role="roles/storage.objectUser"Important
In a project that has never submitted an Open Model Tuning job before, service-${PROJECT_NUMBER}@gcp-sa-vertex-moss-ft.iam.gserviceaccount.com is lazily provisioned on the first tuning API call. If command #4 reports that the service account does not exist yet, proceed to Step 8, submit the tuning job once to trigger automatic service-agent creation, re-run command #4, and re-submit Step 8 (automated seamlessly in ./deploy/run_cloud_shell.sh).
Clone this repository, create an isolated Python virtual environment, and install requirements.txt:
git clone https://github.com/mbettan/AgentForge.git
cd AgentForge
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txtTo make fine-tuning impact unambiguous, we evaluate and customize Qwen/Qwen3-4B on a synthetic enterprise incident triage schema ([CYMBAL-TRIAGE] queue=CYM-... | severity=... | sla=... | runbook=RB-... | action=...). Because these proprietary queue codes and runbook IDs do not exist on the public web, an untuned model scores 0% exact match out of the box.
python3 src/generate_dataset.py
python3 src/stage_hf_weights.py \
--project="${PROJECT_ID}" \
--bucket="${BUCKET_URI}" \
--hf-repo="${HF_REPO_ID}" \
--cleanup-localNote
Managed Open Model Tuning requires val.jsonl byte size to be at most 25% of train.jsonl byte size (12 validation examples vs. 120 training examples keeps the ratio at 10.4%). For gated Hugging Face models, append --hf-token="${HF_TOKEN}".
Verify in Cloud Storage -> Buckets that all 11 Qwen/Qwen3-4B .safetensors shards, config.json, and tokenizer files are staged in ${BUCKET_URI}/hf-weights/Qwen--Qwen3-4B:
You can deploy custom weights from Cloud Storage either via the Model Garden Console UI or via the Vertex AI Python SDK.
- Open Vertex AI -> Model Garden and click Deploy model with custom weights in the top action bar:
- Browse to
${BUCKET_URI}/hf-weights/Qwen--Qwen3-4B, selectus-central1, pick an available GPU machine configuration, leave Public (Dedicated endpoint) selected, and click Deploy:
Note
Three critical SDK rules verified against google-cloud-aiplatform:
- Import
CustomModelfromvertexai.preview.model_garden(notvertexai.model_garden). machine_type,accelerator_type, andaccelerator_countfollow an all-or-none rule: either pass all three or omit all three. Passing--machine-type=autocallslist_deploy_options(available_machines=True, filter_by_user_quota=True)to pick live available GPU stock automatically.CustomModel.deploy()always creates a Dedicated Endpoint—do not passuse_dedicated_endpoint=Trueto.deploy(), only toendpoint.predict(..., use_dedicated_endpoint=True).
python3 src/deploy_custom_weights.py \
--project="${PROJECT_ID}" \
--region="${REGION}" \
--gcs-uri="${BUCKET_URI}/hf-weights/Qwen--Qwen3-4B" \
--endpoint-display-name="qwen3-4b-byom-base-ep" \
--model-display-name="qwen3-4b-byom-base" \
--machine-type="auto" \
--output-json="results/base_deploy.json"Standalone Python SDK equivalent:
import vertexai
from vertexai.preview import model_garden
vertexai.init(project=PROJECT_ID, location=REGION)
base_model = model_garden.CustomModel(gcs_uri=f"{BUCKET_URI}/hf-weights/Qwen--Qwen3-4B")
# Inspect live GPU availability and quota in the target region
print(base_model.list_deploy_options(available_machines=True, filter_by_user_quota=True))
# Deploy to a Managed Vertex AI Dedicated Endpoint (vLLM managed by Google)
base_endpoint = base_model.deploy(
machine_type="g4-standard-48",
accelerator_type="NVIDIA_RTX_PRO_6000",
accelerator_count=1,
model_display_name="qwen3-4b-byom-base",
endpoint_display_name="qwen3-4b-byom-base-ep",
)Open Vertex AI -> Model Registry to verify the imported model (DEPLOY_SOURCE=CUSTOM_WEIGHTS, BASE_MODEL_ID=Qwen/Qwen3-4B, prebuilt pytorch-vllm-serve container—zero GKE clusters created):
Evaluate the as-is base endpoint across data/test_prompts.jsonl using both endpoint.predict(use_dedicated_endpoint=True) and the OpenAI Chat Completions API:
export BASE_EP_ID="$(python3 -c 'import json; print(json.load(open("results/base_deploy.json"))["endpoint_id"])')"
python3 src/evaluate_endpoint.py \
--project="${PROJECT_ID}" \
--region="${REGION}" \
--endpoint-id="${BASE_EP_ID}" \
--label="base_qwen3_4b_as_is" \
--output-json="results/base_model_eval.json"On the 8-prompt evaluation suite, untuned Qwen/Qwen3-4B responds cleanly (200 OK, 17.5s avg latency) with 0 / 8 (0.0%) exact schema & queue/runbook match, confirming it has no prior knowledge of the proprietary triage taxonomy.
Open Cloud Logging (resource.type="aiplatform.googleapis.com/Endpoint") to inspect the managed vLLM container startup (Application startup complete) and /generate + /v1/chat/completions 200 OK logs:
(Optional) If your project has limited regional GPU quota, tear down the base endpoint now before launching fine-tuning:
python3 src/cleanup.py --project="${PROJECT_ID}" --region="${REGION}" --endpoint-ids "${BASE_EP_ID}"In Vertex AI Model Garden, Qwen3 supports both one-click deployment and serverless supervised fine-tuning (publishers/qwen/models/qwen3):
For qwen/qwen3@qwen3-4b, Model Garden's default hyperparameters are epochs=3 and learning_rate=7.5e-5 (FULL tuning mode). Below, we override both hyperparameters (--epochs=5, --learning-rate=5e-5) while pointing --custom-base-model directly at our staged Hugging Face weights in Cloud Storage:
python3 src/finetune_open_model.py \
--project="${PROJECT_ID}" \
--location="${REGION}" \
--sdk="vertexai_sft" \
--base-model="qwen/qwen3@qwen3-4b" \
--custom-base-model="${BUCKET_URI}/hf-weights/Qwen--Qwen3-4B" \
--train-dataset="${BUCKET_URI}/datasets/train.jsonl" \
--val-dataset="${BUCKET_URI}/datasets/val.jsonl" \
--output-uri="${BUCKET_URI}/tuning-runs/qwen3-4b-e5-lr5e5" \
--display-name="qwen3-4b-cymbal-ft-e5-lr5e5" \
--tuning-mode="FULL" \
--epochs=5 \
--learning-rate=5e-5 \
--output-json="results/tuning_job_completed.json"Standalone Python SDK equivalent:
import vertexai
from vertexai.preview.tuning import sft
from vertexai.tuning import SourceModel
vertexai.init(project=PROJECT_ID, location=REGION)
sft_job = sft.train(
source_model=SourceModel(
base_model="qwen/qwen3@qwen3-4b",
custom_base_model=f"{BUCKET_URI}/hf-weights/Qwen--Qwen3-4B",
),
tuning_mode="FULL",
epochs=5, # Overridden from default 3
learning_rate=5e-5, # Overridden from default 7.5e-5
train_dataset=f"{BUCKET_URI}/datasets/train.jsonl",
validation_dataset=f"{BUCKET_URI}/datasets/val.jsonl",
output_uri=f"{BUCKET_URI}/tuning-runs/qwen3-4b-e5-lr5e5",
tuned_model_display_name="qwen3-4b-cymbal-ft-e5-lr5e5",
)Open Vertex AI -> Tuning in the Google Cloud Console to verify the serverless job configuration (Base model: qwen/qwen3@qwen3-4b, Tuning mode: Full tuning, Custom model weights, Number of epochs: 5, Learning rate: 0.00005):
Click the Dataset tab on the tuning job to inspect the validated chat splits (132 total examples across train.jsonl and val.jsonl) and conversation preview:
When src/finetune_open_model.py reaches JOB_STATE_SUCCEEDED, Vertex AI exports standard Hugging Face .safetensors weights and tokenizer files to ${BUCKET_URI}/tuning-runs/qwen3-4b-e5-lr5e5/postprocess/node-0/checkpoints/final.
- Deploy the exported fine-tuned checkpoint to a Managed Dedicated Endpoint (
CustomModel, no GKE):
python3 src/deploy_custom_weights.py \
--project="${PROJECT_ID}" \
--region="${REGION}" \
--gcs-uri="${BUCKET_URI}/tuning-runs/qwen3-4b-e5-lr5e5/postprocess/node-0/checkpoints/final" \
--endpoint-display-name="qwen3-4b-byom-tuned-ep" \
--model-display-name="qwen3-4b-cymbal-ft-e5-lr5e5" \
--machine-type="auto" \
--output-json="results/finetuned_deploy.json"- Evaluate the fine-tuned endpoint on the exact same 8 test prompts and generate
results/comparison_report.md:
export TUNED_EP_ID="$(python3 -c 'import json; print(json.load(open("results/finetuned_deploy.json"))["endpoint_id"])')"
python3 src/evaluate_endpoint.py \
--project="${PROJECT_ID}" \
--region="${REGION}" \
--endpoint-id="${TUNED_EP_ID}" \
--label="finetuned_qwen3_4b_e5_lr5e5" \
--output-json="results/finetuned_model_eval.json"
python3 src/compare_results.py \
--base-json="results/base_model_eval.json" \
--tuned-json="results/finetuned_model_eval.json" \
--output-md="results/comparison_report.md"| Stage | Serving Runtime | Hyperparameters | Domain Exact Match |
|---|---|---|---|
Step 6–7: Base Qwen/Qwen3-4B (As-Is) |
Managed Dedicated Endpoint (vLLM) | Pretrained HF base weights | 0 / 8 (0.0%) |
Step 8–9: Fine-Tuned Qwen/Qwen3-4B |
Managed Dedicated Endpoint (vLLM) | FULL, epochs=5, lr=5e-5 |
Populated in results/comparison_report.md |
- Bucket-Scoped Service Agent IAM (
roles/storage.objectUser): Neithergcp-sa-aiplatformnorgcp-sa-vertex-moss-ftrequires project-wide Storage Admin access. Bindingroles/storage.objectUserstrictly on${BUCKET_URI}scopes read/write object access to that single bucket with Uniform Bucket-Level Access enabled. - Authenticated Inference by Default: Vertex AI Dedicated Endpoints require valid Google Cloud OAuth 2.0 / IAM bearer tokens (
aiplatform.endpoints.predict) on every request—no unauthenticated public endpoints are exposed. For private VPC-only traffic, passenable_private_service_connect=Trueandpsc_project_allow_list=[...]toCustomModel.deploy(). - Non-Mutating Dry-Run Mode: Always run
./deploy/run_cloud_shell.sh --emit-iamand./deploy/run_cloud_shell.sh --preflightbefore mutating a shared or enterprise Google Cloud project. - GPU Billing Safety Caps:
./deploy/run_cloud_shell.sh --deployautomatically undeploys the baseline GPU endpoint before starting multi-hour fine-tuning (unless--keep-base-endpointis explicitly passed) and supports--auto-cleanupso GPU replicas are deleted immediately after evaluation finishes.
FAILED_PRECONDITION: Service account service-<PROJECT_NUMBER>@gcp-sa-vertex-moss-ft.iam.gserviceaccount.com does not have [storage.objects.get, storage.objects.list]gcp-sa-vertex-moss-ftis lazily created on a project's very first Open Model Tuning API call. Grantroles/storage.objectUseron${BUCKET_URI}(command #4 in Step 3) and re-run Step 8.ImportError: cannot import name 'CustomModel' from 'vertexai.model_garden'Import fromvertexai.preview.model_garden(from vertexai.preview import model_garden).TypeError: Failed to set field 'base_model' on TuningJobinsft.train()ImportSourceModelfromvertexai.tuning(from vertexai.tuning import SourceModel), notvertexai.preview.tuning.ValueErroronCustomModel.deploy()hardware parametersmachine_type,accelerator_type, andaccelerator_countmust either all be provided together or all be omitted (None).INVALID_ARGUMENTon validation split size Ensureval.jsonlbyte size is<= 25%oftrain.jsonlbyte size.- Quota Increases: Check Google Cloud Quotas if deployment or tuning reports
RESOURCE_EXHAUSTED:
| Service | Quota Name | Quota Metric | Location | Recommended Minimum |
|---|---|---|---|---|
| Vertex AI API | Custom model serving Nvidia L4 GPUs | aiplatform.googleapis.com/custom_model_serving_nvidia_l4_gpus |
us-central1 |
2 |
| Vertex AI API | Custom model serving Nvidia RTX PRO 6000 GPUs | aiplatform.googleapis.com/custom_model_serving_nvidia_rtx_pro_6000_gpus |
us-central1 |
2 |
| Vertex AI API | Global concurrent managed OSS model tuning jobs | aiplatform.googleapis.com/global_concurrent_managed_oss_model_tuning_jobs |
global |
2 |
Dedicated GPU endpoints accrue hourly charges while deployed, even when idle.
# Option 1: Automated cleanup of any endpoints recorded in results/*.json
./deploy/run_cloud_shell.sh --cleanup
# Option 2: Explicit cleanup by Endpoint ID
python3 src/cleanup.py \
--project="${PROJECT_ID}" \
--region="${REGION}" \
--endpoint-ids "${BASE_EP_ID}" "${TUNED_EP_ID}"
# Option 3 (Optional): Delete staged weights and checkpoints in Cloud Storage
gcloud storage rm --recursive "${BUCKET_URI}"Note
Deleting a Vertex AI Endpoint does not delete your imported models in Vertex AI Model Registry or your exported .safetensors checkpoints in Cloud Storage, so you can re-deploy later without re-running fine-tuning.
All unit tests, CLI smoke checks, --emit-iam dry-run checks, screenshot sync checks, and repository hygiene checks run 100% offline with zero cloud credentials or live API calls required:
# 1. Run the offline unit, CLI, and repository hygiene test suite
python3 -m unittest discover -s tests -v
# 2. Verify docs/assets/screenshots/ parity with images/
./scripts/sync_docs.sh --check
# 3. Optional: Run ruff & shellcheck locally (enforced in GitHub Actions CI)
ruff check src/ tests/
shellcheck deploy/*.sh scripts/*.sh- BYOM (Bring Your Own Model): Importing custom or modified open-weight checkpoints (
.safetensors+ tokenizer/config files) from Cloud Storage into Vertex AI Model Garden. - Dedicated Endpoint: Isolated, low-latency Vertex AI serving endpoint (
*.prediction.vertexai.goog) supporting OpenAI-compatible/chat/completionsand streaming responses. - Managed Open Model Tuning: Serverless Vertex AI supervised fine-tuning pipeline for open-weight families (Qwen, Llama, Gemma, DeepSeek) that exports standard Hugging Face
.safetensorscheckpoints back to GCS. - Limitations:
CustomModelverifiesconfig.jsonagainst Model Garden's supported open-model families (Qwen, Llama, Gemma, Mistral, DeepSeek, Phi, etc.). Arbitrary custom PyTorchnn.Moduleclasses unsupported by vLLM/SGLang require a custom serving container in Vertex AI Model Registry.- For
qwen/qwen3@qwen3-4b, Managed Open Model Tuning supportsFULLtuning (max_seq_length=8192). Parameter-efficientPEFT_ADAPTER(LoRA) tuning is supported on larger variants (qwen/qwen3@qwen3-32b,google/gemma3@gemma-3-27b-it,meta/llama3_1@llama-3.1-8b-instruct).
- Official Documentation:
Disclaimer: Independent personal open-source project, not affiliated with or endorsed by Google LLC or any third-party vendor; provided "AS IS" without warranty under Apache License 2.0—see
DISCLAIMER.mdbefore provisioning billable cloud resources.







