From ea9f791a68add4bf1df86157c08df33bf544a652 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 4 Jun 2026 16:38:27 -0400 Subject: [PATCH] docs: document image and pdf tool tests --- .../image-tool.custom-provider-auth.regression.test.ts | 4 ++++ src/agents/tools/image-tool.ollama.live.test.ts | 3 +++ src/agents/tools/image-tool.providers.live.test.ts | 4 ++++ src/agents/tools/image-tool.test.ts | 6 ++++++ src/agents/tools/pdf-tool.model-catalog.test.ts | 2 ++ src/agents/tools/pdf-tool.test-support.ts | 2 ++ src/agents/tools/pdf-tool.test.ts | 8 ++++++++ 7 files changed, 29 insertions(+) diff --git a/src/agents/tools/image-tool.custom-provider-auth.regression.test.ts b/src/agents/tools/image-tool.custom-provider-auth.regression.test.ts index b22540c9f109..fe0aa868a478 100644 --- a/src/agents/tools/image-tool.custom-provider-auth.regression.test.ts +++ b/src/agents/tools/image-tool.custom-provider-auth.regression.test.ts @@ -1,3 +1,5 @@ +// Regression tests for custom image providers whose credentials live in +// models.json rather than environment variables or auth profiles. import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; @@ -147,6 +149,8 @@ describe("image custom provider auth regression", () => { }); it("executes deferred image tool discovery with config-backed auth and runtime key resolution", async () => { + // This covers the production deferred-discovery path: registration can + // avoid auth work, but execution still resolves the config-backed key. await withEmptyAgentDir(async (agentDir) => { const cfg = createUserReportedConfig(); const auth = await getApiKeyForModel({ diff --git a/src/agents/tools/image-tool.ollama.live.test.ts b/src/agents/tools/image-tool.ollama.live.test.ts index 2edbe53c54ff..7e8b2a728d96 100644 --- a/src/agents/tools/image-tool.ollama.live.test.ts +++ b/src/agents/tools/image-tool.ollama.live.test.ts @@ -1,3 +1,4 @@ +// Live Ollama image tool smoke test for providerless local vision-model config. import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; @@ -12,6 +13,8 @@ const OLLAMA_BASE_URL = const OLLAMA_IMAGE_MODEL = process.env.OPENCLAW_LIVE_OLLAMA_IMAGE_MODEL?.trim() || "qwen2.5vl:7b"; function resolveLiveNumCtx(): number { + // Ollama vision models can fail with tiny context windows; clamp live smoke + // config to a usable minimum while still letting operators override it. const parsed = Number.parseInt(process.env.OPENCLAW_LIVE_OLLAMA_IMAGE_NUM_CTX ?? "2048", 10); return Number.isFinite(parsed) ? Math.max(512, parsed) : 2048; } diff --git a/src/agents/tools/image-tool.providers.live.test.ts b/src/agents/tools/image-tool.providers.live.test.ts index 6509e2624bd0..d4437d290c45 100644 --- a/src/agents/tools/image-tool.providers.live.test.ts +++ b/src/agents/tools/image-tool.providers.live.test.ts @@ -1,3 +1,5 @@ +// Live image provider tests verify real provider calls downscale large local +// images before sending them to OpenAI or Anthropic. import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; @@ -80,6 +82,8 @@ function createLargeCenterRedPng(size: number): Buffer { } function readJpegDimensions(buffer: Buffer): { width: number; height: number } { + // The provider hook receives JPEG bytes after optimization; parsing SOF + // markers keeps the downscale proof independent from image libraries. let offset = 2; while (offset + 9 < buffer.length) { if (buffer[offset] !== 0xff) { diff --git a/src/agents/tools/image-tool.test.ts b/src/agents/tools/image-tool.test.ts index 8a1ee74a1ce3..e074bdf62fcf 100644 --- a/src/agents/tools/image-tool.test.ts +++ b/src/agents/tools/image-tool.test.ts @@ -1,3 +1,5 @@ +// Image tool tests cover model routing, provider auth, path safety, inbound +// media refs, data URLs, response validation, and compression policy. import fsSync from "node:fs"; import fs from "node:fs/promises"; import os from "node:os"; @@ -23,6 +25,8 @@ import { testing, createImageTool, resolveImageModelConfigForTool } from "./imag import { resolveMediaToolInboundRoots } from "./media-tool-shared.js"; function jsonRoundTrip(value: T): T { + // Anthropic rejects union-heavy schemas, so schema snapshots must survive the + // same JSON serialization path used for model-facing tool definitions. const serialized = JSON.stringify(value); return JSON.parse(serialized) as T; } @@ -305,6 +309,8 @@ function createLargeColorBlockPng(size: number): Buffer { } function readJpegDimensions(buffer: Buffer): { width: number; height: number } { + // The tests inspect JPEG SOF markers directly so resize assertions do not + // depend on an external decoder. let offset = 2; while (offset + 9 < buffer.length) { if (buffer[offset] !== 0xff) { diff --git a/src/agents/tools/pdf-tool.model-catalog.test.ts b/src/agents/tools/pdf-tool.model-catalog.test.ts index d7b79b1a2a4f..f7ae3201594a 100644 --- a/src/agents/tools/pdf-tool.model-catalog.test.ts +++ b/src/agents/tools/pdf-tool.model-catalog.test.ts @@ -1,3 +1,5 @@ +// Model catalog document support tests keep PDF routing tied to declared input +// capabilities. import { describe, expect, it } from "vitest"; import { modelSupportsDocument } from "../model-catalog.js"; diff --git a/src/agents/tools/pdf-tool.test-support.ts b/src/agents/tools/pdf-tool.test-support.ts index cacb4e910111..2c55ad35673a 100644 --- a/src/agents/tools/pdf-tool.test-support.ts +++ b/src/agents/tools/pdf-tool.test-support.ts @@ -1,3 +1,5 @@ +// Shared PDF tool test helpers provide isolated agent dirs and scrub provider +// auth variables for deterministic model-resolution tests. import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; diff --git a/src/agents/tools/pdf-tool.test.ts b/src/agents/tools/pdf-tool.test.ts index 5e8e747481dc..8fc8f1bc57a2 100644 --- a/src/agents/tools/pdf-tool.test.ts +++ b/src/agents/tools/pdf-tool.test.ts @@ -1,3 +1,5 @@ +// PDF tool tests cover model discovery, input validation, managed inbound refs, +// native document providers, extraction fallback, and model-facing schema. import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; @@ -116,6 +118,8 @@ async function stubPdfToolInfra( modelFound?: boolean; }, ) { + // Keep PDF tool tests focused on orchestration; provider discovery, auth, and + // remote media loading are replaced with narrow spies at the module boundary. const loadSpy = vi.spyOn(webMedia, "loadWebMediaRaw"); if (params?.mockLoad !== false) { loadSpy.mockResolvedValue(FAKE_PDF_MEDIA as never); @@ -156,6 +160,8 @@ async function stubPdfToolInfra( async function withManagedInboundPdf( run: (params: { stateDir: string; mediaId: string; mediaPath: string }) => Promise, ) { + // Managed inbound PDFs live under state and may be addressed by claim-check + // IDs or absolute paths even when workspace-only policy is active. const stateDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-pdf-managed-inbound-")); const inboundDir = path.join(stateDir, "media", "inbound"); const mediaId = "claim-check-test.pdf"; @@ -483,6 +489,8 @@ describe("createPdfTool", () => { }); it("uses native PDF path without eager extraction", async () => { + // Document-capable providers receive the PDF bytes directly; extraction is + // reserved for text-only model paths. await withTempPdfAgentDir(async (agentDir) => { const workspaceDir = path.join(agentDir, "workspace"); await stubPdfToolInfra(agentDir, { provider: "anthropic", input: ["text", "document"] });