90 lines
2.7 KiB
TypeScript
90 lines
2.7 KiB
TypeScript
import type { MediaUnderstandingAttachmentsConfig } from "../config/types.tools.js";
|
|
import {
|
|
isAudioAttachment,
|
|
isImageAttachment,
|
|
isVideoAttachment,
|
|
} from "./attachments.normalize.js";
|
|
import type { MediaAttachment, MediaUnderstandingCapability } from "./types.js";
|
|
|
|
const DEFAULT_MAX_ATTACHMENTS = 1;
|
|
|
|
function orderAttachments(
|
|
attachments: MediaAttachment[],
|
|
prefer?: MediaUnderstandingAttachmentsConfig["prefer"],
|
|
): MediaAttachment[] {
|
|
const list = Array.isArray(attachments) ? attachments.filter(isAttachmentRecord) : [];
|
|
if (!prefer || prefer === "first") {
|
|
return list;
|
|
}
|
|
if (prefer === "last") {
|
|
return [...list].toReversed();
|
|
}
|
|
if (prefer === "path") {
|
|
const withPath = list.filter((item) => item.path);
|
|
const withoutPath = list.filter((item) => !item.path);
|
|
return [...withPath, ...withoutPath];
|
|
}
|
|
if (prefer === "url") {
|
|
const withUrl = list.filter((item) => item.url);
|
|
const withoutUrl = list.filter((item) => !item.url);
|
|
return [...withUrl, ...withoutUrl];
|
|
}
|
|
return list;
|
|
}
|
|
|
|
function isAttachmentRecord(value: unknown): value is MediaAttachment {
|
|
if (!value || typeof value !== "object") {
|
|
return false;
|
|
}
|
|
const entry = value as Record<string, unknown>;
|
|
if (typeof entry.index !== "number") {
|
|
return false;
|
|
}
|
|
if (entry.path !== undefined && typeof entry.path !== "string") {
|
|
return false;
|
|
}
|
|
if (entry.url !== undefined && typeof entry.url !== "string") {
|
|
return false;
|
|
}
|
|
if (entry.mime !== undefined && typeof entry.mime !== "string") {
|
|
return false;
|
|
}
|
|
if (entry.alreadyTranscribed !== undefined && typeof entry.alreadyTranscribed !== "boolean") {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
export function selectAttachments(params: {
|
|
capability: MediaUnderstandingCapability;
|
|
attachments: MediaAttachment[];
|
|
policy?: MediaUnderstandingAttachmentsConfig;
|
|
}): MediaAttachment[] {
|
|
const { capability, attachments, policy } = params;
|
|
const input = Array.isArray(attachments) ? attachments.filter(isAttachmentRecord) : [];
|
|
const matches = input.filter((item) => {
|
|
// Skip already-transcribed audio attachments from preflight
|
|
if (capability === "audio" && item.alreadyTranscribed) {
|
|
return false;
|
|
}
|
|
if (capability === "image") {
|
|
return isImageAttachment(item);
|
|
}
|
|
if (capability === "audio") {
|
|
return isAudioAttachment(item);
|
|
}
|
|
return isVideoAttachment(item);
|
|
});
|
|
if (matches.length === 0) {
|
|
return [];
|
|
}
|
|
|
|
const ordered = orderAttachments(matches, policy?.prefer);
|
|
const mode = policy?.mode ?? "first";
|
|
const maxAttachments = policy?.maxAttachments ?? DEFAULT_MAX_ATTACHMENTS;
|
|
if (mode === "all") {
|
|
return ordered.slice(0, Math.max(1, maxAttachments));
|
|
}
|
|
return ordered.slice(0, 1);
|
|
}
|