"""Print the resource group + region of the watsonx.ai project. This tells us whether the project is in the same resource group as the WML runtimes (which is the most common cause of the "associate" failure). Run from api/: uv run python scripts/check_project_rg.py """ from __future__ import annotations import json import os import subprocess import sys from dotenv import load_dotenv import httpx load_dotenv() def get_iam_token() -> str: """Get an IAM token by exchanging the watsonx API key with IBM Cloud IAM.""" api_key = os.environ.get("WATSONX_API_KEY", "") if not api_key: print("WATSONX_API_KEY not set", file=sys.stderr) sys.exit(1) # Try ibmcloud CLI first (faster if logged in) try: out = subprocess.check_output( ["ibmcloud", "iam", "oauth-tokens", "--output", "json"], text=True, stderr=subprocess.DEVNULL, ) token = json.loads(out).get("iam_token", "") if token: return token except Exception: pass # Fall back to direct IAM exchange res = httpx.post( "https://iam.cloud.ibm.com/identity/token", data={ "grant_type": "urn:ibm:params:oauth:grant-type:apikey", "apikey": api_key, }, timeout=15.0, ) res.raise_for_status() return f"Bearer {res.json()['access_token']}" def main() -> int: project_id = os.environ.get("WATSONX_PROJECT_ID", "") if not project_id: print("WATSONX_PROJECT_ID not set", file=sys.stderr) return 1 token = get_iam_token() res = httpx.get( f"https://api.dataplatform.cloud.ibm.com/v2/projects/{project_id}", headers={"Authorization": token}, timeout=15.0, ) if res.status_code != 200: print(f"FAILED: {res.status_code}\n{res.text[:500]}") return 2 data = res.json() entity = data.get("entity", {}) print(f"Name: {entity.get('name')}") print(f"Region (storage): {entity.get('storage', {}).get('properties', {}).get('bucket_region')}") print(f"Region (project): {data.get('metadata', {}).get('region')}") print(f"Resource group ID: {entity.get('settings', {}).get('access_restrictions', {}).get('resource_group_id') or 'not set on project'}") print() print(f"Compute resources associated to project:") compute = entity.get("compute") or [] if not compute: print(" NONE — this is the bug. Click '+ Associate service' / '+ New service' in the project.") for c in compute: print(f" - {c.get('name')} ({c.get('type')}) → guid {c.get('guid')}") return 0 if __name__ == "__main__": sys.exit(main())