mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 18:08:27 +00:00
fix(agents): make image sanitization dimension configurable
This commit is contained in:
@@ -1,5 +1,3 @@
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import type { AgentMessage } from "@mariozechner/pi-agent-core";
|
||||
import {
|
||||
createAgentSession,
|
||||
@@ -7,10 +5,14 @@ import {
|
||||
SessionManager,
|
||||
SettingsManager,
|
||||
} from "@mariozechner/pi-coding-agent";
|
||||
import { resolveHeartbeatPrompt } from "../../auto-reply/heartbeat.js";
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import type { ReasoningLevel, ThinkLevel } from "../../auto-reply/thinking.js";
|
||||
import { resolveChannelCapabilities } from "../../config/channel-capabilities.js";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import type { ExecElevatedDefaults } from "../bash-tools.js";
|
||||
import type { EmbeddedPiCompactResult } from "./types.js";
|
||||
import { resolveHeartbeatPrompt } from "../../auto-reply/heartbeat.js";
|
||||
import { resolveChannelCapabilities } from "../../config/channel-capabilities.js";
|
||||
import { getMachineDisplayName } from "../../infra/machine-name.js";
|
||||
import { getGlobalHookRunner } from "../../plugins/hook-runner-global.js";
|
||||
import { type enqueueCommand, enqueueCommandInLane } from "../../process/command-queue.js";
|
||||
@@ -24,7 +26,6 @@ import { normalizeMessageChannel } from "../../utils/message-channel.js";
|
||||
import { isReasoningTagProvider } from "../../utils/provider-utils.js";
|
||||
import { resolveOpenClawAgentDir } from "../agent-paths.js";
|
||||
import { resolveSessionAgentIds } from "../agent-scope.js";
|
||||
import type { ExecElevatedDefaults } from "../bash-tools.js";
|
||||
import { makeBootstrapWarn, resolveBootstrapContextForRun } from "../bootstrap-files.js";
|
||||
import { listChannelSupportedActions, resolveChannelMessageToolHints } from "../channel-tools.js";
|
||||
import { formatUserTime, resolveUserTimeFormat, resolveUserTimezone } from "../date-time.js";
|
||||
@@ -81,7 +82,6 @@ import {
|
||||
createSystemPromptOverride,
|
||||
} from "./system-prompt.js";
|
||||
import { splitSdkTools } from "./tool-split.js";
|
||||
import type { EmbeddedPiCompactResult } from "./types.js";
|
||||
import { describeUnknownError, mapThinkingLevel } from "./utils.js";
|
||||
import { flushPendingToolResultsAfterIdle } from "./wait-for-idle-before-flush.js";
|
||||
|
||||
@@ -570,6 +570,7 @@ export async function compactEmbeddedPiSessionDirect(
|
||||
modelApi: model.api,
|
||||
modelId,
|
||||
provider,
|
||||
config: params.config,
|
||||
sessionManager,
|
||||
sessionId: params.sessionId,
|
||||
policy: transcriptPolicy,
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import { EventEmitter } from "node:events";
|
||||
import type { AgentMessage, AgentTool } from "@mariozechner/pi-agent-core";
|
||||
import type { SessionManager } from "@mariozechner/pi-coding-agent";
|
||||
import type { TSchema } from "@sinclair/typebox";
|
||||
import { EventEmitter } from "node:events";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import type { TranscriptPolicy } from "../transcript-policy.js";
|
||||
import { registerUnhandledRejectionHandler } from "../../infra/unhandled-rejections.js";
|
||||
import {
|
||||
hasInterSessionUserProvenance,
|
||||
normalizeInputProvenance,
|
||||
} from "../../sessions/input-provenance.js";
|
||||
import { resolveImageSanitizationLimits } from "../image-sanitization.js";
|
||||
import {
|
||||
downgradeOpenAIReasoningBlocks,
|
||||
isCompactionFailureError,
|
||||
@@ -20,7 +23,6 @@ import {
|
||||
stripToolResultDetails,
|
||||
sanitizeToolUseResultPairing,
|
||||
} from "../session-transcript-repair.js";
|
||||
import type { TranscriptPolicy } from "../transcript-policy.js";
|
||||
import { resolveTranscriptPolicy } from "../transcript-policy.js";
|
||||
import { log } from "./logger.js";
|
||||
import { describeUnknownError } from "./utils.js";
|
||||
@@ -416,6 +418,7 @@ export async function sanitizeSessionHistory(params: {
|
||||
modelApi?: string | null;
|
||||
modelId?: string;
|
||||
provider?: string;
|
||||
config?: OpenClawConfig;
|
||||
sessionManager: SessionManager;
|
||||
sessionId: string;
|
||||
policy?: TranscriptPolicy;
|
||||
@@ -438,6 +441,7 @@ export async function sanitizeSessionHistory(params: {
|
||||
toolCallIdMode: policy.toolCallIdMode,
|
||||
preserveSignatures: policy.preserveSignatures,
|
||||
sanitizeThoughtSignatures: policy.sanitizeThoughtSignatures,
|
||||
...resolveImageSanitizationLimits(params.config),
|
||||
},
|
||||
);
|
||||
const sanitizedThinking = policy.normalizeAntigravityThinkingBlocks
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import type { AgentMessage } from "@mariozechner/pi-agent-core";
|
||||
import type { ImageContent } from "@mariozechner/pi-ai";
|
||||
import { streamSimple } from "@mariozechner/pi-ai";
|
||||
import { createAgentSession, SessionManager, SettingsManager } from "@mariozechner/pi-coding-agent";
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import type { EmbeddedRunAttemptParams, EmbeddedRunAttemptResult } from "./types.js";
|
||||
import { resolveHeartbeatPrompt } from "../../../auto-reply/heartbeat.js";
|
||||
import { resolveChannelCapabilities } from "../../../config/channel-capabilities.js";
|
||||
import { getMachineDisplayName } from "../../../infra/machine-name.js";
|
||||
@@ -33,6 +34,7 @@ import {
|
||||
import { DEFAULT_CONTEXT_TOKENS } from "../../defaults.js";
|
||||
import { resolveOpenClawDocsPath } from "../../docs-path.js";
|
||||
import { isTimeoutError } from "../../failover-error.js";
|
||||
import { resolveImageSanitizationLimits } from "../../image-sanitization.js";
|
||||
import { resolveModelAuthMode } from "../../model-auth.js";
|
||||
import { resolveDefaultModelForAgent } from "../../model-selection.js";
|
||||
import { createOllamaStreamFn, OLLAMA_NATIVE_BASE_URL } from "../../ollama-stream.js";
|
||||
@@ -105,7 +107,6 @@ import {
|
||||
shouldFlagCompactionTimeout,
|
||||
} from "./compaction-timeout.js";
|
||||
import { detectAndLoadPromptImages } from "./images.js";
|
||||
import type { EmbeddedRunAttemptParams, EmbeddedRunAttemptResult } from "./types.js";
|
||||
|
||||
export function injectHistoryImagesIntoMessages(
|
||||
messages: AgentMessage[],
|
||||
@@ -666,6 +667,7 @@ export async function runEmbeddedAttempt(
|
||||
modelApi: params.model.api,
|
||||
modelId: params.modelId,
|
||||
provider: params.provider,
|
||||
config: params.config,
|
||||
sessionManager,
|
||||
sessionId: params.sessionId,
|
||||
policy: transcriptPolicy,
|
||||
@@ -968,6 +970,7 @@ export async function runEmbeddedAttempt(
|
||||
existingImages: params.images,
|
||||
historyMessages: activeSession.messages,
|
||||
maxBytes: MAX_IMAGE_BYTES,
|
||||
maxDimensionPx: resolveImageSanitizationLimits(params.config).maxDimensionPx,
|
||||
// Enforce sandbox path restrictions when sandbox is enabled
|
||||
sandbox:
|
||||
sandbox?.enabled && sandbox?.fsBridge
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import type { ImageContent } from "@mariozechner/pi-ai";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import type { ImageContent } from "@mariozechner/pi-ai";
|
||||
import type { ImageSanitizationLimits } from "../../image-sanitization.js";
|
||||
import type { SandboxFsBridge } from "../../sandbox/fs-bridge.js";
|
||||
import { resolveUserPath } from "../../../utils.js";
|
||||
import { loadWebMedia } from "../../../web/media.js";
|
||||
import type { SandboxFsBridge } from "../../sandbox/fs-bridge.js";
|
||||
import { sanitizeImageBlocks } from "../../tool-images.js";
|
||||
import { log } from "../logger.js";
|
||||
|
||||
@@ -48,8 +49,13 @@ function isImageExtension(filePath: string): boolean {
|
||||
async function sanitizeImagesWithLog(
|
||||
images: ImageContent[],
|
||||
label: string,
|
||||
imageSanitization?: ImageSanitizationLimits,
|
||||
): Promise<ImageContent[]> {
|
||||
const { images: sanitized, dropped } = await sanitizeImageBlocks(images, label);
|
||||
const { images: sanitized, dropped } = await sanitizeImageBlocks(
|
||||
images,
|
||||
label,
|
||||
imageSanitization,
|
||||
);
|
||||
if (dropped > 0) {
|
||||
log.warn(`Native image: dropped ${dropped} image(s) after sanitization (${label}).`);
|
||||
}
|
||||
@@ -354,6 +360,7 @@ export async function detectAndLoadPromptImages(params: {
|
||||
existingImages?: ImageContent[];
|
||||
historyMessages?: unknown[];
|
||||
maxBytes?: number;
|
||||
maxDimensionPx?: number;
|
||||
sandbox?: { root: string; bridge: SandboxFsBridge };
|
||||
}): Promise<{
|
||||
/** Images for the current prompt (existingImages + detected in current prompt) */
|
||||
@@ -437,10 +444,21 @@ export async function detectAndLoadPromptImages(params: {
|
||||
}
|
||||
}
|
||||
|
||||
const sanitizedPromptImages = await sanitizeImagesWithLog(promptImages, "prompt:images");
|
||||
const imageSanitization: ImageSanitizationLimits = {
|
||||
maxDimensionPx: params.maxDimensionPx,
|
||||
};
|
||||
const sanitizedPromptImages = await sanitizeImagesWithLog(
|
||||
promptImages,
|
||||
"prompt:images",
|
||||
imageSanitization,
|
||||
);
|
||||
const sanitizedHistoryImagesByIndex = new Map<number, ImageContent[]>();
|
||||
for (const [index, images] of historyImagesByIndex) {
|
||||
const sanitized = await sanitizeImagesWithLog(images, `history:images:${index}`);
|
||||
const sanitized = await sanitizeImagesWithLog(
|
||||
images,
|
||||
`history:images:${index}`,
|
||||
imageSanitization,
|
||||
);
|
||||
if (sanitized.length > 0) {
|
||||
sanitizedHistoryImagesByIndex.set(index, sanitized);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user