fix(media): decouple capability registry from runtime loaders

This commit is contained in:
Vincent Koc
2026-04-12 12:00:42 +01:00
parent 50375ab31a
commit 3f32aa7582
4 changed files with 75 additions and 11 deletions

View File

@@ -1,13 +1,9 @@
import type { MediaUnderstandingModelConfig } from "../config/types.tools.js";
import { normalizeMediaProviderId } from "./provider-id.js";
import type { MediaUnderstandingCapability } from "./types.js";
export type MediaUnderstandingCapabilityRegistry = Map<
string,
{
capabilities?: MediaUnderstandingCapability[];
}
>;
import type {
MediaUnderstandingCapability,
MediaUnderstandingCapabilityRegistry,
} from "./types.js";
const MEDIA_CAPABILITIES = ["audio", "image", "video"] as const;

View File

@@ -0,0 +1,61 @@
import type { OpenClawConfig } from "../config/types.js";
import { resolvePluginCapabilityProviders } from "../plugins/capability-provider-runtime.js";
import { normalizeMediaProviderId } from "./provider-id.js";
import type { MediaUnderstandingCapabilityRegistry, MediaUnderstandingProvider } from "./types.js";
type ConfigProvider = NonNullable<
NonNullable<NonNullable<OpenClawConfig["models"]>["providers"]>[string]
>;
type ConfigProviderModel = NonNullable<ConfigProvider["models"]>[number];
function mergeProviderCapabilities(
registry: MediaUnderstandingCapabilityRegistry,
provider: Pick<MediaUnderstandingProvider, "id" | "capabilities">,
) {
const normalizedKey = normalizeMediaProviderId(provider.id);
const existing = registry.get(normalizedKey);
registry.set(normalizedKey, {
capabilities: provider.capabilities ?? existing?.capabilities,
});
}
export function buildMediaUnderstandingCapabilityRegistry(
cfg?: OpenClawConfig,
): MediaUnderstandingCapabilityRegistry {
const registry: MediaUnderstandingCapabilityRegistry = new Map();
for (const provider of resolvePluginCapabilityProviders({
key: "mediaUnderstandingProviders",
cfg,
})) {
mergeProviderCapabilities(registry, provider);
}
const configProviders = cfg?.models?.providers;
if (configProviders && typeof configProviders === "object") {
for (const [providerKey, providerCfg] of Object.entries(configProviders)) {
if (!providerKey?.trim()) {
continue;
}
const normalizedKey = normalizeMediaProviderId(providerKey);
if (registry.has(normalizedKey)) {
continue;
}
const models = providerCfg.models ?? [];
const hasImageModel = models.some(
(model: ConfigProviderModel) =>
Array.isArray(model?.input) && model.input.includes("image"),
);
if (!hasImageModel) {
continue;
}
mergeProviderCapabilities(registry, {
id: normalizedKey,
capabilities: ["image"],
});
}
}
return registry;
}

View File

@@ -7,6 +7,13 @@ export type MediaUnderstandingKind =
export type MediaUnderstandingCapability = "image" | "audio" | "video";
export type MediaUnderstandingCapabilityRegistry = Map<
string,
{
capabilities?: MediaUnderstandingCapability[];
}
>;
export type MediaAttachment = {
path?: string;
url?: string;

View File

@@ -4,7 +4,7 @@ import {
resolveConfiguredMediaEntryCapabilities,
resolveEffectiveMediaEntryCapabilities,
} from "../media-understanding/entry-capabilities.js";
import { buildMediaUnderstandingRegistry } from "../media-understanding/provider-registry.js";
import { buildMediaUnderstandingCapabilityRegistry } from "../media-understanding/provider-capability-registry.js";
import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js";
import { collectTtsApiKeyAssignments } from "./runtime-config-collectors-tts.js";
import { evaluateGatewayAuthSurfaceStates } from "./runtime-gateway-auth-surfaces.js";
@@ -401,9 +401,9 @@ function collectMediaRequestAssignments(params: {
return;
}
let providerRegistry: ReturnType<typeof buildMediaUnderstandingRegistry> | undefined;
let providerRegistry: ReturnType<typeof buildMediaUnderstandingCapabilityRegistry> | undefined;
const getProviderRegistry = () => {
providerRegistry ??= buildMediaUnderstandingRegistry(undefined, params.config);
providerRegistry ??= buildMediaUnderstandingCapabilityRegistry(params.config);
return providerRegistry;
};
const capabilityKeys = ["audio", "image", "video"] as const;