Merge branch 'main' into fix/issue-2954-image-maxbytes-config
This commit is contained in:
commit
840a3962b9
@ -70,6 +70,7 @@ Status: unreleased.
|
||||
- **BREAKING:** Gateway auth mode "none" is removed; gateway now requires token/password (Tailscale Serve identity still allowed).
|
||||
|
||||
### Fixes
|
||||
- Agents: prevent retries on oversized image errors and surface size limits. (#2871) Thanks @Suksham-sharma.
|
||||
- Agents: inherit provider baseUrl/api for inline models. (#2740) Thanks @lploc94.
|
||||
- Memory Search: keep auto provider model defaults and only include remote when configured. (#2576) Thanks @papago2355.
|
||||
- macOS: auto-scroll to bottom when sending a new message while scrolled up. (#2471) Thanks @kennyklee.
|
||||
|
||||
@ -31,6 +31,7 @@ describe("classifyFailoverReason", () => {
|
||||
"messages.84.content.1.image.source.base64.data: At least one of the image dimensions exceed max allowed size for many-image requests: 2000 pixels",
|
||||
),
|
||||
).toBeNull();
|
||||
expect(classifyFailoverReason("image exceeds 5 MB maximum")).toBeNull();
|
||||
});
|
||||
it("classifies OpenAI usage limit errors as rate_limit", () => {
|
||||
expect(classifyFailoverReason("You have hit your ChatGPT usage limit (plus plan)")).toBe(
|
||||
|
||||
14
src/agents/pi-embedded-helpers.image-size-error.test.ts
Normal file
14
src/agents/pi-embedded-helpers.image-size-error.test.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { parseImageSizeError } from "./pi-embedded-helpers.js";
|
||||
|
||||
describe("parseImageSizeError", () => {
|
||||
it("parses max MB values from error text", () => {
|
||||
expect(parseImageSizeError("image exceeds 5 MB maximum")?.maxMb).toBe(5);
|
||||
expect(parseImageSizeError("Image exceeds 5.5 MB limit")?.maxMb).toBe(5.5);
|
||||
});
|
||||
|
||||
it("returns null for unrelated errors", () => {
|
||||
expect(parseImageSizeError("context overflow")).toBeNull();
|
||||
});
|
||||
});
|
||||
@ -23,12 +23,14 @@ export {
|
||||
isFailoverAssistantError,
|
||||
isFailoverErrorMessage,
|
||||
isImageDimensionErrorMessage,
|
||||
isImageSizeError,
|
||||
isOverloadedErrorMessage,
|
||||
isRawApiErrorPayload,
|
||||
isRateLimitAssistantError,
|
||||
isRateLimitErrorMessage,
|
||||
isTimeoutErrorMessage,
|
||||
parseImageDimensionError,
|
||||
parseImageSizeError,
|
||||
} from "./pi-embedded-helpers/errors.js";
|
||||
export { isGoogleModelApi, sanitizeGoogleTurnOrdering } from "./pi-embedded-helpers/google.js";
|
||||
|
||||
|
||||
@ -401,6 +401,7 @@ const ERROR_PATTERNS = {
|
||||
const IMAGE_DIMENSION_ERROR_RE =
|
||||
/image dimensions exceed max allowed size for many-image requests:\s*(\d+)\s*pixels/i;
|
||||
const IMAGE_DIMENSION_PATH_RE = /messages\.(\d+)\.content\.(\d+)\.image/i;
|
||||
const IMAGE_SIZE_ERROR_RE = /image exceeds\s*(\d+(?:\.\d+)?)\s*mb/i;
|
||||
|
||||
function matchesErrorPatterns(raw: string, patterns: readonly ErrorPattern[]): boolean {
|
||||
if (!raw) return false;
|
||||
@ -467,6 +468,25 @@ export function isImageDimensionErrorMessage(raw: string): boolean {
|
||||
return Boolean(parseImageDimensionError(raw));
|
||||
}
|
||||
|
||||
export function parseImageSizeError(raw: string): {
|
||||
maxMb?: number;
|
||||
raw: string;
|
||||
} | null {
|
||||
if (!raw) return null;
|
||||
const lower = raw.toLowerCase();
|
||||
if (!lower.includes("image exceeds") || !lower.includes("mb")) return null;
|
||||
const match = raw.match(IMAGE_SIZE_ERROR_RE);
|
||||
return {
|
||||
maxMb: match?.[1] ? Number.parseFloat(match[1]) : undefined,
|
||||
raw,
|
||||
};
|
||||
}
|
||||
|
||||
export function isImageSizeError(errorMessage?: string): boolean {
|
||||
if (!errorMessage) return false;
|
||||
return Boolean(parseImageSizeError(errorMessage));
|
||||
}
|
||||
|
||||
export function isCloudCodeAssistFormatError(raw: string): boolean {
|
||||
return !isImageDimensionErrorMessage(raw) && matchesErrorPatterns(raw, ERROR_PATTERNS.format);
|
||||
}
|
||||
@ -478,6 +498,7 @@ export function isAuthAssistantError(msg: AssistantMessage | undefined): boolean
|
||||
|
||||
export function classifyFailoverReason(raw: string): FailoverReason | null {
|
||||
if (isImageDimensionErrorMessage(raw)) return null;
|
||||
if (isImageSizeError(raw)) return null;
|
||||
if (isRateLimitErrorMessage(raw)) return "rate_limit";
|
||||
if (isOverloadedErrorMessage(raw)) return "rate_limit";
|
||||
if (isCloudCodeAssistFormatError(raw)) return "format";
|
||||
|
||||
@ -34,6 +34,7 @@ import {
|
||||
isContextOverflowError,
|
||||
isFailoverAssistantError,
|
||||
isFailoverErrorMessage,
|
||||
parseImageSizeError,
|
||||
parseImageDimensionError,
|
||||
isRateLimitAssistantError,
|
||||
isTimeoutErrorMessage,
|
||||
@ -440,6 +441,34 @@ export async function runEmbeddedPiAgent(
|
||||
},
|
||||
};
|
||||
}
|
||||
// Handle image size errors with a user-friendly message (no retry needed)
|
||||
const imageSizeError = parseImageSizeError(errorText);
|
||||
if (imageSizeError) {
|
||||
const maxMb = imageSizeError.maxMb;
|
||||
const maxMbLabel =
|
||||
typeof maxMb === "number" && Number.isFinite(maxMb) ? `${maxMb}` : null;
|
||||
const maxBytesHint = maxMbLabel ? ` (max ${maxMbLabel}MB)` : "";
|
||||
return {
|
||||
payloads: [
|
||||
{
|
||||
text:
|
||||
`Image too large for the model${maxBytesHint}. ` +
|
||||
"Please compress or resize the image and try again.",
|
||||
isError: true,
|
||||
},
|
||||
],
|
||||
meta: {
|
||||
durationMs: Date.now() - started,
|
||||
agentMeta: {
|
||||
sessionId: sessionIdUsed,
|
||||
provider,
|
||||
model: model.id,
|
||||
},
|
||||
systemPromptReport: attempt.systemPromptReport,
|
||||
error: { kind: "image_size", message: errorText },
|
||||
},
|
||||
};
|
||||
}
|
||||
const promptFailoverReason = classifyFailoverReason(errorText);
|
||||
if (promptFailoverReason && promptFailoverReason !== "timeout" && lastProfileId) {
|
||||
await markAuthProfileFailure({
|
||||
|
||||
@ -20,7 +20,7 @@ export type EmbeddedPiRunMeta = {
|
||||
aborted?: boolean;
|
||||
systemPromptReport?: SessionSystemPromptReport;
|
||||
error?: {
|
||||
kind: "context_overflow" | "compaction_failure" | "role_ordering";
|
||||
kind: "context_overflow" | "compaction_failure" | "role_ordering" | "image_size";
|
||||
message: string;
|
||||
};
|
||||
/** Stop reason for the agent run (e.g., "completed", "tool_calls"). */
|
||||
|
||||
Loading…
Reference in New Issue
Block a user