mirror of
https://github.com/samsonjs/Peekaboo.git
synced 2026-06-29 05:39:33 +00:00
accept path as silent fallback parameter
This commit is contained in:
parent
76d0faef42
commit
17b74b4f1f
2 changed files with 97 additions and 6 deletions
|
|
@ -11,6 +11,7 @@ import {
|
|||
export const analyzeToolSchema = z.object({
|
||||
image_path: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe(
|
||||
"Required. Absolute path to image file (.png, .jpg, .webp) to be analyzed.",
|
||||
),
|
||||
|
|
@ -36,7 +37,15 @@ export const analyzeToolSchema = z.object({
|
|||
.describe(
|
||||
"Optional. Explicit provider/model. Validated against server's PEEKABOO_AI_PROVIDERS.",
|
||||
),
|
||||
});
|
||||
// Silent fallback parameter (not advertised in schema)
|
||||
path: z.string().optional(),
|
||||
}).refine(
|
||||
(data) => data.image_path || data.path,
|
||||
{
|
||||
message: "image_path is required",
|
||||
path: ["image_path"],
|
||||
}
|
||||
);
|
||||
|
||||
export type AnalyzeToolInput = z.infer<typeof analyzeToolSchema>;
|
||||
|
||||
|
|
@ -47,13 +56,16 @@ export async function analyzeToolHandler(
|
|||
const { logger } = context;
|
||||
|
||||
try {
|
||||
// Determine the effective image path (prioritize image_path, fallback to path)
|
||||
const effectiveImagePath = input.image_path || input.path!;
|
||||
|
||||
logger.debug(
|
||||
{ input: { ...input, image_path: input.image_path.split("/").pop() } },
|
||||
{ input: { ...input, effectiveImagePath: effectiveImagePath.split("/").pop() } },
|
||||
"Processing peekaboo.analyze tool call",
|
||||
);
|
||||
|
||||
// Validate image file extension
|
||||
const ext = path.extname(input.image_path).toLowerCase();
|
||||
const ext = path.extname(effectiveImagePath).toLowerCase();
|
||||
if (![".png", ".jpg", ".jpeg", ".webp"].includes(ext)) {
|
||||
return {
|
||||
content: [
|
||||
|
|
@ -117,10 +129,10 @@ export async function analyzeToolHandler(
|
|||
// Read image as base64
|
||||
let imageBase64: string;
|
||||
try {
|
||||
imageBase64 = await readImageAsBase64(input.image_path);
|
||||
imageBase64 = await readImageAsBase64(effectiveImagePath);
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
{ error, path: input.image_path },
|
||||
{ error, path: effectiveImagePath },
|
||||
"Failed to read image file",
|
||||
);
|
||||
return {
|
||||
|
|
@ -140,7 +152,7 @@ export async function analyzeToolHandler(
|
|||
try {
|
||||
analysisResult = await analyzeImageWithProvider(
|
||||
{ provider, model },
|
||||
input.image_path,
|
||||
effectiveImagePath,
|
||||
imageBase64,
|
||||
input.question,
|
||||
logger,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { vi, beforeEach, describe, it, expect } from "vitest";
|
|||
import { pino } from "pino";
|
||||
import {
|
||||
analyzeToolHandler,
|
||||
analyzeToolSchema,
|
||||
AnalyzeToolInput,
|
||||
} from "../../../src/tools/analyze";
|
||||
import { readImageAsBase64 } from "../../../src/utils/peekaboo-cli";
|
||||
|
|
@ -567,4 +568,82 @@ describe("Analyze Tool", () => {
|
|||
expect(result.isError).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Schema Validation", () => {
|
||||
it("should validate successfully with image_path", () => {
|
||||
const input = {
|
||||
image_path: "/path/to/image.png",
|
||||
question: "What is this?",
|
||||
};
|
||||
|
||||
const result = analyzeToolSchema.safeParse(input);
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it("should validate successfully with path as silent fallback", () => {
|
||||
const input = {
|
||||
path: "/path/to/image.png",
|
||||
question: "What is this?",
|
||||
};
|
||||
|
||||
const result = analyzeToolSchema.safeParse(input);
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it("should validate successfully when both image_path and path are provided", () => {
|
||||
const input = {
|
||||
image_path: "/priority/image.png",
|
||||
path: "/fallback/image.png",
|
||||
question: "What is this?",
|
||||
};
|
||||
|
||||
const result = analyzeToolSchema.safeParse(input);
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it("should fail validation when neither image_path nor path is provided", () => {
|
||||
const input = {
|
||||
question: "What is this?",
|
||||
};
|
||||
|
||||
const result = analyzeToolSchema.safeParse(input);
|
||||
expect(result.success).toBe(false);
|
||||
if (!result.success) {
|
||||
expect(result.error.errors[0].message).toBe("image_path is required");
|
||||
expect(result.error.errors[0].path).toEqual(["image_path"]);
|
||||
}
|
||||
});
|
||||
|
||||
it("should fail validation when question is missing", () => {
|
||||
const input = {
|
||||
image_path: "/path/to/image.png",
|
||||
};
|
||||
|
||||
const result = analyzeToolSchema.safeParse(input);
|
||||
expect(result.success).toBe(false);
|
||||
if (!result.success) {
|
||||
expect(result.error.errors[0].message).toBe("Required");
|
||||
expect(result.error.errors[0].path).toEqual(["question"]);
|
||||
}
|
||||
});
|
||||
|
||||
it("should validate optional provider_config correctly", () => {
|
||||
const inputWithoutProvider = {
|
||||
image_path: "/path/to/image.png",
|
||||
question: "What is this?",
|
||||
};
|
||||
|
||||
const inputWithProvider = {
|
||||
image_path: "/path/to/image.png",
|
||||
question: "What is this?",
|
||||
provider_config: {
|
||||
type: "openai" as const,
|
||||
model: "gpt-4o",
|
||||
},
|
||||
};
|
||||
|
||||
expect(analyzeToolSchema.safeParse(inputWithoutProvider).success).toBe(true);
|
||||
expect(analyzeToolSchema.safeParse(inputWithProvider).success).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue