





















@@ -130,6 +130,7 @@ const mocks = vi.hoisted(() => ({
130130]),
131131registerBuiltInMemoryEmbeddingProviders: vi.fn(),
132132buildMediaUnderstandingRegistry: vi.fn(() => new Map()),
133+convertHeicToJpeg: vi.fn(async () => Buffer.from("jpeg-normalized")),
133134isWebSearchProviderConfigured: vi.fn(() => false),
134135isWebFetchProviderConfigured: vi.fn(() => false),
135136modelsStatusCommand: vi.fn(
@@ -221,6 +222,15 @@ vi.mock("../media-understanding/provider-registry.js", () => ({
221222mocks.buildMediaUnderstandingRegistry as typeof import("../media-understanding/provider-registry.js").buildMediaUnderstandingRegistry,
222223}));
223224225+vi.mock("../media/image-ops.js", async (importOriginal) => {
226+const actual = await importOriginal<typeof import("../media/image-ops.js")>();
227+return {
228+ ...actual,
229+convertHeicToJpeg:
230+mocks.convertHeicToJpeg as typeof import("../media/image-ops.js").convertHeicToJpeg,
231+};
232+});
233+224234vi.mock("../plugins/memory-embedding-providers.js", () => ({
225235listMemoryEmbeddingProviders:
226236mocks.listMemoryEmbeddingProviders as unknown as typeof import("../plugins/memory-embedding-providers.js").listMemoryEmbeddingProviders,
@@ -333,6 +343,7 @@ describe("capability cli", () => {
333343mocks.setTtsProvider.mockClear();
334344mocks.resolveExplicitTtsOverrides.mockClear();
335345mocks.buildMediaUnderstandingRegistry.mockReset().mockReturnValue(new Map());
346+mocks.convertHeicToJpeg.mockClear();
336347mocks.createEmbeddingProvider.mockClear();
337348mocks.registerMemoryEmbeddingProvider.mockClear();
338349mocks.registerBuiltInMemoryEmbeddingProviders.mockClear();
@@ -561,6 +572,56 @@ describe("capability cli", () => {
561572);
562573});
563574575+it("normalizes HEIC files to JPEG before local model probes", async () => {
576+const tempInput = path.join(os.tmpdir(), `openclaw-model-run-image-${Date.now()}.heic`);
577+await fs.writeFile(tempInput, Buffer.from("heic-like"));
578+579+await runRegisteredCli({
580+register: registerCapabilityCli as (program: Command) => void,
581+argv: [
582+"capability",
583+"model",
584+"run",
585+"--prompt",
586+"describe this",
587+"--file",
588+tempInput,
589+"--json",
590+],
591+});
592+593+expect(mocks.convertHeicToJpeg).toHaveBeenCalledWith(Buffer.from("heic-like"));
594+expect(mocks.completeWithPreparedSimpleCompletionModel).toHaveBeenCalledWith(
595+expect.objectContaining({
596+context: expect.objectContaining({
597+messages: [
598+expect.objectContaining({
599+role: "user",
600+content: [
601+{ type: "text", text: "describe this" },
602+{
603+type: "image",
604+data: Buffer.from("jpeg-normalized").toString("base64"),
605+mimeType: "image/jpeg",
606+},
607+],
608+}),
609+],
610+}),
611+}),
612+);
613+expect(mocks.runtime.writeJson).toHaveBeenCalledWith(
614+expect.objectContaining({
615+inputs: [
616+expect.objectContaining({
617+path: tempInput,
618+mimeType: "image/jpeg",
619+}),
620+],
621+}),
622+);
623+});
624+564625it("rejects non-image files for model probes", async () => {
565626const tempInput = path.join(os.tmpdir(), `openclaw-model-run-audio-${Date.now()}.mp3`);
566627await fs.writeFile(tempInput, Buffer.from("not really audio"));
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。