mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-28 13:26:04 -06:00
fix(media-understanding): restore image description token default
Restore the describeImageWithModel default token budget to the helper-level 4096-token default instead of forcing 512 before resolution. Add regression coverage for the default and for smaller model caps, and record the user-facing fix in the changelog. Co-authored-by: scotthuang <scotthuang@tencent.com>
This commit is contained in:
@@ -271,6 +271,7 @@ Docs: https://docs.openclaw.ai
|
||||
- PDF tool: time out idle remote PDF body reads after 120 seconds so stalled remote documents return an error instead of wedging the session. Fixes #68649. (#84768) Thanks @luoyanglang.
|
||||
- Diagnostics/OpenTelemetry plugin: suppress handled OTLP exporter promise rejections so collector shutdowns no longer crash the Gateway. (#81085) Thanks @luoyanglang.
|
||||
- Agents/exec: omit raw command text and env values from denied exec failure logs while keeping safe correlation metadata. Fixes #85049. (#85140) Thanks @joshavant.
|
||||
- Media-understanding: restore the 4096-token default for image descriptions so reasoning-capable vision models no longer truncate before returning text, while preserving smaller model caps. (#84932) Thanks @scotthuang.
|
||||
- Media/audio: skip empty structured sherpa-onnx transcripts instead of treating the raw JSON payload as spoken text. (#84667) Thanks @TurboTheTurtle.
|
||||
- Agents/exec: preserve inherited XDG base-directory environment values for subprocesses while still rejecting agent-supplied XDG overrides. Fixes #84854. (#85139) Thanks @joshavant.
|
||||
- Node/Linux: keep `OPENCLAW_GATEWAY_TOKEN` out of generated systemd unit files by writing node service token values to a node-specific env file. (#84408)
|
||||
|
||||
@@ -811,7 +811,7 @@ describe("describeImageWithModel", () => {
|
||||
expect(context.messages).toHaveLength(1);
|
||||
expect(Object.keys(options).toSorted()).toEqual(["apiKey", "maxTokens", "signal", "timeoutMs"]);
|
||||
expect(options.apiKey).toBe("oauth-test");
|
||||
expect(options.maxTokens).toBe(512);
|
||||
expect(options.maxTokens).toBe(4096);
|
||||
expect(options.signal).toBeInstanceOf(AbortSignal);
|
||||
expect(options.timeoutMs).toBeGreaterThan(0);
|
||||
expect(options.timeoutMs).toBeLessThanOrEqual(1000);
|
||||
@@ -1290,4 +1290,77 @@ describe("describeImageWithModel", () => {
|
||||
expect(contentTypes).not.toContain("text");
|
||||
expect(contentTypes).toContain("image");
|
||||
});
|
||||
|
||||
it("defaults image-describe maxTokens to 4096 for reasoning-capable VLMs", async () => {
|
||||
discoverModelsMock.mockReturnValue({
|
||||
find: vi.fn(() => ({
|
||||
api: "openai-completions",
|
||||
provider: "agent-plan",
|
||||
id: "doubao-seed-2.0-pro",
|
||||
input: ["text", "image"],
|
||||
baseUrl: "https://ark.cn-beijing.volces.com/api/plan/v3",
|
||||
})),
|
||||
});
|
||||
completeMock.mockResolvedValue({
|
||||
role: "assistant",
|
||||
api: "openai-completions",
|
||||
provider: "agent-plan",
|
||||
model: "doubao-seed-2.0-pro",
|
||||
stopReason: "stop",
|
||||
timestamp: Date.now(),
|
||||
content: [{ type: "text", text: "ok" }],
|
||||
});
|
||||
|
||||
await describeImageWithModel({
|
||||
cfg: {},
|
||||
agentDir: "/tmp/openclaw-agent",
|
||||
provider: "agent-plan",
|
||||
model: "doubao-seed-2.0-pro",
|
||||
buffer: Buffer.from("png-bytes"),
|
||||
fileName: "image.png",
|
||||
mime: "image/png",
|
||||
prompt: "Describe the image.",
|
||||
timeoutMs: 1000,
|
||||
});
|
||||
|
||||
const [, , options] = requireFirstMockCall(completeMock, "image completion");
|
||||
expect(options.maxTokens).toBe(4096);
|
||||
});
|
||||
|
||||
it("caps image-describe maxTokens by the resolved model's own maxTokens", async () => {
|
||||
discoverModelsMock.mockReturnValue({
|
||||
find: vi.fn(() => ({
|
||||
api: "openai-completions",
|
||||
provider: "fake",
|
||||
id: "small-vlm",
|
||||
input: ["text", "image"],
|
||||
baseUrl: "https://example.test",
|
||||
maxTokens: 1024,
|
||||
})),
|
||||
});
|
||||
completeMock.mockResolvedValue({
|
||||
role: "assistant",
|
||||
api: "openai-completions",
|
||||
provider: "fake",
|
||||
model: "small-vlm",
|
||||
stopReason: "stop",
|
||||
timestamp: Date.now(),
|
||||
content: [{ type: "text", text: "ok" }],
|
||||
});
|
||||
|
||||
await describeImageWithModel({
|
||||
cfg: {},
|
||||
agentDir: "/tmp/openclaw-agent",
|
||||
provider: "fake",
|
||||
model: "small-vlm",
|
||||
buffer: Buffer.from("png-bytes"),
|
||||
fileName: "image.png",
|
||||
mime: "image/png",
|
||||
prompt: "Describe the image.",
|
||||
timeoutMs: 1000,
|
||||
});
|
||||
|
||||
const [, , options] = requireFirstMockCall(completeMock, "image completion");
|
||||
expect(options.maxTokens).toBe(1024);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -518,7 +518,7 @@ async function describeImagesWithModelInternal(
|
||||
promptInUserContent: shouldPlaceImagePromptInUserContent(model),
|
||||
});
|
||||
|
||||
const maxTokens = resolveImageToolMaxTokens(model.maxTokens, params.maxTokens ?? 512);
|
||||
const maxTokens = resolveImageToolMaxTokens(model.maxTokens, params.maxTokens);
|
||||
const completeImage = async (onPayload?: ProviderStreamOptions["onPayload"]) => {
|
||||
const payloadHandler = composeImageDescriptionPayloadHandlers(onPayload, options.onPayload);
|
||||
const timeoutMs = resolveImageDescriptionTimeoutMs(params.timeoutMs, startedAtMs);
|
||||
|
||||
Reference in New Issue
Block a user