Skip to content

fix(google): honor models.providers.google.request.allowPrivateNetwork in TTS#71723

Merged
steipete merged 2 commits intoopenclaw:mainfrom
ro-hansolo:fix/google-tts-honor-allow-private-network
Apr 25, 2026
Merged

fix(google): honor models.providers.google.request.allowPrivateNetwork in TTS#71723
steipete merged 2 commits intoopenclaw:mainfrom
ro-hansolo:fix/google-tts-honor-allow-private-network

Conversation

@ro-hansolo
Copy link
Copy Markdown
Contributor

Summary

  • Problem: Google TTS provider ignores models.providers.google.request.allowPrivateNetwork config that image-gen + media-understanding honor. Always-blocked when the configured Google API endpoint resolves to a private IP. Silent fallback to a different speech provider.
  • Why it matters: Documented config knob is non-functional for one capability. Users with proxies / internal backends / test mocks lose Google TTS.
  • What changed: extensions/google/speech-provider.ts now threads sanitizeConfiguredModelProviderRequest(req.cfg?.models?.providers?.google?.request) into resolveGoogleGenerativeAiHttpRequestConfig, mirroring image-generation-provider.ts.
  • What did NOT change: Default behavior identical when models.providers.google.request unset (which is the case for vast majority of users).

Change Type (select all)

  • Bug fix

Scope (select all touched areas)

  • Integrations

Linked Issue/PR

Root Cause

Regression Test Plan

  • Coverage level that should have caught this:
    • Unit test
  • Target test or file: extensions/google/speech-provider.test.ts
  • Scenario the test should lock in: When models.providers.google.request.allowPrivateNetwork: true is configured, calling provider.synthesize(...) should result in postJsonRequest being called with allowPrivateNetwork: true.
  • Why this is the smallest reliable guardrail: Mirrors the existing test in extensions/google/image-generation-provider.test.ts ("honors configured private-network opt-in for Google image generation"). Catches the asymmetry that allowed this regression.
  • Existing test that already covers this: None for TTS — only image gen had coverage; that's why the regression was not caught for the TTS path.

User-visible / Behavior Changes

  • TTS now honors models.providers.google.request.allowPrivateNetwork config like other Google capabilities.
  • Default behavior unchanged when that config is unset (still defaults to false).

Diagram (if applicable)

N/A

Security Impact (required)

  • New permissions/capabilities? No (extends an existing, documented config knob to one missed call site)
  • Secrets/tokens handling changed? No
  • New/changed network calls? No (same endpoint, same auth)
  • Command/tool execution surface changed? No
  • Data access scope changed? No

Repro + Verification

Environment

  • OS: Debian 12 (gateway VM)
  • Runtime: node 24.x
  • Model/provider: Google TTS via messages.tts.providers.google + custom models.providers.google.baseUrl
  • Integration/channel: WhatsApp (any messaging channel reproduces)
  • Relevant config: models.providers.google.request.allowPrivateNetwork: true; messages.tts.provider: "google"; messages.tts.providers.google.{model, voiceName} configured

Steps

  1. Configure models.providers.google.{baseUrl, models, request.allowPrivateNetwork: true} where the resolved baseUrl points at a private IP (e.g. via /etc/hosts redirecting generativelanguage.googleapis.com to 127.0.0.1 for a local proxy).
  2. Configure messages.tts.provider: "google" and messages.tts.providers.google.{model, voiceName}.
  3. Trigger a TTS reply (any messaging channel).

Expected

  • TTS request reaches the configured baseUrl; receives audio response.

Actual (before this fix)

  • [security] blocked URL fetch (url-fetch) targetOrigin=<configured baseUrl> reason=Blocked: resolves to private/internal/special-use IP address. OC silently falls back to a different speech provider (e.g. Microsoft Edge), losing Google-specific config (voiceName, audioProfile, etc.).

Evidence

Human Verification (required)

  • Verified scenarios:
    • Default (no models.providers.google.request configured): TTS request behavior unchanged — existing tests pass.
    • request.allowPrivateNetwork: true set: TTS reaches configured private-IP backend successfully — new test passes; verified on a deploy with the patched build.
  • Edge cases checked: synthesizeTelephony path uses the same threading via the second call site.
  • What I did not verify: live audio playback from a hostile-private-IP backend; testing was via local fetch mocks (the regression test) plus a controlled production deploy.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? No
  • Migration needed? No

Risks and Mitigations

  • Risk: sanitizeConfiguredModelProviderRequest returns undefined for unset config; passing undefined to resolveGoogleGenerativeAiHttpRequestConfig.request is the same as omitting it (default-false path).
    • Mitigation: Verified against image-gen sibling which does the exact same.

AI-assisted (Claude wrote this PR after diagnosing the bug; lightly tested locally + verified end-to-end on a production gateway deploy). Human reviewed every diff hunk before push.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 25, 2026

Greptile Summary

This PR fixes a bug where synthesizeGoogleTtsPcm in extensions/google/speech-provider.ts ignored the models.providers.google.request.allowPrivateNetwork config, causing TTS to always block private-IP endpoints. The fix threads sanitizeConfiguredModelProviderRequest(req.cfg?.models?.providers?.google?.request) through both synthesize and synthesizeTelephony call sites, exactly mirroring the pattern already present in image-generation-provider.ts and media-understanding-provider.ts.

Confidence Score: 5/5

Safe to merge — minimal, targeted fix that aligns speech-provider with the established pattern from sibling providers.

The change is a clean one-line-per-call-site addition that exactly mirrors the already-reviewed pattern in image-generation-provider.ts. Both synthesize and synthesizeTelephony call sites are updated consistently. The new test correctly spies on postJsonRequest (with call-through to the stubbed fetch) to assert allowPrivateNetwork: true is forwarded. Default behavior when the config key is absent is unchanged. No new logic paths, no security surface changes.

No files require special attention.

Reviews (1): Last reviewed commit: "fix(google): honor models.providers.goog..." | Re-trigger Greptile

@steipete steipete force-pushed the fix/google-tts-honor-allow-private-network branch from 8e71c17 to 2023f88 Compare April 25, 2026 19:30
@steipete steipete force-pushed the fix/google-tts-honor-allow-private-network branch from 2023f88 to d384312 Compare April 25, 2026 19:43
ro-hansolo and others added 2 commits April 25, 2026 20:52
…k in TTS

Image generation and media understanding both thread the
sanitized models.providers.google.request config (including
allowPrivateNetwork) into resolveGoogleGenerativeAiHttpRequestConfig.
Speech synthesis omitted that arg, so TTS always saw
allowPrivateNetwork: false regardless of config — silently falling
back to a different speech provider when the configured Google TTS
endpoint resolved to a private/internal IP (proxies, custom backends,
test mocks).

Mirror the image-generation-provider pattern: thread request through
synthesizeGoogleTtsPcm at both call sites (synthesize and
synthesizeTelephony).

Follow-up to openclaw#67216.
@steipete steipete force-pushed the fix/google-tts-honor-allow-private-network branch from d384312 to 5c596f9 Compare April 25, 2026 19:53
@steipete steipete merged commit 8fb24ac into openclaw:main Apr 25, 2026
63 checks passed
@steipete
Copy link
Copy Markdown
Contributor

Landed via rebase onto main.

  • Local gate: pnpm check:changed on the final rebased head; 21 files / 194 tests passed.
  • Focused gate: pnpm test extensions/google/speech-provider.test.ts extensions/google/api.test.ts extensions/google/image-generation-provider.test.ts extensions/google/media-understanding-provider.video.test.ts; 4 files / 38 tests passed.
  • Live smoke: Google TTS via ~/.profile credentials; regular WAV and telephony PCM paths both succeeded with request.allowPrivateNetwork: true.
  • PR source head: 5c596f9
  • Landed commits: cab66c5 and 8fb24ac

Thanks @ro-hansolo!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants