mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:40:44 +00:00
fix: allow private OpenAI image endpoints
This commit is contained in:
@@ -20,6 +20,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Agents/OpenAI: surface selected-model capacity failures from PI, Codex, and auto-reply harness paths with a model-switch hint instead of the generic empty-response error. Thanks @vincentkoc.
|
||||
- Providers/OpenAI: route `openai/gpt-image-2` through configured Codex OAuth directly when an `openai-codex` profile is active, instead of probing `OPENAI_API_KEY` first.
|
||||
- Providers/OpenAI: harden image generation auth routing and Codex OAuth response parsing so fallback only applies to public OpenAI API routes and bounded SSE results. Thanks @Takhoffman.
|
||||
- Providers/OpenAI: honor the private-network SSRF opt-in for OpenAI-compatible image generation endpoints, so trusted LocalAI/LAN `image_generate` routes work without disabling SSRF checks globally. Fixes #62879. Thanks @seitzbg.
|
||||
- Providers/OpenAI: stop advertising the removed `gpt-5.3-codex-spark` Codex model through fallback catalogs, and suppress stale rows with a GPT-5.5 recovery hint.
|
||||
- Plugins/QR: replace legacy `qrcode-terminal` QR rendering with bounded `qrcode-tui` helpers for plugin login/setup flows. (#65969) Thanks @vincentkoc.
|
||||
- Voice-call/realtime: wait for OpenAI session configuration before greeting or forwarding buffered audio, and reject non-allowlisted Twilio callers before stream setup. (#43501) Thanks @forrestblount.
|
||||
|
||||
@@ -236,6 +236,10 @@ does not first try `OPENAI_API_KEY` or silently fall back to an API key for that
|
||||
request. Configure `models.providers.openai` explicitly with an API key,
|
||||
custom base URL, or Azure endpoint when you want the direct OpenAI Images API
|
||||
route instead.
|
||||
If that custom image endpoint is on a trusted LAN/private address, also set
|
||||
`browser.ssrfPolicy.dangerouslyAllowPrivateNetwork: true`; OpenClaw keeps
|
||||
private/internal OpenAI-compatible image endpoints blocked unless this opt-in is
|
||||
present.
|
||||
|
||||
Generate:
|
||||
|
||||
|
||||
@@ -35,6 +35,10 @@ Codex OAuth uses the same `openai/gpt-image-2` model ref. When an
|
||||
through that same OAuth profile instead of first trying `OPENAI_API_KEY`.
|
||||
Explicit custom `models.providers.openai` image config, such as an API key or
|
||||
custom/Azure base URL, opts back into the direct OpenAI Images API route.
|
||||
For OpenAI-compatible LAN endpoints such as LocalAI, keep the custom
|
||||
`models.providers.openai.baseUrl` and explicitly opt in with
|
||||
`browser.ssrfPolicy.dangerouslyAllowPrivateNetwork: true`; private/internal
|
||||
image endpoints remain blocked by default.
|
||||
|
||||
3. Ask the agent: _"Generate an image of a friendly robot mascot."_
|
||||
|
||||
|
||||
@@ -316,6 +316,47 @@ describe("openai image generation provider", () => {
|
||||
expect(result.images).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("allows OpenAI-compatible private image endpoints when browser SSRF policy opts in", async () => {
|
||||
mockGeneratedPngResponse();
|
||||
|
||||
const provider = buildOpenAIImageGenerationProvider();
|
||||
const result = await provider.generateImage({
|
||||
provider: "openai",
|
||||
model: "flux2-klein",
|
||||
prompt: "A simple, clean illustration of a red apple with a green leaf",
|
||||
cfg: {
|
||||
browser: {
|
||||
ssrfPolicy: {
|
||||
dangerouslyAllowPrivateNetwork: true,
|
||||
},
|
||||
},
|
||||
models: {
|
||||
providers: {
|
||||
openai: {
|
||||
baseUrl: "http://192.168.1.15:8082/v1",
|
||||
apiKey: "local-noauth",
|
||||
models: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(resolveProviderHttpRequestConfigMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
baseUrl: "http://192.168.1.15:8082/v1",
|
||||
allowPrivateNetwork: true,
|
||||
}),
|
||||
);
|
||||
expect(postJsonRequestMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
url: "http://192.168.1.15:8082/v1/images/generations",
|
||||
allowPrivateNetwork: true,
|
||||
}),
|
||||
);
|
||||
expect(result.images).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("forwards generation count and custom size overrides", async () => {
|
||||
mockGeneratedPngResponse();
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
resolveProviderHttpRequestConfig,
|
||||
sanitizeConfiguredModelProviderRequest,
|
||||
} from "openclaw/plugin-sdk/provider-http";
|
||||
import { isPrivateNetworkOptInEnabled } from "openclaw/plugin-sdk/ssrf-runtime";
|
||||
import { OPENAI_DEFAULT_IMAGE_MODEL as DEFAULT_OPENAI_IMAGE_MODEL } from "./default-models.js";
|
||||
import { resolveConfiguredOpenAIBaseUrl } from "./shared.js";
|
||||
|
||||
@@ -190,6 +191,9 @@ function shouldAllowPrivateImageEndpoint(req: {
|
||||
if (req.provider === MOCK_OPENAI_PROVIDER_ID) {
|
||||
return true;
|
||||
}
|
||||
if (isPrivateNetworkOptInEnabled(req.cfg?.browser?.ssrfPolicy)) {
|
||||
return true;
|
||||
}
|
||||
const baseUrl = resolveConfiguredOpenAIBaseUrl(req.cfg);
|
||||
if (!baseUrl.startsWith("http://127.0.0.1:") && !baseUrl.startsWith("http://localhost:")) {
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user