fix: restore allowPrivateNetwork for self-hosted STT endpoints (#66692) (thanks @jhsmith409)

* fix(audio): restore allowPrivateNetwork for self-hosted STT endpoints

resolveProviderExecutionContext built the request object passed to
transcribeAudio using only sanitizeConfiguredProviderRequest on the
tool-level config and entry — which strips allowPrivateNetwork. The
provider-level request config (models.providers.*.request) was never
included in the merge, so allowPrivateNetwork:true was silently dropped.

Additionally, resolveProviderRequestPolicyConfig only read allowPrivate
Network from params.allowPrivateNetwork (a direct parameter) and ignored
params.request?.allowPrivateNetwork even when it was present.

Fix both gaps:
- runner.entries.ts: use mergeModelProviderRequestOverrides with
  sanitizeConfiguredModelProviderRequest(providerConfig?.request) so
  models.providers.*.request.allowPrivateNetwork flows through to the
  media execution context
- provider-request-config.ts: fall back to params.request?.allowPrivate
  Network when params.allowPrivateNetwork is undefined

Fixes #66691. Regression introduced in v2026.4.14.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(media-understanding): assert allowPrivateNetwork flows through resolveProviderExecutionContext

Regression test for the bug where providerConfig.request.allowPrivateNetwork
was dropped when building the AudioTranscriptionRequest passed to media
providers. Verifies that setting allowPrivateNetwork in the provider config
reaches the provider's request object after the fix to use
mergeModelProviderRequestOverrides + sanitizeConfiguredModelProviderRequest.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(media-understanding): tighten allowPrivateNetwork regression types

* fix: restore allowPrivateNetwork for self-hosted STT endpoints (#66692) (thanks @jhsmith409)

---------

Co-authored-by: Jim Smith <jhsmith0@me.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
This commit is contained in:
Jim Smith
2026-04-14 22:35:37 -04:00
committed by GitHub
parent b1d03b4057
commit 0c0463b2b7
4 changed files with 58 additions and 2 deletions

View File

@@ -40,6 +40,7 @@ Docs: https://docs.openclaw.ai
- Telegram/native commands: restore plugin-registry-backed auto defaults for native commands and native skills so Telegram slash commands keep registering when `commands.native` and `commands.nativeSkills` stay on `auto`. (#66843) Thanks @kashevk0.
- fix(bluebubbles): replay missed webhook messages after gateway restart via a persistent per-account cursor and `/api/v1/message/query?after=<ts>` pass, so messages delivered while the gateway was down no longer disappear. Uses the existing `processMessage` path and is deduped by #66816's inbound GUID cache. (#66857, #66721) Thanks @omarshahine.
- Telegram/native commands: keep Telegram command-sync cache process-local so gateway restarts re-register the menu instead of trusting stale on-disk sync state after Telegram cleared commands out-of-band. (#66730) Thanks @nightq.
- Audio/self-hosted STT: restore `models.providers.*.request.allowPrivateNetwork` for audio transcription so private or LAN speech-to-text endpoints stop tripping SSRF blocks after the v2026.4.14 regression. (#66692) Thanks @jhsmith409.
## 2026.4.14

View File

@@ -661,7 +661,7 @@ export function resolveProviderRequestPolicyConfig(
tls: resolveTlsOverride(params.request?.tls),
policy,
capabilities,
allowPrivateNetwork: params.allowPrivateNetwork ?? false,
allowPrivateNetwork: params.allowPrivateNetwork ?? params.request?.allowPrivateNetwork ?? false,
};
}

View File

@@ -6,7 +6,9 @@ import {
} from "../agents/api-key-rotation.js";
import { requireApiKey, resolveApiKeyForProvider } from "../agents/model-auth.js";
import {
mergeModelProviderRequestOverrides,
mergeProviderRequestOverrides,
sanitizeConfiguredModelProviderRequest,
sanitizeConfiguredProviderRequest,
} from "../agents/provider-request-config.js";
import type { MsgContext } from "../auto-reply/templating.js";
@@ -429,7 +431,8 @@ async function resolveProviderExecutionContext(params: {
...sanitizeProviderHeaders(params.entry.headers as Record<string, unknown> | undefined),
};
const headers = Object.keys(mergedHeaders).length > 0 ? mergedHeaders : undefined;
const request = mergeProviderRequestOverrides(
const request = mergeModelProviderRequestOverrides(
sanitizeConfiguredModelProviderRequest(providerConfig?.request),
sanitizeConfiguredProviderRequest(params.config?.request),
sanitizeConfiguredProviderRequest(params.entry.request),
);

View File

@@ -178,4 +178,56 @@ describe("runCapability proxy fetch passthrough", () => {
});
expect(seenFetchFn).toBeUndefined();
});
it("passes allowPrivateNetwork to audio provider when set in providerConfig.request", async () => {
let seenRequest: AudioTranscriptionRequest["request"];
await withAudioFixture("openclaw-audio-allowprivatenetwork", async ({ ctx, media, cache }) => {
const providerRegistry = buildProviderRegistry({
openai: {
id: "openai",
capabilities: ["audio"],
transcribeAudio: async (req: AudioTranscriptionRequest) => {
seenRequest = req.request;
return { text: "ok", model: req.model };
},
},
});
const cfg = {
models: {
providers: {
openai: {
apiKey: "test-key", // pragma: allowlist secret
request: {
allowPrivateNetwork: true,
},
models: [],
},
},
},
tools: {
media: {
audio: {
enabled: true,
models: [{ provider: "openai", model: "whisper-1" }],
},
},
},
} as unknown as OpenClawConfig;
const result = await runCapability({
capability: "audio",
cfg,
ctx,
attachments: cache,
media,
providerRegistry,
});
expect(result.outputs[0]?.text).toBe("ok");
});
expect(seenRequest?.allowPrivateNetwork).toBe(true);
});
});