mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-15 03:01:02 +00:00
714 lines
28 KiB
TypeScript
714 lines
28 KiB
TypeScript
import { loadBundledCapabilityRuntimeRegistry } from "../bundled-capability-runtime.js";
|
|
import {
|
|
loadPluginManifestRegistry,
|
|
resolveManifestContractPluginIds,
|
|
} from "../manifest-registry.js";
|
|
import type {
|
|
ImageGenerationProviderPlugin,
|
|
MediaUnderstandingProviderPlugin,
|
|
MusicGenerationProviderPlugin,
|
|
ProviderPlugin,
|
|
RealtimeTranscriptionProviderPlugin,
|
|
RealtimeVoiceProviderPlugin,
|
|
SpeechProviderPlugin,
|
|
VideoGenerationProviderPlugin,
|
|
WebFetchProviderPlugin,
|
|
WebSearchProviderPlugin,
|
|
} from "../types.js";
|
|
import { BUNDLED_PLUGIN_CONTRACT_SNAPSHOTS } from "./inventory/bundled-capability-metadata.js";
|
|
import { uniqueStrings } from "./shared.js";
|
|
import {
|
|
loadVitestImageGenerationProviderContractRegistry,
|
|
loadVitestMediaUnderstandingProviderContractRegistry,
|
|
loadVitestMusicGenerationProviderContractRegistry,
|
|
loadVitestRealtimeTranscriptionProviderContractRegistry,
|
|
loadVitestRealtimeVoiceProviderContractRegistry,
|
|
loadVitestSpeechProviderContractRegistry,
|
|
loadVitestVideoGenerationProviderContractRegistry,
|
|
} from "./speech-vitest-registry.js";
|
|
|
|
type BundledCapabilityRuntimeRegistry = ReturnType<typeof loadBundledCapabilityRuntimeRegistry>;
|
|
type CapabilityContractEntry<T> = {
|
|
pluginId: string;
|
|
provider: T;
|
|
};
|
|
|
|
type ProviderContractEntry = CapabilityContractEntry<ProviderPlugin>;
|
|
type WebSearchProviderContractEntry = CapabilityContractEntry<WebSearchProviderPlugin> & {
|
|
credentialValue: unknown;
|
|
};
|
|
type WebFetchProviderContractEntry = CapabilityContractEntry<WebFetchProviderPlugin> & {
|
|
credentialValue: unknown;
|
|
};
|
|
type SpeechProviderContractEntry = CapabilityContractEntry<SpeechProviderPlugin>;
|
|
type RealtimeTranscriptionProviderContractEntry =
|
|
CapabilityContractEntry<RealtimeTranscriptionProviderPlugin>;
|
|
type RealtimeVoiceProviderContractEntry = CapabilityContractEntry<RealtimeVoiceProviderPlugin>;
|
|
type MediaUnderstandingProviderContractEntry =
|
|
CapabilityContractEntry<MediaUnderstandingProviderPlugin>;
|
|
type ImageGenerationProviderContractEntry = CapabilityContractEntry<ImageGenerationProviderPlugin>;
|
|
type VideoGenerationProviderContractEntry = CapabilityContractEntry<VideoGenerationProviderPlugin>;
|
|
type MusicGenerationProviderContractEntry = CapabilityContractEntry<MusicGenerationProviderPlugin>;
|
|
|
|
type PluginRegistrationContractEntry = {
|
|
pluginId: string;
|
|
cliBackendIds: string[];
|
|
providerIds: string[];
|
|
speechProviderIds: string[];
|
|
realtimeTranscriptionProviderIds: string[];
|
|
realtimeVoiceProviderIds: string[];
|
|
mediaUnderstandingProviderIds: string[];
|
|
imageGenerationProviderIds: string[];
|
|
videoGenerationProviderIds: string[];
|
|
musicGenerationProviderIds: string[];
|
|
webFetchProviderIds: string[];
|
|
webSearchProviderIds: string[];
|
|
toolNames: string[];
|
|
};
|
|
|
|
type ManifestContractKey =
|
|
| "speechProviders"
|
|
| "realtimeTranscriptionProviders"
|
|
| "realtimeVoiceProviders"
|
|
| "mediaUnderstandingProviders"
|
|
| "imageGenerationProviders"
|
|
| "videoGenerationProviders"
|
|
| "musicGenerationProviders"
|
|
| "webFetchProviders"
|
|
| "webSearchProviders"
|
|
| "tools";
|
|
|
|
type ManifestRegistryContractKey = "webFetchProviders" | "webSearchProviders";
|
|
|
|
function resolveBundledManifestContracts(): PluginRegistrationContractEntry[] {
|
|
if (process.env.VITEST) {
|
|
return BUNDLED_PLUGIN_CONTRACT_SNAPSHOTS.map((entry) => ({
|
|
pluginId: entry.pluginId,
|
|
cliBackendIds: [...entry.cliBackendIds],
|
|
providerIds: [...entry.providerIds],
|
|
speechProviderIds: [...entry.speechProviderIds],
|
|
realtimeTranscriptionProviderIds: [...entry.realtimeTranscriptionProviderIds],
|
|
realtimeVoiceProviderIds: [...entry.realtimeVoiceProviderIds],
|
|
mediaUnderstandingProviderIds: [...entry.mediaUnderstandingProviderIds],
|
|
imageGenerationProviderIds: [...entry.imageGenerationProviderIds],
|
|
videoGenerationProviderIds: [...entry.videoGenerationProviderIds],
|
|
musicGenerationProviderIds: [...entry.musicGenerationProviderIds],
|
|
webFetchProviderIds: [...entry.webFetchProviderIds],
|
|
webSearchProviderIds: [...entry.webSearchProviderIds],
|
|
toolNames: [...entry.toolNames],
|
|
}));
|
|
}
|
|
return loadPluginManifestRegistry({})
|
|
.plugins.filter(
|
|
(plugin) =>
|
|
plugin.origin === "bundled" &&
|
|
(plugin.cliBackends.length > 0 ||
|
|
plugin.providers.length > 0 ||
|
|
(plugin.contracts?.speechProviders?.length ?? 0) > 0 ||
|
|
(plugin.contracts?.realtimeTranscriptionProviders?.length ?? 0) > 0 ||
|
|
(plugin.contracts?.realtimeVoiceProviders?.length ?? 0) > 0 ||
|
|
(plugin.contracts?.mediaUnderstandingProviders?.length ?? 0) > 0 ||
|
|
(plugin.contracts?.imageGenerationProviders?.length ?? 0) > 0 ||
|
|
(plugin.contracts?.videoGenerationProviders?.length ?? 0) > 0 ||
|
|
(plugin.contracts?.musicGenerationProviders?.length ?? 0) > 0 ||
|
|
(plugin.contracts?.webFetchProviders?.length ?? 0) > 0 ||
|
|
(plugin.contracts?.webSearchProviders?.length ?? 0) > 0 ||
|
|
(plugin.contracts?.tools?.length ?? 0) > 0),
|
|
)
|
|
.map((plugin) => ({
|
|
pluginId: plugin.id,
|
|
cliBackendIds: uniqueStrings(plugin.cliBackends),
|
|
providerIds: uniqueStrings(plugin.providers),
|
|
speechProviderIds: uniqueStrings(plugin.contracts?.speechProviders ?? []),
|
|
realtimeTranscriptionProviderIds: uniqueStrings(
|
|
plugin.contracts?.realtimeTranscriptionProviders ?? [],
|
|
),
|
|
realtimeVoiceProviderIds: uniqueStrings(plugin.contracts?.realtimeVoiceProviders ?? []),
|
|
mediaUnderstandingProviderIds: uniqueStrings(
|
|
plugin.contracts?.mediaUnderstandingProviders ?? [],
|
|
),
|
|
imageGenerationProviderIds: uniqueStrings(plugin.contracts?.imageGenerationProviders ?? []),
|
|
videoGenerationProviderIds: uniqueStrings(plugin.contracts?.videoGenerationProviders ?? []),
|
|
musicGenerationProviderIds: uniqueStrings(plugin.contracts?.musicGenerationProviders ?? []),
|
|
webFetchProviderIds: uniqueStrings(plugin.contracts?.webFetchProviders ?? []),
|
|
webSearchProviderIds: uniqueStrings(plugin.contracts?.webSearchProviders ?? []),
|
|
toolNames: uniqueStrings(plugin.contracts?.tools ?? []),
|
|
}));
|
|
}
|
|
|
|
function resolveBundledProviderContractPluginIdsByProviderId(): Map<string, string[]> {
|
|
const result = new Map<string, string[]>();
|
|
for (const entry of resolveBundledManifestContracts()) {
|
|
for (const providerId of entry.providerIds) {
|
|
const existing = result.get(providerId) ?? [];
|
|
if (!existing.includes(entry.pluginId)) {
|
|
existing.push(entry.pluginId);
|
|
}
|
|
result.set(providerId, existing);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function resolveBundledProviderContractPluginIds(): string[] {
|
|
return uniqueStrings(
|
|
resolveBundledManifestContracts()
|
|
.filter((entry) => entry.providerIds.length > 0)
|
|
.map((entry) => entry.pluginId),
|
|
).toSorted((left, right) => left.localeCompare(right));
|
|
}
|
|
|
|
function resolveBundledManifestContractPluginIds(contract: ManifestRegistryContractKey): string[] {
|
|
return resolveManifestContractPluginIds({
|
|
contract,
|
|
origin: "bundled",
|
|
});
|
|
}
|
|
|
|
function resolveBundledManifestPluginIdsForContract(contract: ManifestContractKey): string[] {
|
|
return uniqueStrings(
|
|
resolveBundledManifestContracts()
|
|
.filter((entry) => {
|
|
switch (contract) {
|
|
case "speechProviders":
|
|
return entry.speechProviderIds.length > 0;
|
|
case "realtimeTranscriptionProviders":
|
|
return entry.realtimeTranscriptionProviderIds.length > 0;
|
|
case "realtimeVoiceProviders":
|
|
return entry.realtimeVoiceProviderIds.length > 0;
|
|
case "mediaUnderstandingProviders":
|
|
return entry.mediaUnderstandingProviderIds.length > 0;
|
|
case "imageGenerationProviders":
|
|
return entry.imageGenerationProviderIds.length > 0;
|
|
case "videoGenerationProviders":
|
|
return entry.videoGenerationProviderIds.length > 0;
|
|
case "musicGenerationProviders":
|
|
return entry.musicGenerationProviderIds.length > 0;
|
|
case "webFetchProviders":
|
|
return entry.webFetchProviderIds.length > 0;
|
|
case "webSearchProviders":
|
|
return entry.webSearchProviderIds.length > 0;
|
|
case "tools":
|
|
return entry.toolNames.length > 0;
|
|
}
|
|
})
|
|
.map((entry) => entry.pluginId),
|
|
).toSorted((left, right) => left.localeCompare(right));
|
|
}
|
|
|
|
let providerContractRegistryCache: ProviderContractEntry[] | null = null;
|
|
let providerContractRegistryByPluginIdCache: Map<string, ProviderContractEntry[]> | null = null;
|
|
let webFetchProviderContractRegistryCache: WebFetchProviderContractEntry[] | null = null;
|
|
let webFetchProviderContractRegistryByPluginIdCache: Map<
|
|
string,
|
|
WebFetchProviderContractEntry[]
|
|
> | null = null;
|
|
let webSearchProviderContractRegistryCache: WebSearchProviderContractEntry[] | null = null;
|
|
let webSearchProviderContractRegistryByPluginIdCache: Map<
|
|
string,
|
|
WebSearchProviderContractEntry[]
|
|
> | null = null;
|
|
let speechProviderContractRegistryCache: SpeechProviderContractEntry[] | null = null;
|
|
let realtimeTranscriptionProviderContractRegistryCache:
|
|
| RealtimeTranscriptionProviderContractEntry[]
|
|
| null = null;
|
|
let realtimeVoiceProviderContractRegistryCache: RealtimeVoiceProviderContractEntry[] | null = null;
|
|
let mediaUnderstandingProviderContractRegistryCache:
|
|
| MediaUnderstandingProviderContractEntry[]
|
|
| null = null;
|
|
let imageGenerationProviderContractRegistryCache: ImageGenerationProviderContractEntry[] | null =
|
|
null;
|
|
let videoGenerationProviderContractRegistryCache: VideoGenerationProviderContractEntry[] | null =
|
|
null;
|
|
let musicGenerationProviderContractRegistryCache: MusicGenerationProviderContractEntry[] | null =
|
|
null;
|
|
|
|
export let providerContractLoadError: Error | undefined;
|
|
|
|
function formatBundledCapabilityPluginLoadError(params: {
|
|
pluginId: string;
|
|
capabilityLabel: string;
|
|
registry: BundledCapabilityRuntimeRegistry;
|
|
}): Error {
|
|
const plugin = params.registry.plugins.find((entry) => entry.id === params.pluginId);
|
|
const diagnostics = params.registry.diagnostics
|
|
.filter((entry) => entry.pluginId === params.pluginId)
|
|
.map((entry) => entry.message);
|
|
const detailParts = plugin
|
|
? [
|
|
`status=${plugin.status}`,
|
|
...(plugin.error ? [`error=${plugin.error}`] : []),
|
|
`providerIds=[${plugin.providerIds.join(", ")}]`,
|
|
`webFetchProviderIds=[${plugin.webFetchProviderIds.join(", ")}]`,
|
|
`webSearchProviderIds=[${plugin.webSearchProviderIds.join(", ")}]`,
|
|
]
|
|
: ["plugin record missing"];
|
|
if (diagnostics.length > 0) {
|
|
detailParts.push(`diagnostics=${diagnostics.join(" | ")}`);
|
|
}
|
|
return new Error(
|
|
`bundled ${params.capabilityLabel} contract load failed for ${params.pluginId}: ${detailParts.join("; ")}`,
|
|
);
|
|
}
|
|
|
|
function loadScopedCapabilityRuntimeRegistryEntries<T>(params: {
|
|
pluginId: string;
|
|
capabilityLabel: string;
|
|
loadEntries: (registry: BundledCapabilityRuntimeRegistry) => T[];
|
|
loadDeclaredIds: (
|
|
plugin: BundledCapabilityRuntimeRegistry["plugins"][number],
|
|
) => readonly string[];
|
|
}): T[] {
|
|
let lastFailure: Error | undefined;
|
|
|
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
const registry = loadBundledCapabilityRuntimeRegistry({
|
|
pluginIds: [params.pluginId],
|
|
pluginSdkResolution: "dist",
|
|
});
|
|
const entries = params.loadEntries(registry);
|
|
if (entries.length > 0) {
|
|
return entries;
|
|
}
|
|
|
|
const plugin = registry.plugins.find((entry) => entry.id === params.pluginId);
|
|
lastFailure = formatBundledCapabilityPluginLoadError({
|
|
pluginId: params.pluginId,
|
|
capabilityLabel: params.capabilityLabel,
|
|
registry,
|
|
});
|
|
const shouldRetry =
|
|
attempt === 0 &&
|
|
(!plugin || plugin.status !== "loaded" || params.loadDeclaredIds(plugin).length === 0);
|
|
if (!shouldRetry) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
throw (
|
|
lastFailure ??
|
|
new Error(
|
|
`bundled ${params.capabilityLabel} contract load failed for ${params.pluginId}: no entries`,
|
|
)
|
|
);
|
|
}
|
|
|
|
function loadProviderContractEntriesForPluginIds(
|
|
pluginIds: readonly string[],
|
|
): ProviderContractEntry[] {
|
|
return pluginIds.flatMap((pluginId) => loadProviderContractEntriesForPluginId(pluginId));
|
|
}
|
|
|
|
function loadProviderContractEntriesForPluginId(pluginId: string): ProviderContractEntry[] {
|
|
if (providerContractRegistryCache) {
|
|
return providerContractRegistryCache.filter((entry) => entry.pluginId === pluginId);
|
|
}
|
|
|
|
const cache =
|
|
providerContractRegistryByPluginIdCache ?? new Map<string, ProviderContractEntry[]>();
|
|
providerContractRegistryByPluginIdCache = cache;
|
|
const cached = cache.get(pluginId);
|
|
if (cached) {
|
|
return cached;
|
|
}
|
|
|
|
try {
|
|
providerContractLoadError = undefined;
|
|
const entries = loadScopedCapabilityRuntimeRegistryEntries({
|
|
pluginId,
|
|
capabilityLabel: "provider",
|
|
loadEntries: (registry) =>
|
|
registry.providers
|
|
.filter((entry) => entry.pluginId === pluginId)
|
|
.map((entry) => ({
|
|
pluginId: entry.pluginId,
|
|
provider: entry.provider,
|
|
})),
|
|
loadDeclaredIds: (plugin) => plugin.providerIds,
|
|
}).map((entry) => ({
|
|
pluginId: entry.pluginId,
|
|
provider: entry.provider,
|
|
}));
|
|
cache.set(pluginId, entries);
|
|
return entries;
|
|
} catch (error) {
|
|
providerContractLoadError = error instanceof Error ? error : new Error(String(error));
|
|
cache.set(pluginId, []);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
function loadProviderContractRegistry(): ProviderContractEntry[] {
|
|
if (!providerContractRegistryCache) {
|
|
try {
|
|
providerContractLoadError = undefined;
|
|
providerContractRegistryCache = loadBundledCapabilityRuntimeRegistry({
|
|
pluginIds: resolveBundledProviderContractPluginIds(),
|
|
pluginSdkResolution: "dist",
|
|
}).providers.map((entry) => ({
|
|
pluginId: entry.pluginId,
|
|
provider: entry.provider,
|
|
}));
|
|
} catch (error) {
|
|
providerContractLoadError = error instanceof Error ? error : new Error(String(error));
|
|
providerContractRegistryCache = [];
|
|
}
|
|
}
|
|
return providerContractRegistryCache;
|
|
}
|
|
|
|
function loadUniqueProviderContractProviders(): ProviderPlugin[] {
|
|
return [
|
|
...new Map(
|
|
loadProviderContractRegistry().map((entry) => [entry.provider.id, entry.provider]),
|
|
).values(),
|
|
];
|
|
}
|
|
|
|
function loadProviderContractPluginIds(): string[] {
|
|
return [...resolveBundledProviderContractPluginIds()];
|
|
}
|
|
|
|
function loadProviderContractCompatPluginIds(): string[] {
|
|
return loadProviderContractPluginIds();
|
|
}
|
|
|
|
function resolveWebSearchCredentialValue(provider: WebSearchProviderPlugin): unknown {
|
|
if (provider.requiresCredential === false) {
|
|
return `${provider.id}-no-key-needed`;
|
|
}
|
|
const envVar = provider.envVars.find((entry) => entry.trim().length > 0);
|
|
if (!envVar) {
|
|
return `${provider.id}-test`;
|
|
}
|
|
if (envVar === "OPENROUTER_API_KEY") {
|
|
return "openrouter-test";
|
|
}
|
|
return envVar.toLowerCase().includes("api_key") ? `${provider.id}-test` : "sk-test";
|
|
}
|
|
|
|
function resolveWebFetchCredentialValue(provider: WebFetchProviderPlugin): unknown {
|
|
if (provider.requiresCredential === false) {
|
|
return `${provider.id}-no-key-needed`;
|
|
}
|
|
const envVar = provider.envVars.find((entry) => entry.trim().length > 0);
|
|
if (!envVar) {
|
|
return `${provider.id}-test`;
|
|
}
|
|
return envVar.toLowerCase().includes("api_key") ? `${provider.id}-test` : "sk-test";
|
|
}
|
|
|
|
function loadWebFetchProviderContractRegistry(): WebFetchProviderContractEntry[] {
|
|
if (!webFetchProviderContractRegistryCache) {
|
|
const registry = loadBundledCapabilityRuntimeRegistry({
|
|
pluginIds: resolveBundledManifestContractPluginIds("webFetchProviders"),
|
|
pluginSdkResolution: "dist",
|
|
});
|
|
webFetchProviderContractRegistryCache = registry.webFetchProviders.map((entry) => ({
|
|
pluginId: entry.pluginId,
|
|
provider: entry.provider,
|
|
credentialValue: resolveWebFetchCredentialValue(entry.provider),
|
|
}));
|
|
}
|
|
return webFetchProviderContractRegistryCache;
|
|
}
|
|
|
|
export function resolveWebFetchProviderContractEntriesForPluginId(
|
|
pluginId: string,
|
|
): WebFetchProviderContractEntry[] {
|
|
if (webFetchProviderContractRegistryCache) {
|
|
return webFetchProviderContractRegistryCache.filter((entry) => entry.pluginId === pluginId);
|
|
}
|
|
|
|
const cache =
|
|
webFetchProviderContractRegistryByPluginIdCache ??
|
|
new Map<string, WebFetchProviderContractEntry[]>();
|
|
webFetchProviderContractRegistryByPluginIdCache = cache;
|
|
const cached = cache.get(pluginId);
|
|
if (cached) {
|
|
return cached;
|
|
}
|
|
|
|
const entries = loadScopedCapabilityRuntimeRegistryEntries({
|
|
pluginId,
|
|
capabilityLabel: "web fetch provider",
|
|
loadEntries: (registry) =>
|
|
registry.webFetchProviders
|
|
.filter((entry) => entry.pluginId === pluginId)
|
|
.map((entry) => ({
|
|
pluginId: entry.pluginId,
|
|
provider: entry.provider,
|
|
credentialValue: resolveWebFetchCredentialValue(entry.provider),
|
|
})),
|
|
loadDeclaredIds: (plugin) => plugin.webFetchProviderIds,
|
|
});
|
|
cache.set(pluginId, entries);
|
|
return entries;
|
|
}
|
|
|
|
function loadWebSearchProviderContractRegistry(): WebSearchProviderContractEntry[] {
|
|
if (!webSearchProviderContractRegistryCache) {
|
|
const registry = loadBundledCapabilityRuntimeRegistry({
|
|
pluginIds: resolveBundledManifestContractPluginIds("webSearchProviders"),
|
|
pluginSdkResolution: "dist",
|
|
});
|
|
webSearchProviderContractRegistryCache = registry.webSearchProviders.map((entry) => ({
|
|
pluginId: entry.pluginId,
|
|
provider: entry.provider,
|
|
credentialValue: resolveWebSearchCredentialValue(entry.provider),
|
|
}));
|
|
}
|
|
return webSearchProviderContractRegistryCache;
|
|
}
|
|
|
|
export function resolveWebSearchProviderContractEntriesForPluginId(
|
|
pluginId: string,
|
|
): WebSearchProviderContractEntry[] {
|
|
if (webSearchProviderContractRegistryCache) {
|
|
return webSearchProviderContractRegistryCache.filter((entry) => entry.pluginId === pluginId);
|
|
}
|
|
|
|
const cache =
|
|
webSearchProviderContractRegistryByPluginIdCache ??
|
|
new Map<string, WebSearchProviderContractEntry[]>();
|
|
webSearchProviderContractRegistryByPluginIdCache = cache;
|
|
const cached = cache.get(pluginId);
|
|
if (cached) {
|
|
return cached;
|
|
}
|
|
|
|
const entries = loadScopedCapabilityRuntimeRegistryEntries({
|
|
pluginId,
|
|
capabilityLabel: "web search provider",
|
|
loadEntries: (registry) =>
|
|
registry.webSearchProviders
|
|
.filter((entry) => entry.pluginId === pluginId)
|
|
.map((entry) => ({
|
|
pluginId: entry.pluginId,
|
|
provider: entry.provider,
|
|
credentialValue: resolveWebSearchCredentialValue(entry.provider),
|
|
})),
|
|
loadDeclaredIds: (plugin) => plugin.webSearchProviderIds,
|
|
});
|
|
cache.set(pluginId, entries);
|
|
return entries;
|
|
}
|
|
|
|
function loadSpeechProviderContractRegistry(): SpeechProviderContractEntry[] {
|
|
if (!speechProviderContractRegistryCache) {
|
|
speechProviderContractRegistryCache = process.env.VITEST
|
|
? loadVitestSpeechProviderContractRegistry()
|
|
: loadBundledCapabilityRuntimeRegistry({
|
|
pluginIds: resolveBundledManifestPluginIdsForContract("speechProviders"),
|
|
pluginSdkResolution: "dist",
|
|
}).speechProviders.map((entry) => ({
|
|
pluginId: entry.pluginId,
|
|
provider: entry.provider,
|
|
}));
|
|
}
|
|
return speechProviderContractRegistryCache;
|
|
}
|
|
|
|
function loadRealtimeVoiceProviderContractRegistry(): RealtimeVoiceProviderContractEntry[] {
|
|
if (!realtimeVoiceProviderContractRegistryCache) {
|
|
realtimeVoiceProviderContractRegistryCache = process.env.VITEST
|
|
? loadVitestRealtimeVoiceProviderContractRegistry()
|
|
: loadBundledCapabilityRuntimeRegistry({
|
|
pluginIds: resolveBundledManifestPluginIdsForContract("realtimeVoiceProviders"),
|
|
pluginSdkResolution: "dist",
|
|
}).realtimeVoiceProviders.map((entry) => ({
|
|
pluginId: entry.pluginId,
|
|
provider: entry.provider,
|
|
}));
|
|
}
|
|
return realtimeVoiceProviderContractRegistryCache;
|
|
}
|
|
|
|
function loadRealtimeTranscriptionProviderContractRegistry(): RealtimeTranscriptionProviderContractEntry[] {
|
|
if (!realtimeTranscriptionProviderContractRegistryCache) {
|
|
realtimeTranscriptionProviderContractRegistryCache = process.env.VITEST
|
|
? loadVitestRealtimeTranscriptionProviderContractRegistry()
|
|
: loadBundledCapabilityRuntimeRegistry({
|
|
pluginIds: resolveBundledManifestPluginIdsForContract("realtimeTranscriptionProviders"),
|
|
pluginSdkResolution: "dist",
|
|
}).realtimeTranscriptionProviders.map((entry) => ({
|
|
pluginId: entry.pluginId,
|
|
provider: entry.provider,
|
|
}));
|
|
}
|
|
return realtimeTranscriptionProviderContractRegistryCache;
|
|
}
|
|
|
|
function loadMediaUnderstandingProviderContractRegistry(): MediaUnderstandingProviderContractEntry[] {
|
|
if (!mediaUnderstandingProviderContractRegistryCache) {
|
|
mediaUnderstandingProviderContractRegistryCache = process.env.VITEST
|
|
? loadVitestMediaUnderstandingProviderContractRegistry()
|
|
: loadBundledCapabilityRuntimeRegistry({
|
|
pluginIds: resolveBundledManifestPluginIdsForContract("mediaUnderstandingProviders"),
|
|
pluginSdkResolution: "dist",
|
|
}).mediaUnderstandingProviders.map((entry) => ({
|
|
pluginId: entry.pluginId,
|
|
provider: entry.provider,
|
|
}));
|
|
}
|
|
return mediaUnderstandingProviderContractRegistryCache;
|
|
}
|
|
|
|
function loadImageGenerationProviderContractRegistry(): ImageGenerationProviderContractEntry[] {
|
|
if (!imageGenerationProviderContractRegistryCache) {
|
|
imageGenerationProviderContractRegistryCache = process.env.VITEST
|
|
? loadVitestImageGenerationProviderContractRegistry()
|
|
: loadBundledCapabilityRuntimeRegistry({
|
|
pluginIds: resolveBundledManifestPluginIdsForContract("imageGenerationProviders"),
|
|
pluginSdkResolution: "dist",
|
|
}).imageGenerationProviders.map((entry) => ({
|
|
pluginId: entry.pluginId,
|
|
provider: entry.provider,
|
|
}));
|
|
}
|
|
return imageGenerationProviderContractRegistryCache;
|
|
}
|
|
|
|
function loadVideoGenerationProviderContractRegistry(): VideoGenerationProviderContractEntry[] {
|
|
if (!videoGenerationProviderContractRegistryCache) {
|
|
videoGenerationProviderContractRegistryCache = process.env.VITEST
|
|
? loadVitestVideoGenerationProviderContractRegistry()
|
|
: loadBundledCapabilityRuntimeRegistry({
|
|
pluginIds: resolveBundledManifestPluginIdsForContract("videoGenerationProviders"),
|
|
pluginSdkResolution: "dist",
|
|
}).videoGenerationProviders.map((entry) => ({
|
|
pluginId: entry.pluginId,
|
|
provider: entry.provider,
|
|
}));
|
|
}
|
|
return videoGenerationProviderContractRegistryCache;
|
|
}
|
|
|
|
function loadMusicGenerationProviderContractRegistry(): MusicGenerationProviderContractEntry[] {
|
|
if (!musicGenerationProviderContractRegistryCache) {
|
|
musicGenerationProviderContractRegistryCache = process.env.VITEST
|
|
? loadVitestMusicGenerationProviderContractRegistry()
|
|
: loadBundledCapabilityRuntimeRegistry({
|
|
pluginIds: resolveBundledManifestPluginIdsForContract("musicGenerationProviders"),
|
|
pluginSdkResolution: "dist",
|
|
}).musicGenerationProviders.map((entry) => ({
|
|
pluginId: entry.pluginId,
|
|
provider: entry.provider,
|
|
}));
|
|
}
|
|
return musicGenerationProviderContractRegistryCache;
|
|
}
|
|
|
|
function createLazyArrayView<T>(load: () => T[]): T[] {
|
|
return new Proxy([] as T[], {
|
|
get(_target, prop) {
|
|
const actual = load();
|
|
const value = Reflect.get(actual, prop, actual);
|
|
return typeof value === "function" ? value.bind(actual) : value;
|
|
},
|
|
has(_target, prop) {
|
|
return Reflect.has(load(), prop);
|
|
},
|
|
ownKeys() {
|
|
return Reflect.ownKeys(load());
|
|
},
|
|
getOwnPropertyDescriptor(_target, prop) {
|
|
const actual = load();
|
|
const descriptor = Reflect.getOwnPropertyDescriptor(actual, prop);
|
|
if (descriptor) {
|
|
return descriptor;
|
|
}
|
|
if (Reflect.has(actual, prop)) {
|
|
return {
|
|
configurable: true,
|
|
enumerable: true,
|
|
writable: false,
|
|
value: Reflect.get(actual, prop, actual),
|
|
};
|
|
}
|
|
return undefined;
|
|
},
|
|
});
|
|
}
|
|
|
|
export const providerContractRegistry: ProviderContractEntry[] = createLazyArrayView(
|
|
loadProviderContractRegistry,
|
|
);
|
|
export const uniqueProviderContractProviders: ProviderPlugin[] = createLazyArrayView(
|
|
loadUniqueProviderContractProviders,
|
|
);
|
|
export const providerContractPluginIds: string[] = createLazyArrayView(
|
|
loadProviderContractPluginIds,
|
|
);
|
|
export const providerContractCompatPluginIds: string[] = createLazyArrayView(
|
|
loadProviderContractCompatPluginIds,
|
|
);
|
|
|
|
export function requireProviderContractProvider(providerId: string): ProviderPlugin {
|
|
const pluginIds = resolveBundledProviderContractPluginIdsByProviderId().get(providerId) ?? [];
|
|
const entries = loadProviderContractEntriesForPluginIds(pluginIds);
|
|
const provider = entries.find((entry) => entry.provider.id === providerId)?.provider;
|
|
if (!provider) {
|
|
const pluginScopedProviders = [
|
|
...new Map(entries.map((entry) => [entry.provider.id, entry.provider])).values(),
|
|
];
|
|
if (pluginIds.length === 1 && pluginScopedProviders.length === 1) {
|
|
return pluginScopedProviders[0];
|
|
}
|
|
if (providerContractLoadError) {
|
|
throw new Error(
|
|
`provider contract entry missing for ${providerId}; bundled provider registry failed to load: ${providerContractLoadError.message}`,
|
|
);
|
|
}
|
|
throw new Error(`provider contract entry missing for ${providerId}`);
|
|
}
|
|
return provider;
|
|
}
|
|
|
|
export function resolveProviderContractPluginIdsForProvider(
|
|
providerId: string,
|
|
): string[] | undefined {
|
|
const pluginIds = resolveBundledProviderContractPluginIdsByProviderId().get(providerId) ?? [];
|
|
return pluginIds.length > 0 ? pluginIds : undefined;
|
|
}
|
|
|
|
export function resolveProviderContractProvidersForPluginIds(
|
|
pluginIds: readonly string[],
|
|
): ProviderPlugin[] {
|
|
const allowed = new Set(pluginIds);
|
|
return [
|
|
...new Map(
|
|
loadProviderContractEntriesForPluginIds([...allowed])
|
|
.filter((entry) => allowed.has(entry.pluginId))
|
|
.map((entry) => [entry.provider.id, entry.provider]),
|
|
).values(),
|
|
];
|
|
}
|
|
|
|
export const webSearchProviderContractRegistry: WebSearchProviderContractEntry[] =
|
|
createLazyArrayView(loadWebSearchProviderContractRegistry);
|
|
export const webFetchProviderContractRegistry: WebFetchProviderContractEntry[] =
|
|
createLazyArrayView(loadWebFetchProviderContractRegistry);
|
|
export const speechProviderContractRegistry: SpeechProviderContractEntry[] = createLazyArrayView(
|
|
loadSpeechProviderContractRegistry,
|
|
);
|
|
export const realtimeTranscriptionProviderContractRegistry: RealtimeTranscriptionProviderContractEntry[] =
|
|
createLazyArrayView(loadRealtimeTranscriptionProviderContractRegistry);
|
|
export const realtimeVoiceProviderContractRegistry: RealtimeVoiceProviderContractEntry[] =
|
|
createLazyArrayView(loadRealtimeVoiceProviderContractRegistry);
|
|
export const mediaUnderstandingProviderContractRegistry: MediaUnderstandingProviderContractEntry[] =
|
|
createLazyArrayView(loadMediaUnderstandingProviderContractRegistry);
|
|
export const imageGenerationProviderContractRegistry: ImageGenerationProviderContractEntry[] =
|
|
createLazyArrayView(loadImageGenerationProviderContractRegistry);
|
|
export const videoGenerationProviderContractRegistry: VideoGenerationProviderContractEntry[] =
|
|
createLazyArrayView(loadVideoGenerationProviderContractRegistry);
|
|
export const musicGenerationProviderContractRegistry: MusicGenerationProviderContractEntry[] =
|
|
createLazyArrayView(loadMusicGenerationProviderContractRegistry);
|
|
|
|
function loadPluginRegistrationContractRegistry(): PluginRegistrationContractEntry[] {
|
|
return resolveBundledManifestContracts();
|
|
}
|
|
|
|
export const pluginRegistrationContractRegistry: PluginRegistrationContractEntry[] =
|
|
createLazyArrayView(loadPluginRegistrationContractRegistry);
|