mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 09:00:42 +00:00
fix: keep explicit image generation model exact
This commit is contained in:
@@ -12,6 +12,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
- Discord/replies: run `message_sending` plugin hooks for Discord reply delivery, including DM targets, so plugins can transform or cancel outbound Discord replies consistently with other channels. Fixes #59350. (#71094) Thanks @wei840222.
|
||||
- Control UI/commands: carry provider-owned thinking option ids/labels in session rows and defaults so fresh sessions show and accept dynamic modes such as `adaptive`, `xhigh`, and `max`. Fixes #71269. Thanks @Young-Khalil.
|
||||
- Image generation: make explicit `model=` overrides exact-only so failed `openai/gpt-image-2` requests no longer fall through to Gemini or other configured providers, and update `image_generate list` to mention OpenAI Codex OAuth as valid auth for `openai/gpt-image-2`. Fixes #71290 and #71231. Thanks @Young-Khalil and @steipete.
|
||||
- Providers/GitHub Copilot: keep the plugin stream wrapper from claiming transport selection before OpenClaw picks a boundary-aware stream path, avoiding Pi's stale fallback Copilot headers on normal model turns. Thanks @steipete.
|
||||
- Discord/subagents: pass runtime config into thread-bound native subagent binding and require it at the helper boundary so Discord channel resolution keeps account-aware config. Fixes #71054. (#70945) Thanks @jai.
|
||||
- Slack/Assistant: accept Slack Assistant DM `message_changed` events when their metadata identifies the human sender, while continuing to drop self-authored bot edits. Fixes #55445. Thanks @AlfredPros.
|
||||
|
||||
@@ -172,10 +172,13 @@ When generating an image, OpenClaw tries providers in this order:
|
||||
- current default provider first
|
||||
- remaining registered image-generation providers in provider-id order
|
||||
|
||||
If a provider fails (auth error, rate limit, etc.), the next candidate is tried automatically. If all fail, the error includes details from each attempt.
|
||||
If a provider fails (auth error, rate limit, etc.), the next configured candidate is tried automatically. If all fail, the error includes details from each attempt.
|
||||
|
||||
Notes:
|
||||
|
||||
- A per-call `model` override is exact: OpenClaw tries only that provider/model
|
||||
and does not continue to configured primary/fallback or auto-detected
|
||||
providers.
|
||||
- Auto-detection is auth-aware. A provider default only enters the candidate list
|
||||
when OpenClaw can actually authenticate that provider.
|
||||
- Auto-detection is enabled by default. Set
|
||||
|
||||
@@ -1070,7 +1070,9 @@ describe("createImageGenerateTool", () => {
|
||||
expect(text).toContain("gemini-3.1-flash-image-preview");
|
||||
expect(text).toContain("gemini-3-pro-image-preview");
|
||||
expect(text).toContain("auth: set GEMINI_API_KEY / GOOGLE_API_KEY to use google/*");
|
||||
expect(text).toContain("auth: set OPENAI_API_KEY to use openai/*");
|
||||
expect(text).toContain(
|
||||
"auth: set OPENAI_API_KEY or configure OpenAI Codex OAuth for openai/gpt-image-2",
|
||||
);
|
||||
expect(text).toContain("editing up to 5 refs");
|
||||
expect(text).toContain("aspect ratios 1:1, 16:9");
|
||||
expect(result).toMatchObject({
|
||||
|
||||
@@ -160,6 +160,19 @@ function getImageGenerationProviderAuthEnvVars(providerId: string): string[] {
|
||||
return getProviderEnvVars(providerId);
|
||||
}
|
||||
|
||||
function formatImageGenerationAuthHint(provider: {
|
||||
id: string;
|
||||
authEnvVars: readonly string[];
|
||||
}): string | undefined {
|
||||
if (provider.id === "openai") {
|
||||
return "set OPENAI_API_KEY or configure OpenAI Codex OAuth for openai/gpt-image-2";
|
||||
}
|
||||
if (provider.authEnvVars.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
return `set ${provider.authEnvVars.join(" / ")} to use ${provider.id}/*`;
|
||||
}
|
||||
|
||||
export function resolveImageGenerationModelConfigForTool(params: {
|
||||
cfg?: OpenClawConfig;
|
||||
agentDir?: string;
|
||||
@@ -592,13 +605,12 @@ export function createImageGenerateTool(options?: {
|
||||
provider.models.length > 0
|
||||
? `models: ${provider.models.join(", ")}`
|
||||
: "models: unknown";
|
||||
const authHint = formatImageGenerationAuthHint(provider);
|
||||
return [
|
||||
`${provider.id}${provider.defaultModel ? ` (default ${provider.defaultModel})` : ""}`,
|
||||
` ${modelLine}`,
|
||||
` configured: ${provider.configured ? "yes" : "no"}`,
|
||||
...(provider.authEnvVars.length > 0
|
||||
? [` auth: set ${provider.authEnvVars.join(" / ")} to use ${provider.id}/*`]
|
||||
: []),
|
||||
...(authHint ? [` auth: ${authHint}`] : []),
|
||||
...(caps.length > 0 ? [` capabilities: ${caps.join("; ")}`] : []),
|
||||
];
|
||||
});
|
||||
|
||||
@@ -119,6 +119,33 @@ describe("media-generation runtime shared candidates", () => {
|
||||
|
||||
expect(candidates).toEqual([{ provider: "google", model: "gemini-3.1-flash-image-preview" }]);
|
||||
});
|
||||
|
||||
it("treats an explicit model override as exact-only", () => {
|
||||
const candidates = resolveCapabilityModelCandidates({
|
||||
cfg: {
|
||||
agents: {
|
||||
defaults: {
|
||||
mediaGenerationAutoProviderFallback: false,
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig,
|
||||
modelConfig: {
|
||||
primary: "google/gemini-3.1-flash-image-preview",
|
||||
fallbacks: ["fal/fal-ai/flux/dev"],
|
||||
},
|
||||
modelOverride: "openai/gpt-image-2",
|
||||
parseModelRef,
|
||||
listProviders: () => [
|
||||
{
|
||||
id: "google",
|
||||
defaultModel: "gemini-3.1-flash-image-preview",
|
||||
isConfigured: () => true,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(candidates).toEqual([{ provider: "openai", model: "gpt-image-2" }]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("media-generation runtime shared normalization", () => {
|
||||
|
||||
@@ -178,6 +178,11 @@ export function resolveCapabilityModelCandidates(params: {
|
||||
candidates.push(parsed);
|
||||
};
|
||||
|
||||
const override = params.parseModelRef(params.modelOverride);
|
||||
if (override) {
|
||||
return [override];
|
||||
}
|
||||
|
||||
add(params.modelOverride);
|
||||
add(resolveAgentModelPrimaryValue(params.modelConfig));
|
||||
for (const fallback of resolveAgentModelFallbackValues(params.modelConfig)) {
|
||||
|
||||
Reference in New Issue
Block a user