refactor: split agent tools
This commit is contained in:
141
src/agents/tools/common.ts
Normal file
141
src/agents/tools/common.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
import fs from "node:fs/promises";
|
||||
|
||||
import type { AgentTool, AgentToolResult } from "@mariozechner/pi-agent-core";
|
||||
|
||||
import { detectMime } from "../../media/mime.js";
|
||||
import { sanitizeToolResultImages } from "../tool-images.js";
|
||||
|
||||
// biome-ignore lint/suspicious/noExplicitAny: TypeBox schema type from pi-agent-core uses a different module instance.
|
||||
export type AnyAgentTool = AgentTool<any, unknown>;
|
||||
|
||||
export type StringParamOptions = {
|
||||
required?: boolean;
|
||||
trim?: boolean;
|
||||
label?: string;
|
||||
};
|
||||
|
||||
export function readStringParam(
|
||||
params: Record<string, unknown>,
|
||||
key: string,
|
||||
options: StringParamOptions & { required: true },
|
||||
): string;
|
||||
export function readStringParam(
|
||||
params: Record<string, unknown>,
|
||||
key: string,
|
||||
options?: StringParamOptions,
|
||||
): string | undefined;
|
||||
export function readStringParam(
|
||||
params: Record<string, unknown>,
|
||||
key: string,
|
||||
options: StringParamOptions = {},
|
||||
) {
|
||||
const { required = false, trim = true, label = key } = options;
|
||||
const raw = params[key];
|
||||
if (typeof raw !== "string") {
|
||||
if (required) throw new Error(`${label} required`);
|
||||
return undefined;
|
||||
}
|
||||
const value = trim ? raw.trim() : raw;
|
||||
if (!value) {
|
||||
if (required) throw new Error(`${label} required`);
|
||||
return undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
export function readStringArrayParam(
|
||||
params: Record<string, unknown>,
|
||||
key: string,
|
||||
options: StringParamOptions & { required: true },
|
||||
): string[];
|
||||
export function readStringArrayParam(
|
||||
params: Record<string, unknown>,
|
||||
key: string,
|
||||
options?: StringParamOptions,
|
||||
): string[] | undefined;
|
||||
export function readStringArrayParam(
|
||||
params: Record<string, unknown>,
|
||||
key: string,
|
||||
options: StringParamOptions = {},
|
||||
) {
|
||||
const { required = false, label = key } = options;
|
||||
const raw = params[key];
|
||||
if (Array.isArray(raw)) {
|
||||
const values = raw
|
||||
.filter((entry) => typeof entry === "string")
|
||||
.map((entry) => entry.trim())
|
||||
.filter(Boolean);
|
||||
if (values.length === 0) {
|
||||
if (required) throw new Error(`${label} required`);
|
||||
return undefined;
|
||||
}
|
||||
return values;
|
||||
}
|
||||
if (typeof raw === "string") {
|
||||
const value = raw.trim();
|
||||
if (!value) {
|
||||
if (required) throw new Error(`${label} required`);
|
||||
return undefined;
|
||||
}
|
||||
return [value];
|
||||
}
|
||||
if (required) throw new Error(`${label} required`);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function jsonResult(payload: unknown): AgentToolResult<unknown> {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify(payload, null, 2),
|
||||
},
|
||||
],
|
||||
details: payload,
|
||||
};
|
||||
}
|
||||
|
||||
export async function imageResult(params: {
|
||||
label: string;
|
||||
path: string;
|
||||
base64: string;
|
||||
mimeType: string;
|
||||
extraText?: string;
|
||||
details?: Record<string, unknown>;
|
||||
}): Promise<AgentToolResult<unknown>> {
|
||||
const content: AgentToolResult<unknown>["content"] = [
|
||||
{
|
||||
type: "text",
|
||||
text: params.extraText ?? `MEDIA:${params.path}`,
|
||||
},
|
||||
{
|
||||
type: "image",
|
||||
data: params.base64,
|
||||
mimeType: params.mimeType,
|
||||
},
|
||||
];
|
||||
const result: AgentToolResult<unknown> = {
|
||||
content,
|
||||
details: { path: params.path, ...params.details },
|
||||
};
|
||||
return await sanitizeToolResultImages(result, params.label);
|
||||
}
|
||||
|
||||
export async function imageResultFromFile(params: {
|
||||
label: string;
|
||||
path: string;
|
||||
extraText?: string;
|
||||
details?: Record<string, unknown>;
|
||||
}): Promise<AgentToolResult<unknown>> {
|
||||
const buf = await fs.readFile(params.path);
|
||||
const mimeType =
|
||||
(await detectMime({ buffer: buf.slice(0, 256) })) ?? "image/png";
|
||||
return await imageResult({
|
||||
label: params.label,
|
||||
path: params.path,
|
||||
base64: buf.toString("base64"),
|
||||
mimeType,
|
||||
extraText: params.extraText,
|
||||
details: params.details,
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user