fix(genai): Prevent ADC lookup delay when initializing VertexAI with API key#1486
fix(genai): Prevent ADC lookup delay when initializing VertexAI with API key#1486ADITYAKUMARRAI2007 wants to merge 18 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes a performance issue where initializing ChatGoogleGenerativeAI with vertexai=True and an API key (but no credentials) caused a ~10-12 second delay due to Application Default Credentials (ADC) lookup. The fix optimizes initialization by providing AnonymousCredentials and a placeholder project ID when using API key authentication, reducing startup latency to under 1 second.
- Injects
AnonymousCredentialswhen API key is provided without explicit credentials to skip ADC lookup - Provides a placeholder project ID ("missing-project-id") when none is specified to prevent slow project ID lookups
- Adds
allowed_objects="all"parameter to the serialization test
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| libs/genai/langchain_google_genai/chat_models.py | Added logic to use AnonymousCredentials and placeholder project ID when initializing Vertex AI client with API key authentication |
| libs/genai/tests/unit_tests/test_chat_models.py | Added allowed_objects parameter to the loads function call in test_serialize |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
this implementation looks like it breaks API authentication at first glance. will investigate more later |
|
Thanks for the review Mason Daugherty (@mdrxy)! I understand the concern regarding Currently, if a user provides only an By passing Also, it appears the integration tests failed with a |
8adf4ff to
5e5bcca
Compare
9170941 to
db1cbf6
Compare
d868009 to
d563f99
Compare
|
Mason Daugherty (@mdrxy) I have pushed the final fixes. Summary of updates:
All CI checks are passing. Ready for your final review! |
|
Mason Daugherty (@mdrxy) hi! Just for planning purposes - is there some sort of ETA on when we could expect this to be merged/available for use? We're trying to migrate from consuming Gemini from AI Studio to Vertex AI ASAP for its better latency, and blocked until this is resolved. Thanks in advance! |
|
Also, ADITYAKUMARRAI2007: I see you've also linked this issue to the PR. Wanted to confirm whether this fixes the behavior of not being able to specify |
|
Hi Ian Spektor (@ianspektor), thanks for checking in! Regarding #1473 and Mason Daugherty (@mdrxy) Re: CI, the |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if os.name == "nt": # Windows | ||
| default_path = os.path.join( | ||
| os.environ.get("APPDATA", ""), | ||
| "gcloud", | ||
| "application_default_credentials.json", | ||
| ) |
There was a problem hiding this comment.
The fallback for APPDATA environment variable may result in an invalid path. If APPDATA is not set (uncommon but possible), os.environ.get("APPDATA", "") returns an empty string, leading to a path starting with just "gcloud". This could cause unexpected behavior when checking os.path.exists(default_path). Consider using a more robust approach or adding a guard to skip the check if APPDATA is empty.
| if google_api_key and client_credentials is None: | ||
| # Check 1: Env Var | ||
| has_env_var = "GOOGLE_APPLICATION_CREDENTIALS" in os.environ | ||
|
|
||
| # Check 2: Default File Path | ||
| if os.name == "nt": # Windows | ||
| default_path = os.path.join( | ||
| os.environ.get("APPDATA", ""), | ||
| "gcloud", | ||
| "application_default_credentials.json", | ||
| ) | ||
| else: # Mac/Linux | ||
| default_path = os.path.join( | ||
| os.path.expanduser("~"), | ||
| ".config", | ||
| "gcloud", | ||
| "application_default_credentials.json", | ||
| ) | ||
|
|
||
| has_default_file = os.path.exists(default_path) | ||
|
|
||
| # Only force Anonymous if absolutely NO standard credentials exist | ||
| if not has_env_var and not has_default_file: | ||
| client_credentials = AnonymousCredentials() # type: ignore |
There was a problem hiding this comment.
The new credential detection logic lacks dedicated test coverage. While the PR description mentions running existing unit tests, there are no new tests specifically verifying that AnonymousCredentials are correctly injected when using vertexai=True with only an API key and no credentials files. Consider adding tests that verify the credentials parameter passed to the Client when: (1) GOOGLE_APPLICATION_CREDENTIALS is not set and the default file doesn't exist, (2) GOOGLE_APPLICATION_CREDENTIALS is set, and (3) the default credentials file exists.
|
|
||
| # Only force Anonymous if absolutely NO standard credentials exist | ||
| if not has_env_var and not has_default_file: | ||
| client_credentials = AnonymousCredentials() # type: ignore |
There was a problem hiding this comment.
Using AnonymousCredentials bypasses the standard Google authentication flow. While this is intentional to avoid the ADC lookup delay, ensure this doesn't inadvertently expose API calls that should require proper authentication. The API key authentication should still be enforced by the backend service, but it would be good to verify that the Client with AnonymousCredentials and an API key functions correctly and securely in the VertexAI context.
| # Check 1: Env Var | ||
| has_env_var = "GOOGLE_APPLICATION_CREDENTIALS" in os.environ | ||
|
|
||
| # Check 2: Default File Path | ||
| if os.name == "nt": # Windows | ||
| default_path = os.path.join( | ||
| os.environ.get("APPDATA", ""), | ||
| "gcloud", | ||
| "application_default_credentials.json", | ||
| ) | ||
| else: # Mac/Linux | ||
| default_path = os.path.join( | ||
| os.path.expanduser("~"), | ||
| ".config", | ||
| "gcloud", | ||
| "application_default_credentials.json", | ||
| ) | ||
|
|
||
| has_default_file = os.path.exists(default_path) |
There was a problem hiding this comment.
The inline comments explain the fix well, but the logic for checking credentials could be more maintainable. The hardcoded paths for different operating systems duplicate logic that might already exist in the google-auth library. Consider checking if there's a utility function from google.auth that can be used to determine if default credentials are available, or at minimum, extract this credential detection logic into a separate helper function for better testability and maintainability.
|
Mason Daugherty (@mdrxy) hey, we are encountering the same issue, and it holds our project back until a fix, is there any chance you can get this pr going? |
|
"Alon Nahmias (@alonahmias) Thanks for confirming! It’s good to know I'm not the only one blocked by this. Mason Daugherty (@mdrxy) It seems this issue is affecting multiple users now (myself, Ian Spektor (@ianspektor), and Alon Nahmias (@alonahmias)). Regarding the build failure: I've investigated the logs (see my comment above), and the failure in test_context_caching is an AssertionError unrelated to my Auth changes. Since the Unit Tests are passing (248 passed) and the fix is verified by the community, could we please merge this to unblock everyone?" |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Mason Daugherty (@mdrxy) hi 👋🏼 any updates/ETA on this? would be extremely useful to understand if we need to find a workaround on our end or its safe to wait for a fix coming from langchain. thanks in advance! |
|
ADITYAKUMARRAI2007 we've switched to authenticating with service account credentials to bypass this issue, but we're still seeing a slower first request than subsequent ones (~1.5s vs ~0.5-0.7 on subsequent ones). Does that make any sense to you? repro steps are the same ones as in my original issue #1485, only passing |
|
Yes, your observation makes perfect sense! The ~1.5s "warm-up" you are seeing is the standard cold-start overhead (TCP/TLS handshake + token exchange), while subsequent requests (~0.6s) reuse the connection. This confirms that your Service Account workaround is successfully bypassing the bug. To explain the approach in this PR versus your workaround: Your Workaround (Service Account): By providing a Service Account, you give the client valid credentials immediately. This prevents the library from triggering the fallback "search" for default credentials, which is what causes the hang. My Approach (This PR): This PR fixes the root cause so users can use API Keys (which are simpler) without that penalty. The Problem: Currently, if you provide only an API key, the client triggers a 10-12s "Application Default Credentials" (ADC) lookup that hangs until it times out. The Solution: I updated the initialization logic to inject AnonymousCredentials when only an API key is present. This explicitly tells the auth library to skip the ADC lookup entirely. Why this PR is better: While your workaround is effective, it requires managing sensitive Service Account JSON files. This PR allows the standard API Key authentication to achieve that same fast startup (~1s) natively, keeping the setup simple. Glad the service account method is unblocking you for now! |
Oh, got it. What's the recommended way to get around this? We are instantiating a new model on every call, so I'm running into this cold start on every message during a voice call, which adds 1s latency every time. Do you happen to know if there's any reference docs for this use case? I'm guessing doing a mock empty request when I instantiate the model + then reusing the model should do the trick, but asking just in case. Thank you! ADITYAKUMARRAI2007 |
|
We migrated from ChatVertexAI to ChatGoogleGenerativeAI with vertexai=True and ran A/B testing on our LangGraph agent using Gemini 2.5 Flash, and have been facing latency issues. Setup:
Results - Median latency comparison:
We're seeing 50-90% latency increases consistently across query types. |
|
Any update or eta for that PR? |
|
This is still an issue on |
|
Is there any update on this? |
Description
Fixes #1485,
Fixes #1473.
When initializing
ChatGoogleGenerativeAIwithvertexai=Trueand a providedapi_key, the client previously triggered a default credentials lookup (ADC). On local machines without Google Cloud credentials, this lookup causes a blocking delay of ~10-12 seconds before timing out and falling back to the API key.This PR optimizes the initialization logic to skip the ADC lookup if an API key is explicitly provided but no credentials are found.
Changes
ChatGoogleGenerativeAI.validate_environmentinlibs/genai/langchain_google_genai/chat_models.py.AnonymousCredentialsand a placeholder project ID whenvertexai=Trueand only anapi_keyis present.google-authlibrary to skip the environment scan, reducing initialization latency from ~11s to <1s.Verification
pytest tests/unit_tests/test_chat_models.py- All 178 tests passed.vertexai=False) function correctly with normal latency (~1.4s).