mirror of
https://github.com/calesthio/OpenMontage.git
synced 2026-08-15 13:14:38 +08:00
scope Google TTS credentials safely
This commit is contained in:
36
tests/tools/test_google_tts_scoped_key.py
Normal file
36
tests/tools/test_google_tts_scoped_key.py
Normal file
@@ -0,0 +1,36 @@
|
||||
from tools.audio.google_tts import GoogleTTS
|
||||
from tools.google_credentials import has_google_credentials
|
||||
|
||||
|
||||
def test_tts_only_key_does_not_enable_shared_google_providers(monkeypatch):
|
||||
monkeypatch.setenv("GOOGLE_TTS_API_KEY", "test-tts-only-key")
|
||||
monkeypatch.delenv("GOOGLE_API_KEY", raising=False)
|
||||
monkeypatch.delenv("GEMINI_API_KEY", raising=False)
|
||||
monkeypatch.delenv("GOOGLE_APPLICATION_CREDENTIALS", raising=False)
|
||||
|
||||
assert GoogleTTS().get_status().value == "available"
|
||||
assert has_google_credentials() is False
|
||||
|
||||
|
||||
def test_tts_key_uses_header_and_is_redacted_from_errors(monkeypatch, tmp_path):
|
||||
import requests
|
||||
|
||||
secret = "test-production-shaped-tts-key"
|
||||
monkeypatch.setenv("GOOGLE_TTS_API_KEY", secret)
|
||||
monkeypatch.delenv("GOOGLE_API_KEY", raising=False)
|
||||
monkeypatch.delenv("GEMINI_API_KEY", raising=False)
|
||||
monkeypatch.delenv("GOOGLE_APPLICATION_CREDENTIALS", raising=False)
|
||||
|
||||
def fail_request(url, **kwargs):
|
||||
assert kwargs["headers"]["x-goog-api-key"] == secret
|
||||
assert "params" not in kwargs
|
||||
raise requests.HTTPError(f"403 for {url}?key={secret}")
|
||||
|
||||
monkeypatch.setattr(requests, "post", fail_request)
|
||||
result = GoogleTTS().execute(
|
||||
{"text": "safe test sentence", "output_path": str(tmp_path / "speech.mp3")}
|
||||
)
|
||||
|
||||
assert result.success is False
|
||||
assert secret not in result.error
|
||||
assert "[REDACTED]" in result.error
|
||||
@@ -27,7 +27,6 @@ from tools.base_tool import (
|
||||
from tools.google_credentials import (
|
||||
get_access_token,
|
||||
service_account_configured,
|
||||
has_google_credentials,
|
||||
)
|
||||
|
||||
|
||||
@@ -44,7 +43,8 @@ class GoogleTTS(BaseTool):
|
||||
|
||||
dependencies = []
|
||||
install_instructions = (
|
||||
"Auth option A — API key: set GOOGLE_API_KEY (or GEMINI_API_KEY) to a\n"
|
||||
"Auth option A — TTS-only API key: set GOOGLE_TTS_API_KEY.\n"
|
||||
" GOOGLE_API_KEY or GEMINI_API_KEY remain supported for broader Google setups.\n"
|
||||
" Google Cloud API key with Text-to-Speech enabled.\n"
|
||||
" Enable the API at https://console.cloud.google.com/apis/library/texttospeech.googleapis.com\n"
|
||||
"Auth option B — service account: set GOOGLE_APPLICATION_CREDENTIALS to the\n"
|
||||
@@ -149,12 +149,16 @@ class GoogleTTS(BaseTool):
|
||||
}
|
||||
|
||||
def _get_api_key(self) -> str | None:
|
||||
return os.environ.get("GOOGLE_API_KEY") or os.environ.get("GEMINI_API_KEY")
|
||||
return (
|
||||
os.environ.get("GOOGLE_TTS_API_KEY")
|
||||
or os.environ.get("GOOGLE_API_KEY")
|
||||
or os.environ.get("GEMINI_API_KEY")
|
||||
)
|
||||
|
||||
def get_status(self) -> ToolStatus:
|
||||
# Available via either an API key or a service-account JSON. Both paths
|
||||
# are honoured by execute() — so this no longer over-reports.
|
||||
if has_google_credentials():
|
||||
if self._get_api_key() or service_account_configured():
|
||||
return ToolStatus.AVAILABLE
|
||||
return ToolStatus.UNAVAILABLE
|
||||
|
||||
@@ -204,7 +208,11 @@ class GoogleTTS(BaseTool):
|
||||
try:
|
||||
result = self._generate(inputs, api_key=api_key, bearer_token=bearer_token)
|
||||
except Exception as exc:
|
||||
return ToolResult(success=False, error=f"Google TTS failed: {exc}")
|
||||
safe_error = str(exc)
|
||||
for credential in (api_key, bearer_token):
|
||||
if credential:
|
||||
safe_error = safe_error.replace(credential, "[REDACTED]")
|
||||
return ToolResult(success=False, error=f"Google TTS failed: {safe_error}")
|
||||
|
||||
result.duration_seconds = round(time.time() - start, 2)
|
||||
result.cost_usd = self.estimate_cost(inputs)
|
||||
@@ -266,16 +274,14 @@ class GoogleTTS(BaseTool):
|
||||
url = f"https://texttospeech.googleapis.com/{api_version}/text:synthesize"
|
||||
|
||||
headers = {"Content-Type": "application/json"}
|
||||
params: dict[str, str] = {}
|
||||
if bearer_token:
|
||||
headers["Authorization"] = f"Bearer {bearer_token}"
|
||||
elif api_key:
|
||||
params["key"] = api_key
|
||||
headers["x-goog-api-key"] = api_key
|
||||
|
||||
response = requests.post(
|
||||
url,
|
||||
headers=headers,
|
||||
params=params,
|
||||
json=payload,
|
||||
timeout=120,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user