Files
openclaw/src/agents/tools/pdf-tool.model-config.ts
2026-04-11 13:26:50 +01:00

132 lines
4.5 KiB
TypeScript

import type { OpenClawConfig } from "../../config/types.openclaw.js";
import {
bundledProviderSupportsNativePdfDocument,
resolveBundledAutoMediaKeyProviders,
resolveBundledDefaultMediaModel,
} from "../../media-understanding/bundled-defaults.js";
import {
coerceImageModelConfig,
type ImageModelConfig,
resolveProviderVisionModelFromConfig,
} from "./image-tool.helpers.js";
import { hasAuthForProvider, resolveDefaultModelRef } from "./model-config.helpers.js";
import { coercePdfModelConfig } from "./pdf-tool.helpers.js";
function resolveBundledImageCandidateRefs(params: {
cfg?: OpenClawConfig;
agentDir: string;
filter?: (providerId: string) => boolean;
}): string[] {
return resolveBundledAutoMediaKeyProviders("image")
.filter((providerId) => !params.filter || params.filter(providerId))
.filter((providerId) => hasAuthForProvider({ provider: providerId, agentDir: params.agentDir }))
.map((providerId) => {
const modelId =
resolveProviderVisionModelFromConfig({
cfg: params.cfg,
provider: providerId,
})?.split("/")[1] ??
resolveBundledDefaultMediaModel({
providerId,
capability: "image",
});
return modelId ? `${providerId}/${modelId}` : null;
})
.filter((value): value is string => Boolean(value));
}
export function resolvePdfModelConfigForTool(params: {
cfg?: OpenClawConfig;
agentDir: string;
}): ImageModelConfig | null {
const explicitPdf = coercePdfModelConfig(params.cfg);
if (explicitPdf.primary?.trim() || (explicitPdf.fallbacks?.length ?? 0) > 0) {
return explicitPdf;
}
const explicitImage = coerceImageModelConfig(params.cfg);
if (explicitImage.primary?.trim() || (explicitImage.fallbacks?.length ?? 0) > 0) {
return explicitImage;
}
const primary = resolveDefaultModelRef(params.cfg);
const googleOk = hasAuthForProvider({ provider: "google", agentDir: params.agentDir });
const fallbacks: string[] = [];
const addFallback = (ref: string) => {
const trimmed = ref.trim();
if (trimmed && !fallbacks.includes(trimmed)) {
fallbacks.push(trimmed);
}
};
let preferred: string | null = null;
const providerOk = hasAuthForProvider({ provider: primary.provider, agentDir: params.agentDir });
const providerVision = resolveProviderVisionModelFromConfig({
cfg: params.cfg,
provider: primary.provider,
});
const providerDefault =
providerVision?.split("/")[1] ??
resolveBundledDefaultMediaModel({
providerId: primary.provider,
capability: "image",
});
const primarySupportsNativePdf = bundledProviderSupportsNativePdfDocument(primary.provider);
const nativePdfCandidates = resolveBundledImageCandidateRefs({
cfg: params.cfg,
agentDir: params.agentDir,
filter: bundledProviderSupportsNativePdfDocument,
});
const genericImageCandidates = resolveBundledImageCandidateRefs({
cfg: params.cfg,
agentDir: params.agentDir,
});
if (params.cfg?.models?.providers && typeof params.cfg.models.providers === "object") {
for (const [providerKey, providerCfg] of Object.entries(params.cfg.models.providers)) {
const providerId = providerKey.trim();
if (!providerId || !hasAuthForProvider({ provider: providerId, agentDir: params.agentDir })) {
continue;
}
const models = providerCfg?.models ?? [];
const modelId = models
.find(
(model) =>
Boolean(model?.id?.trim()) &&
Array.isArray(model?.input) &&
model.input.includes("image"),
)
?.id?.trim();
if (!modelId) {
continue;
}
const ref = `${providerId}/${modelId}`;
if (!genericImageCandidates.includes(ref)) {
genericImageCandidates.push(ref);
}
}
}
if (primary.provider === "google" && googleOk && providerVision && primarySupportsNativePdf) {
preferred = providerVision;
} else if (providerOk && primarySupportsNativePdf && (providerVision || providerDefault)) {
preferred = providerVision ?? `${primary.provider}/${providerDefault}`;
} else {
preferred = nativePdfCandidates[0] ?? genericImageCandidates[0] ?? null;
}
if (preferred?.trim()) {
for (const candidate of [...nativePdfCandidates, ...genericImageCandidates]) {
if (candidate !== preferred) {
addFallback(candidate);
}
}
const pruned = fallbacks.filter((ref) => ref !== preferred);
return { primary: preferred, ...(pruned.length > 0 ? { fallbacks: pruned } : {}) };
}
return null;
}