diff --git a/scripts/generate-bundled-plugin-metadata.mjs b/scripts/generate-bundled-plugin-metadata.mjs index 161b6bade7e..903f5a96324 100644 --- a/scripts/generate-bundled-plugin-metadata.mjs +++ b/scripts/generate-bundled-plugin-metadata.mjs @@ -1,10 +1,11 @@ import path from "node:path"; import { collectBundledPluginSources } from "./lib/bundled-plugin-source-utils.mjs"; import { formatGeneratedModule } from "./lib/format-generated-module.mjs"; -import { reportGeneratedOutputCli, writeGeneratedOutput } from "./lib/generated-output-utils.mjs"; +import { writeGeneratedOutput } from "./lib/generated-output-utils.mjs"; const GENERATED_BY = "scripts/generate-bundled-plugin-metadata.mjs"; const DEFAULT_OUTPUT_PATH = "src/plugins/bundled-plugin-metadata.generated.ts"; +const DEFAULT_ENTRIES_OUTPUT_PATH = "src/generated/bundled-plugin-entries.generated.ts"; const MANIFEST_KEY = "openclaw"; const FORMATTER_CWD = path.resolve(import.meta.dirname, ".."); const CANONICAL_PACKAGE_ID_ALIASES = { @@ -134,6 +135,19 @@ function formatTypeScriptModule(source, { outputPath }) { }); } +function toIdentifier(dirName) { + const cleaned = String(dirName) + .replace(/[^a-zA-Z0-9]+(.)/g, (_match, next) => next.toUpperCase()) + .replace(/[^a-zA-Z0-9]/g, "") + .replace(/^[^a-zA-Z]+/g, ""); + const base = cleaned || "plugin"; + return `${base[0].toLowerCase()}${base.slice(1)}Plugin`; +} + +function normalizeGeneratedImportPath(dirName, builtPath) { + return `../../extensions/${dirName}/${String(builtPath).replace(/^\.\//u, "")}`; +} + export function collectBundledPluginMetadata(params = {}) { const repoRoot = path.resolve(params.repoRoot ?? process.cwd()); const entries = []; @@ -202,23 +216,73 @@ export const GENERATED_BUNDLED_PLUGIN_METADATA = ${JSON.stringify(entries, null, `; } -export function writeBundledPluginMetadataModule(params = {}) { - const repoRoot = path.resolve(params.repoRoot ?? process.cwd()); - const outputPath = path.resolve(repoRoot, params.outputPath ?? DEFAULT_OUTPUT_PATH); - const next = formatTypeScriptModule( - renderBundledPluginMetadataModule(collectBundledPluginMetadata({ repoRoot })), - { outputPath }, - ); - return writeGeneratedOutput({ - repoRoot, - outputPath: params.outputPath ?? DEFAULT_OUTPUT_PATH, - next, - check: params.check, - }); +export function renderBundledPluginEntriesModule(entries) { + const imports = entries + .map((entry) => { + const identifier = toIdentifier(entry.dirName); + const importPath = normalizeGeneratedImportPath(entry.dirName, entry.source.built); + return `import ${identifier} from "${importPath}";`; + }) + .join("\n"); + const identifiers = entries.map((entry) => toIdentifier(entry.dirName)).join(",\n "); + return `// Auto-generated by ${GENERATED_BY}. Do not edit directly. + +${imports} + +export const GENERATED_BUNDLED_PLUGIN_ENTRIES = [ + ${identifiers} +] as const; +`; } -reportGeneratedOutputCli({ - importMetaUrl: import.meta.url, - label: "bundled-plugin-metadata", - run: ({ check }) => writeBundledPluginMetadataModule({ check }), -}); +export function writeBundledPluginMetadataModule(params = {}) { + const repoRoot = path.resolve(params.repoRoot ?? process.cwd()); + const entries = collectBundledPluginMetadata({ repoRoot }); + const outputPath = path.resolve(repoRoot, params.outputPath ?? DEFAULT_OUTPUT_PATH); + const entriesOutputPath = path.resolve( + repoRoot, + params.entriesOutputPath ?? DEFAULT_ENTRIES_OUTPUT_PATH, + ); + const metadataNext = formatTypeScriptModule(renderBundledPluginMetadataModule(entries), { + outputPath, + }); + const registryNext = formatTypeScriptModule(renderBundledPluginEntriesModule(entries), { + outputPath: entriesOutputPath, + }); + const metadataResult = writeGeneratedOutput({ + repoRoot, + outputPath: params.outputPath ?? DEFAULT_OUTPUT_PATH, + next: metadataNext, + check: params.check, + }); + const entriesResult = writeGeneratedOutput({ + repoRoot, + outputPath: params.entriesOutputPath ?? DEFAULT_ENTRIES_OUTPUT_PATH, + next: registryNext, + check: params.check, + }); + return { + changed: metadataResult.changed || entriesResult.changed, + wrote: metadataResult.wrote || entriesResult.wrote, + outputPaths: [metadataResult.outputPath, entriesResult.outputPath], + }; +} + +if (import.meta.url === new URL(process.argv[1] ?? "", "file:").href) { + const check = process.argv.includes("--check"); + const result = writeBundledPluginMetadataModule({ check }); + if (!result.changed) { + process.exitCode = 0; + } else if (check) { + for (const outputPath of result.outputPaths) { + const relativeOutputPath = path.relative(process.cwd(), outputPath); + console.error(`[bundled-plugin-metadata] stale generated output at ${relativeOutputPath}`); + } + process.exitCode = 1; + } else { + for (const outputPath of result.outputPaths) { + const relativeOutputPath = path.relative(process.cwd(), outputPath); + console.log(`[bundled-plugin-metadata] wrote ${relativeOutputPath}`); + } + } +} diff --git a/src/gateway/server-methods/tts.ts b/src/gateway/server-methods/tts.ts index 3894cc0bac2..38934afbae9 100644 --- a/src/gateway/server-methods/tts.ts +++ b/src/gateway/server-methods/tts.ts @@ -5,8 +5,6 @@ import { normalizeSpeechProviderId, } from "../../tts/provider-registry.js"; import { - OPENAI_TTS_MODELS, - OPENAI_TTS_VOICES, getTtsProvider, isTtsEnabled, isTtsProviderConfigured, @@ -138,14 +136,8 @@ export const ttsHandlers: GatewayRequestHandlers = { id: provider.id, name: provider.label, configured: provider.isConfigured({ cfg, config }), - models: - provider.id === "openai" && provider.models == null - ? [...OPENAI_TTS_MODELS] - : [...(provider.models ?? [])], - voices: - provider.id === "openai" && provider.voices == null - ? [...OPENAI_TTS_VOICES] - : [...(provider.voices ?? [])], + models: [...(provider.models ?? [])], + voices: [...(provider.voices ?? [])], })), active: getTtsProvider(config, prefsPath), }); diff --git a/src/generated/bundled-plugin-entries.generated.ts b/src/generated/bundled-plugin-entries.generated.ts new file mode 100644 index 00000000000..67c003247f0 --- /dev/null +++ b/src/generated/bundled-plugin-entries.generated.ts @@ -0,0 +1,157 @@ +// Auto-generated by scripts/generate-bundled-plugin-metadata.mjs. Do not edit directly. + +import acpxPlugin from "../../extensions/acpx/index.js"; +import amazonBedrockPlugin from "../../extensions/amazon-bedrock/index.js"; +import anthropicPlugin from "../../extensions/anthropic/index.js"; +import bluebubblesPlugin from "../../extensions/bluebubbles/index.js"; +import bravePlugin from "../../extensions/brave/index.js"; +import byteplusPlugin from "../../extensions/byteplus/index.js"; +import chutesPlugin from "../../extensions/chutes/index.js"; +import cloudflareAiGatewayPlugin from "../../extensions/cloudflare-ai-gateway/index.js"; +import copilotProxyPlugin from "../../extensions/copilot-proxy/index.js"; +import deepgramPlugin from "../../extensions/deepgram/index.js"; +import deepseekPlugin from "../../extensions/deepseek/index.js"; +import diagnosticsOtelPlugin from "../../extensions/diagnostics-otel/index.js"; +import diffsPlugin from "../../extensions/diffs/index.js"; +import discordPlugin from "../../extensions/discord/index.js"; +import duckduckgoPlugin from "../../extensions/duckduckgo/index.js"; +import elevenlabsPlugin from "../../extensions/elevenlabs/index.js"; +import exaPlugin from "../../extensions/exa/index.js"; +import falPlugin from "../../extensions/fal/index.js"; +import feishuPlugin from "../../extensions/feishu/index.js"; +import firecrawlPlugin from "../../extensions/firecrawl/index.js"; +import githubCopilotPlugin from "../../extensions/github-copilot/index.js"; +import googlePlugin from "../../extensions/google/index.js"; +import googlechatPlugin from "../../extensions/googlechat/index.js"; +import groqPlugin from "../../extensions/groq/index.js"; +import huggingfacePlugin from "../../extensions/huggingface/index.js"; +import imessagePlugin from "../../extensions/imessage/index.js"; +import ircPlugin from "../../extensions/irc/index.js"; +import kilocodePlugin from "../../extensions/kilocode/index.js"; +import kimiCodingPlugin from "../../extensions/kimi-coding/index.js"; +import linePlugin from "../../extensions/line/index.js"; +import llmTaskPlugin from "../../extensions/llm-task/index.js"; +import lobsterPlugin from "../../extensions/lobster/index.js"; +import matrixPlugin from "../../extensions/matrix/index.js"; +import mattermostPlugin from "../../extensions/mattermost/index.js"; +import memoryCorePlugin from "../../extensions/memory-core/index.js"; +import memoryLancedbPlugin from "../../extensions/memory-lancedb/index.js"; +import microsoftFoundryPlugin from "../../extensions/microsoft-foundry/index.js"; +import microsoftPlugin from "../../extensions/microsoft/index.js"; +import minimaxPlugin from "../../extensions/minimax/index.js"; +import mistralPlugin from "../../extensions/mistral/index.js"; +import modelstudioPlugin from "../../extensions/modelstudio/index.js"; +import moonshotPlugin from "../../extensions/moonshot/index.js"; +import msteamsPlugin from "../../extensions/msteams/index.js"; +import nextcloudTalkPlugin from "../../extensions/nextcloud-talk/index.js"; +import nostrPlugin from "../../extensions/nostr/index.js"; +import nvidiaPlugin from "../../extensions/nvidia/index.js"; +import ollamaPlugin from "../../extensions/ollama/index.js"; +import openProsePlugin from "../../extensions/open-prose/index.js"; +import openaiPlugin from "../../extensions/openai/index.js"; +import opencodeGoPlugin from "../../extensions/opencode-go/index.js"; +import opencodePlugin from "../../extensions/opencode/index.js"; +import openrouterPlugin from "../../extensions/openrouter/index.js"; +import openshellPlugin from "../../extensions/openshell/index.js"; +import perplexityPlugin from "../../extensions/perplexity/index.js"; +import qianfanPlugin from "../../extensions/qianfan/index.js"; +import sglangPlugin from "../../extensions/sglang/index.js"; +import signalPlugin from "../../extensions/signal/index.js"; +import slackPlugin from "../../extensions/slack/index.js"; +import synologyChatPlugin from "../../extensions/synology-chat/index.js"; +import syntheticPlugin from "../../extensions/synthetic/index.js"; +import tavilyPlugin from "../../extensions/tavily/index.js"; +import telegramPlugin from "../../extensions/telegram/index.js"; +import tlonPlugin from "../../extensions/tlon/index.js"; +import togetherPlugin from "../../extensions/together/index.js"; +import twitchPlugin from "../../extensions/twitch/index.js"; +import venicePlugin from "../../extensions/venice/index.js"; +import vercelAiGatewayPlugin from "../../extensions/vercel-ai-gateway/index.js"; +import vllmPlugin from "../../extensions/vllm/index.js"; +import voiceCallPlugin from "../../extensions/voice-call/index.js"; +import volcenginePlugin from "../../extensions/volcengine/index.js"; +import whatsappPlugin from "../../extensions/whatsapp/index.js"; +import xaiPlugin from "../../extensions/xai/index.js"; +import xiaomiPlugin from "../../extensions/xiaomi/index.js"; +import zaiPlugin from "../../extensions/zai/index.js"; +import zaloPlugin from "../../extensions/zalo/index.js"; +import zalouserPlugin from "../../extensions/zalouser/index.js"; + +export const GENERATED_BUNDLED_PLUGIN_ENTRIES = [ + acpxPlugin, + amazonBedrockPlugin, + anthropicPlugin, + bluebubblesPlugin, + bravePlugin, + byteplusPlugin, + chutesPlugin, + cloudflareAiGatewayPlugin, + copilotProxyPlugin, + deepgramPlugin, + deepseekPlugin, + diagnosticsOtelPlugin, + diffsPlugin, + discordPlugin, + duckduckgoPlugin, + elevenlabsPlugin, + exaPlugin, + falPlugin, + feishuPlugin, + firecrawlPlugin, + githubCopilotPlugin, + googlePlugin, + googlechatPlugin, + groqPlugin, + huggingfacePlugin, + imessagePlugin, + ircPlugin, + kilocodePlugin, + kimiCodingPlugin, + linePlugin, + llmTaskPlugin, + lobsterPlugin, + matrixPlugin, + mattermostPlugin, + memoryCorePlugin, + memoryLancedbPlugin, + microsoftPlugin, + microsoftFoundryPlugin, + minimaxPlugin, + mistralPlugin, + modelstudioPlugin, + moonshotPlugin, + msteamsPlugin, + nextcloudTalkPlugin, + nostrPlugin, + nvidiaPlugin, + ollamaPlugin, + openProsePlugin, + openaiPlugin, + opencodePlugin, + opencodeGoPlugin, + openrouterPlugin, + openshellPlugin, + perplexityPlugin, + qianfanPlugin, + sglangPlugin, + signalPlugin, + slackPlugin, + synologyChatPlugin, + syntheticPlugin, + tavilyPlugin, + telegramPlugin, + tlonPlugin, + togetherPlugin, + twitchPlugin, + venicePlugin, + vercelAiGatewayPlugin, + vllmPlugin, + voiceCallPlugin, + volcenginePlugin, + whatsappPlugin, + xaiPlugin, + xiaomiPlugin, + zaiPlugin, + zaloPlugin, + zalouserPlugin, +] as const; diff --git a/src/plugins/bundled-plugin-entries.ts b/src/plugins/bundled-plugin-entries.ts new file mode 100644 index 00000000000..31f1d1a8e75 --- /dev/null +++ b/src/plugins/bundled-plugin-entries.ts @@ -0,0 +1,10 @@ +import { GENERATED_BUNDLED_PLUGIN_ENTRIES } from "../generated/bundled-plugin-entries.generated.js"; +import type { OpenClawPluginDefinition } from "./types.js"; + +type BundledRegistrablePlugin = OpenClawPluginDefinition & { + id: string; + register: NonNullable; +}; + +export const BUNDLED_PLUGIN_ENTRIES = + GENERATED_BUNDLED_PLUGIN_ENTRIES as unknown as readonly BundledRegistrablePlugin[]; diff --git a/src/plugins/contracts/registry.ts b/src/plugins/contracts/registry.ts index 73578d401f2..01fd7d4f91a 100644 --- a/src/plugins/contracts/registry.ts +++ b/src/plugins/contracts/registry.ts @@ -1,46 +1,7 @@ -import amazonBedrockPlugin from "../../../extensions/amazon-bedrock/index.js"; -import anthropicPlugin from "../../../extensions/anthropic/index.js"; -import byteplusPlugin from "../../../extensions/byteplus/index.js"; -import chutesPlugin from "../../../extensions/chutes/index.js"; -import cloudflareAiGatewayPlugin from "../../../extensions/cloudflare-ai-gateway/index.js"; -import copilotProxyPlugin from "../../../extensions/copilot-proxy/index.js"; -import deepgramPlugin from "../../../extensions/deepgram/index.js"; -import deepseekPlugin from "../../../extensions/deepseek/index.js"; -import elevenLabsPlugin from "../../../extensions/elevenlabs/index.js"; -import falPlugin from "../../../extensions/fal/index.js"; -import githubCopilotPlugin from "../../../extensions/github-copilot/index.js"; -import googlePlugin from "../../../extensions/google/index.js"; -import groqPlugin from "../../../extensions/groq/index.js"; -import huggingFacePlugin from "../../../extensions/huggingface/index.js"; -import kilocodePlugin from "../../../extensions/kilocode/index.js"; -import kimiCodingPlugin from "../../../extensions/kimi-coding/index.js"; -import microsoftFoundryPlugin from "../../../extensions/microsoft-foundry/index.js"; -import microsoftPlugin from "../../../extensions/microsoft/index.js"; -import minimaxPlugin from "../../../extensions/minimax/index.js"; -import mistralPlugin from "../../../extensions/mistral/index.js"; -import modelStudioPlugin from "../../../extensions/modelstudio/index.js"; -import moonshotPlugin from "../../../extensions/moonshot/index.js"; -import nvidiaPlugin from "../../../extensions/nvidia/index.js"; -import ollamaPlugin from "../../../extensions/ollama/index.js"; -import openAIPlugin from "../../../extensions/openai/index.js"; -import opencodeGoPlugin from "../../../extensions/opencode-go/index.js"; -import opencodePlugin from "../../../extensions/opencode/index.js"; -import openrouterPlugin from "../../../extensions/openrouter/index.js"; -import qianfanPlugin from "../../../extensions/qianfan/index.js"; -import sglangPlugin from "../../../extensions/sglang/index.js"; -import syntheticPlugin from "../../../extensions/synthetic/index.js"; -import togetherPlugin from "../../../extensions/together/index.js"; -import venicePlugin from "../../../extensions/venice/index.js"; -import vercelAiGatewayPlugin from "../../../extensions/vercel-ai-gateway/index.js"; -import vllmPlugin from "../../../extensions/vllm/index.js"; -import volcenginePlugin from "../../../extensions/volcengine/index.js"; -import xaiPlugin from "../../../extensions/xai/index.js"; -import xiaomiPlugin from "../../../extensions/xiaomi/index.js"; -import zaiPlugin from "../../../extensions/zai/index.js"; import { bundledWebSearchPluginRegistrations } from "../../bundled-web-search-registry.js"; +import { BUNDLED_PLUGIN_ENTRIES } from "../bundled-plugin-entries.js"; import { createCapturedPluginRegistration } from "../captured-registration.js"; import { loadPluginManifestRegistry } from "../manifest-registry.js"; -import { resolvePluginProviders } from "../provider-auth-choice.runtime.js"; import type { ImageGenerationProviderPlugin, MediaUnderstandingProviderPlugin, @@ -118,28 +79,6 @@ function dedupePlugins( export let providerContractLoadError: Error | undefined; -function loadBundledProviderRegistry(): ProviderContractEntry[] { - try { - providerContractLoadError = undefined; - return resolvePluginProviders({ - bundledProviderAllowlistCompat: true, - bundledProviderVitestCompat: true, - cache: false, - activate: false, - }) - .filter((provider: ProviderPlugin): provider is ProviderPlugin & { pluginId: string } => - Boolean(provider.pluginId), - ) - .map((provider: ProviderPlugin & { pluginId: string }) => ({ - pluginId: provider.pluginId, - provider, - })); - } catch (error) { - providerContractLoadError = error instanceof Error ? error : new Error(String(error)); - return []; - } -} - function createLazyArrayView(load: () => T[]): T[] { return new Proxy([] as T[], { get(_target, prop) { @@ -181,23 +120,21 @@ let mediaUnderstandingProviderContractRegistryCache: let imageGenerationProviderContractRegistryCache: ImageGenerationProviderContractEntry[] | null = null; let pluginRegistrationContractRegistryCache: PluginRegistrationContractEntry[] | null = null; -let providerRegistrationEntriesLoaded = false; function loadProviderContractRegistry(): ProviderContractEntry[] { if (!providerContractRegistryCache) { - providerContractRegistryCache = buildCapabilityContractRegistry({ - plugins: bundledProviderPlugins, - select: (captured) => captured.providers, - }).map((entry) => ({ - pluginId: entry.pluginId, - provider: entry.provider, - })); - } - if (!providerRegistrationEntriesLoaded) { - const registrationEntries = loadPluginRegistrationContractRegistry(); - if (!providerRegistrationEntriesLoaded) { - mergeProviderContractRegistrations(registrationEntries, providerContractRegistryCache); - providerRegistrationEntriesLoaded = true; + try { + providerContractLoadError = undefined; + providerContractRegistryCache = buildCapabilityContractRegistry({ + plugins: bundledProviderPlugins, + select: (captured) => captured.providers, + }).map((entry) => ({ + pluginId: entry.pluginId, + provider: entry.provider, + })); + } catch (error) { + providerContractLoadError = error instanceof Error ? error : new Error(String(error)); + providerContractRegistryCache = []; } } return providerContractRegistryCache; @@ -243,7 +180,7 @@ export function requireProviderContractProvider(providerId: string): ProviderPlu const provider = uniqueProviderContractProviders.find((entry) => entry.id === providerId); if (!provider) { if (!providerContractLoadError) { - loadBundledProviderRegistry(); + loadProviderContractRegistry(); } if (providerContractLoadError) { throw new Error( @@ -338,92 +275,52 @@ export const mediaUnderstandingProviderContractRegistry: MediaUnderstandingProvi export const imageGenerationProviderContractRegistry: ImageGenerationProviderContractEntry[] = createLazyArrayView(loadImageGenerationProviderContractRegistry); -const bundledProviderPlugins = dedupePlugins([ - amazonBedrockPlugin, - anthropicPlugin, - byteplusPlugin, - chutesPlugin, - cloudflareAiGatewayPlugin, - copilotProxyPlugin, - deepseekPlugin, - githubCopilotPlugin, - falPlugin, - googlePlugin, - huggingFacePlugin, - kilocodePlugin, - kimiCodingPlugin, - microsoftFoundryPlugin, - minimaxPlugin, - mistralPlugin, - modelStudioPlugin, - moonshotPlugin, - nvidiaPlugin, - ollamaPlugin, - openAIPlugin, - opencodePlugin, - opencodeGoPlugin, - openrouterPlugin, - qianfanPlugin, - sglangPlugin, - syntheticPlugin, - togetherPlugin, - venicePlugin, - vercelAiGatewayPlugin, - vllmPlugin, - volcenginePlugin, - xaiPlugin, - xiaomiPlugin, - zaiPlugin, -]); - const bundledRegistrablePluginsById = new Map( - dedupePlugins([ - ...bundledProviderPlugins, - elevenLabsPlugin, - microsoftPlugin, - deepgramPlugin, - groqPlugin, - ...bundledWebSearchPlugins, - ]).map((plugin) => [plugin.id, plugin]), + dedupePlugins([...BUNDLED_PLUGIN_ENTRIES, ...bundledWebSearchPlugins]).map((plugin) => [ + plugin.id, + plugin, + ]), ); -function resolveBundledCapabilityPluginIds( - capability: "speechProviders" | "mediaUnderstandingProviders" | "imageGenerationProviders", +function resolveBundledManifestPluginIds( + predicate: (plugin: ReturnType["plugins"][number]) => boolean, ): string[] { return loadPluginManifestRegistry({}) - .plugins.filter( - (plugin) => plugin.origin === "bundled" && (plugin[capability]?.length ?? 0) > 0, - ) + .plugins.filter((plugin) => plugin.origin === "bundled" && predicate(plugin)) .map((plugin) => plugin.id) .toSorted((left, right) => left.localeCompare(right)); } -function resolveBundledCapabilityPlugins( - capability: "speechProviders" | "mediaUnderstandingProviders" | "imageGenerationProviders", +function resolveBundledRegistrablePlugins( + predicate: (plugin: ReturnType["plugins"][number]) => boolean, ): RegistrablePlugin[] { - return resolveBundledCapabilityPluginIds(capability).flatMap((pluginId) => { + return resolveBundledManifestPluginIds(predicate).flatMap((pluginId) => { const plugin = bundledRegistrablePluginsById.get(pluginId); return plugin ? [plugin] : []; }); } -const bundledSpeechPlugins = resolveBundledCapabilityPlugins("speechProviders"); -const bundledMediaUnderstandingPlugins = resolveBundledCapabilityPlugins( - "mediaUnderstandingProviders", +const bundledProviderPlugins = resolveBundledRegistrablePlugins( + (plugin) => plugin.providers.length > 0, +); +const bundledSpeechPlugins = resolveBundledRegistrablePlugins( + (plugin) => (plugin.speechProviders?.length ?? 0) > 0, +); +const bundledMediaUnderstandingPlugins = resolveBundledRegistrablePlugins( + (plugin) => (plugin.mediaUnderstandingProviders?.length ?? 0) > 0, +); +const bundledImageGenerationPlugins = resolveBundledRegistrablePlugins( + (plugin) => (plugin.imageGenerationProviders?.length ?? 0) > 0, ); -const bundledImageGenerationPlugins = resolveBundledCapabilityPlugins("imageGenerationProviders"); const bundledPluginRegistrationList = dedupePlugins([ + ...bundledProviderPlugins, ...bundledSpeechPlugins, ...bundledMediaUnderstandingPlugins, ...bundledImageGenerationPlugins, ...bundledWebSearchPlugins, ]); -function mergeIds(existing: string[], next: string[]): string[] { - return next.length > 0 ? next : existing; -} - function upsertPluginRegistrationContractEntry( entries: PluginRegistrationContractEntry[], next: PluginRegistrationContractEntry, @@ -433,46 +330,27 @@ function upsertPluginRegistrationContractEntry( entries.push(next); return; } - existing.cliBackendIds = mergeIds(existing.cliBackendIds, next.cliBackendIds); - existing.providerIds = mergeIds(existing.providerIds, next.providerIds); - existing.speechProviderIds = mergeIds(existing.speechProviderIds, next.speechProviderIds); - existing.mediaUnderstandingProviderIds = mergeIds( - existing.mediaUnderstandingProviderIds, - next.mediaUnderstandingProviderIds, + existing.cliBackendIds = [ + ...new Set([...existing.cliBackendIds, ...next.cliBackendIds]), + ].toSorted((left, right) => left.localeCompare(right)); + existing.providerIds = [...new Set([...existing.providerIds, ...next.providerIds])].toSorted( + (left, right) => left.localeCompare(right), ); - existing.imageGenerationProviderIds = mergeIds( - existing.imageGenerationProviderIds, - next.imageGenerationProviderIds, + existing.speechProviderIds = [ + ...new Set([...existing.speechProviderIds, ...next.speechProviderIds]), + ].toSorted((left, right) => left.localeCompare(right)); + existing.mediaUnderstandingProviderIds = [ + ...new Set([...existing.mediaUnderstandingProviderIds, ...next.mediaUnderstandingProviderIds]), + ].toSorted((left, right) => left.localeCompare(right)); + existing.imageGenerationProviderIds = [ + ...new Set([...existing.imageGenerationProviderIds, ...next.imageGenerationProviderIds]), + ].toSorted((left, right) => left.localeCompare(right)); + existing.webSearchProviderIds = [ + ...new Set([...existing.webSearchProviderIds, ...next.webSearchProviderIds]), + ].toSorted((left, right) => left.localeCompare(right)); + existing.toolNames = [...new Set([...existing.toolNames, ...next.toolNames])].toSorted( + (left, right) => left.localeCompare(right), ); - existing.webSearchProviderIds = mergeIds( - existing.webSearchProviderIds, - next.webSearchProviderIds, - ); - existing.toolNames = mergeIds(existing.toolNames, next.toolNames); -} - -function mergeProviderContractRegistrations( - registrationEntries: PluginRegistrationContractEntry[], - providerEntries: ProviderContractEntry[], -): void { - const byPluginId = new Map(); - for (const entry of providerEntries) { - const providerIds = byPluginId.get(entry.pluginId) ?? []; - providerIds.push(entry.provider.id); - byPluginId.set(entry.pluginId, providerIds); - } - for (const [pluginId, providerIds] of byPluginId) { - upsertPluginRegistrationContractEntry(registrationEntries, { - pluginId, - cliBackendIds: [], - providerIds: providerIds.toSorted((left, right) => left.localeCompare(right)), - speechProviderIds: [], - mediaUnderstandingProviderIds: [], - imageGenerationProviderIds: [], - webSearchProviderIds: [], - toolNames: [], - }); - } } function loadPluginRegistrationContractRegistry(): PluginRegistrationContractEntry[] { @@ -497,13 +375,6 @@ function loadPluginRegistrationContractRegistry(): PluginRegistrationContractEnt } pluginRegistrationContractRegistryCache = entries; } - if (providerContractRegistryCache && !providerRegistrationEntriesLoaded) { - mergeProviderContractRegistrations( - pluginRegistrationContractRegistryCache, - providerContractRegistryCache, - ); - providerRegistrationEntriesLoaded = true; - } return pluginRegistrationContractRegistryCache; }