···445445 if auth_mode == "platform":
446446 env.pop(env_key, None)
447447448448- # Vertex AI: set backend env vars for Google provider
448448+ # Vertex AI / AI Studio: set backend env vars for Google provider
449449 if env_key == "GOOGLE_API_KEY":
450450 providers_config = config.get("providers", {})
451451 google_backend = providers_config.get("google_backend", "auto")
···464464465465 if effective_backend == "vertex":
466466 env["GOOGLE_GENAI_USE_VERTEXAI"] = "true"
467467- # Service account credentials path
468468- vertex_credentials = providers_config.get("vertex_credentials")
469469- if vertex_credentials:
470470- env["GOOGLE_APPLICATION_CREDENTIALS"] = vertex_credentials
471471- # Don't set GOOGLE_API_KEY for service account auth
472472- env.pop("GOOGLE_API_KEY", None)
473473- else:
474474- # Vertex Express: preserve API key
475475- api_key = os.getenv("GOOGLE_API_KEY")
476476- if api_key:
477477- env["GOOGLE_API_KEY"] = api_key
467467+ # Vertex uses SA credentials, not API key — always strip
468468+ env.pop("GOOGLE_API_KEY", None)
469469+ # SA credentials: set GOOGLE_APPLICATION_CREDENTIALS
470470+ creds_path = providers_config.get("vertex_credentials")
471471+ if creds_path and os.path.exists(creds_path):
472472+ env["GOOGLE_APPLICATION_CREDENTIALS"] = creds_path
473473+ # else: GOOGLE_APPLICATION_CREDENTIALS may be inherited from env
478474 # Set project/location from config if present
479475 vertex_project = providers_config.get("vertex_project")
480476 if vertex_project:
+84-60
think/providers/google.py
···38383939from google import genai
4040from google.genai import types
4141-from google.oauth2 import service_account
42414342from think.models import GEMINI_FLASH
4443from think.utils import now_ms
···61606261logger = logging.getLogger(__name__)
63626464-# Vertex AI backend detection cache
6363+# Backend detection cache
6564_detected_backend: str | None = None
66656766···117116def get_or_create_client(client: genai.Client | None = None) -> genai.Client:
118117 """Get existing client or create new one.
119118120120- Parameters
121121- ----------
122122- client : genai.Client, optional
123123- Existing client to reuse. If not provided, creates a new one
124124- using service account credentials, ADC, or GOOGLE_API_KEY
125125- depending on the configured backend.
119119+ For Vertex AI backend, uses service account credentials from config
120120+ or falls back to GOOGLE_APPLICATION_CREDENTIALS env var.
121121+ For AI Studio / auto-detect, uses GOOGLE_API_KEY.
122122+ """
123123+ if client is not None:
124124+ return client
125125+126126+ from think.utils import get_config
127127+128128+ config = get_config()
129129+ providers_config = config.get("providers", {})
130130+131131+ http_options = types.HttpOptions(
132132+ retry_options=types.HttpRetryOptions(attempts=8)
133133+ )
126134127127- Returns
128128- -------
129129- genai.Client
130130- The provided client or a newly created one.
131131- """
132132- if client is None:
133133- from think.utils import get_config
135135+ api_key = os.getenv("GOOGLE_API_KEY")
134136135135- config = get_config()
136136- providers_config = config.get("providers", {})
137137- backend = providers_config.get("google_backend", "auto")
138138- http_options = types.HttpOptions(
139139- retry_options=types.HttpRetryOptions(attempts=8)
140140- )
137137+ # Determine backend
138138+ configured_backend = providers_config.get("google_backend", "auto")
139139+ if configured_backend == "vertex":
140140+ backend = "vertex"
141141+ elif configured_backend == "aistudio":
142142+ backend = "aistudio"
143143+ elif api_key:
144144+ backend = _get_effective_backend(api_key)
145145+ else:
146146+ raise ValueError("GOOGLE_API_KEY not found in environment")
141147142142- if backend == "vertex" and providers_config.get("vertex_credentials"):
143143- creds_path = providers_config["vertex_credentials"]
144144- creds = service_account.Credentials.from_service_account_file(
148148+ if backend == "vertex":
149149+ creds_path = providers_config.get("vertex_credentials")
150150+ project = providers_config.get("vertex_project")
151151+ location = providers_config.get("vertex_location")
152152+153153+ client_kwargs: dict[str, Any] = {
154154+ "vertexai": True,
155155+ "http_options": http_options,
156156+ }
157157+ if project:
158158+ client_kwargs["project"] = project
159159+ if location:
160160+ client_kwargs["location"] = location
161161+162162+ if creds_path and os.path.exists(creds_path):
163163+ from google.oauth2.service_account import Credentials
164164+165165+ creds = Credentials.from_service_account_file(
145166 creds_path,
146167 scopes=["https://www.googleapis.com/auth/cloud-platform"],
147168 )
148148- client = genai.Client(
149149- vertexai=True,
150150- credentials=creds,
151151- project=providers_config.get("vertex_project"),
152152- location=providers_config.get("vertex_location"),
153153- http_options=http_options,
169169+ client_kwargs["credentials"] = creds
170170+ elif not os.getenv("GOOGLE_APPLICATION_CREDENTIALS"):
171171+ raise ValueError(
172172+ "Vertex AI backend requires service account credentials. "
173173+ "Configure in Settings or set GOOGLE_APPLICATION_CREDENTIALS."
154174 )
155155- elif backend == "vertex":
156156- client = genai.Client(
157157- vertexai=True,
158158- project=providers_config.get("vertex_project"),
159159- location=providers_config.get("vertex_location"),
160160- http_options=http_options,
161161- )
162162- else:
163163- api_key = os.getenv("GOOGLE_API_KEY")
164164- if not api_key:
165165- raise ValueError("GOOGLE_API_KEY not found in environment")
166166- client_kwargs = {
167167- "api_key": api_key,
168168- "http_options": http_options,
169169- }
170170- client_kwargs["vertexai"] = _get_effective_backend(api_key) == "vertex"
171171- client = genai.Client(**client_kwargs)
175175+ # else: GOOGLE_APPLICATION_CREDENTIALS is set, SDK auto-discovers
176176+177177+ client = genai.Client(**client_kwargs)
178178+ else:
179179+ # AI Studio path
180180+ if not api_key:
181181+ raise ValueError("GOOGLE_API_KEY not found in environment")
182182+ client = genai.Client(
183183+ api_key=api_key,
184184+ vertexai=False,
185185+ http_options=http_options,
186186+ )
187187+172188 return client
173189174190···784800785801786802def validate_vertex_credentials(
787787- credentials_path: str,
803803+ creds_path: str,
788804 project: str | None = None,
789805 location: str | None = None,
790806) -> dict:
791807 """Validate Vertex AI service account credentials by listing models.
792808793793- Returns {"valid": True, "backend": "vertex"} or
794794- {"valid": False, "error": "..."}.
809809+ Creates a temporary client with the provided SA credentials.
810810+811811+ Returns {"valid": True, "email": "..."} or {"valid": False, "error": "..."}.
795812 """
796813 try:
797797- creds = service_account.Credentials.from_service_account_file(
798798- credentials_path,
814814+ from google.oauth2.service_account import Credentials
815815+816816+ creds = Credentials.from_service_account_file(
817817+ creds_path,
799818 scopes=["https://www.googleapis.com/auth/cloud-platform"],
800819 )
801801- client = genai.Client(
802802- vertexai=True,
803803- credentials=creds,
804804- project=project,
805805- location=location,
806806- http_options=types.HttpOptions(timeout=10000),
807807- )
820820+ client_kwargs: dict[str, Any] = {
821821+ "vertexai": True,
822822+ "credentials": creds,
823823+ "http_options": types.HttpOptions(timeout=10000),
824824+ }
825825+ if project:
826826+ client_kwargs["project"] = project
827827+ if location:
828828+ client_kwargs["location"] = location
829829+830830+ client = genai.Client(**client_kwargs)
808831 list(client.models.list(config={"page_size": 1}))
809809- return {"valid": True, "backend": "vertex"}
832832+ return {"valid": True, "email": creds.service_account_email}
810833 except Exception as e:
811834 return {"valid": False, "error": str(e)}
812835···821844 "_get_effective_backend",
822845 "list_models",
823846 "validate_key",
847847+ "validate_vertex_credentials",
824848]