mirror of
https://github.com/browser-use/browser-use.git
synced 2026-09-14 19:59:47 +08:00
feat: add client header to GoogleChat (#4884)
Added header per integration
[guidelines](https://ai.google.dev/gemini-api/docs/partner-integration).
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Set the `x-goog-api-client` header for Google Chat requests to meet
Google partner integration guidelines. It identifies the client as
`browser-use/{version}` with a fallback of `unknown`.
- **New Features**
- Add `x-goog-api-client` header with value `browser-use/{version}`.
- Normalize and merge `http_options`; always set/override the header in
client params.
- **Bug Fixes**
- Handle both `types.HttpOptions` and `types.HttpOptionsDict`,
preserving `timeout` and existing headers.
- Add tests covering `None`, Pydantic, and dict `http_options` to ensure
the header is set and prefixed with `browser-use/`.
<sup>Written for commit 115199d2ba.
Summary will update on new commits. <a
href="https://cubic.dev/pr/browser-use/browser-use/pull/4884?utm_source=github">Review
in cubic</a></sup>
<!-- End of auto-generated description by cubic. -->
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import asyncio
|
||||
import importlib.metadata
|
||||
import json
|
||||
import logging
|
||||
import random
|
||||
@@ -122,6 +123,33 @@ class ChatGoogle(BaseChatModel):
|
||||
"""Get logger for this chat instance"""
|
||||
return logging.getLogger(f'browser_use.llm.google.{self.model}')
|
||||
|
||||
def _get_http_options(self) -> dict[str, Any]:
|
||||
"""Get http options with the default headers set."""
|
||||
try:
|
||||
bu_version = importlib.metadata.version('browser-use')
|
||||
except importlib.metadata.PackageNotFoundError:
|
||||
bu_version = 'unknown'
|
||||
|
||||
header_value = f'browser-use/{bu_version}'
|
||||
|
||||
http_opts: dict[str, Any] = {}
|
||||
|
||||
if self.http_options is not None:
|
||||
if isinstance(self.http_options, types.HttpOptions):
|
||||
http_opts = self.http_options.model_dump(exclude_unset=True)
|
||||
elif isinstance(self.http_options, dict):
|
||||
http_opts = dict(self.http_options)
|
||||
|
||||
headers: dict[str, str] = {}
|
||||
existing_headers = http_opts.get('headers')
|
||||
if isinstance(existing_headers, dict):
|
||||
headers = {str(k): str(v) for k, v in existing_headers.items()}
|
||||
|
||||
headers['x-goog-api-client'] = header_value
|
||||
http_opts['headers'] = headers
|
||||
|
||||
return http_opts
|
||||
|
||||
def _get_client_params(self) -> dict[str, Any]:
|
||||
"""Prepare client parameters dictionary."""
|
||||
# Define base client params
|
||||
@@ -131,7 +159,7 @@ class ChatGoogle(BaseChatModel):
|
||||
'credentials': self.credentials,
|
||||
'project': self.project,
|
||||
'location': self.location,
|
||||
'http_options': self.http_options,
|
||||
'http_options': self._get_http_options(),
|
||||
}
|
||||
|
||||
# Create client_params dict with non-None values
|
||||
|
||||
@@ -13,3 +13,59 @@ async def test_google_gemini_flash_latest(httpserver):
|
||||
extra_kwargs={},
|
||||
httpserver=httpserver,
|
||||
)
|
||||
|
||||
|
||||
def test_x_goog_api_client_header_is_set():
|
||||
"""Test that the x-goog-api-client header is correctly set in the HTTP options."""
|
||||
chat = ChatGoogle(model='gemini-flash-latest', api_key='fake')
|
||||
|
||||
# Generate the params used for genai.Client
|
||||
params = chat._get_client_params()
|
||||
|
||||
# Extract the header
|
||||
http_options = params.get('http_options', {})
|
||||
headers = http_options.get('headers', {})
|
||||
|
||||
assert 'x-goog-api-client' in headers, 'x-goog-api-client header missing'
|
||||
assert 'browser-use/' in headers['x-goog-api-client'], 'browser-use not found in x-goog-api-client header'
|
||||
|
||||
|
||||
def test_x_goog_api_client_header_with_none_http_options():
|
||||
"""Test setting header when http_options is None."""
|
||||
chat = ChatGoogle(model='gemini-flash-latest', api_key='fake', http_options=None)
|
||||
params = chat._get_client_params()
|
||||
http_opts = params.get('http_options', {})
|
||||
assert http_opts.get('headers', {}).get('x-goog-api-client', '').startswith('browser-use/')
|
||||
|
||||
|
||||
def test_x_goog_api_client_header_with_pydantic_http_options():
|
||||
"""Test setting header when http_options is a types.HttpOptions Pydantic model."""
|
||||
from google.genai import types
|
||||
|
||||
pydantic_opts = types.HttpOptions(timeout=30, headers={'custom-header': 'value'})
|
||||
chat = ChatGoogle(model='gemini-flash-latest', api_key='fake', http_options=pydantic_opts)
|
||||
params = chat._get_client_params()
|
||||
http_opts = params.get('http_options', {})
|
||||
|
||||
# Verify it extracts and preserves timeout and custom-header
|
||||
assert http_opts.get('timeout') == 30
|
||||
assert http_opts.get('headers', {}).get('custom-header') == 'value'
|
||||
assert http_opts.get('headers', {}).get('x-goog-api-client', '').startswith('browser-use/')
|
||||
|
||||
|
||||
def test_x_goog_api_client_header_with_dict_http_options():
|
||||
"""Test setting header when http_options is a dictionary (types.HttpOptionsDict)."""
|
||||
from google.genai import types
|
||||
|
||||
dict_opts: types.HttpOptionsDict = {
|
||||
'timeout': 45,
|
||||
'headers': {'another-header': 'another-value'},
|
||||
}
|
||||
chat = ChatGoogle(model='gemini-flash-latest', api_key='fake', http_options=dict_opts)
|
||||
params = chat._get_client_params()
|
||||
http_opts = params.get('http_options', {})
|
||||
|
||||
# Verify it preserves dictionary values and appends the tracking header
|
||||
assert http_opts.get('timeout') == 45
|
||||
assert http_opts.get('headers', {}).get('another-header') == 'another-value'
|
||||
assert http_opts.get('headers', {}).get('x-goog-api-client', '').startswith('browser-use/')
|
||||
|
||||
Reference in New Issue
Block a user