
























@@ -39,6 +39,11 @@ export type GatewayStartupPluginPlan = {
3939};
40404141type NormalizedPluginsConfig = ReturnType<typeof normalizePluginsConfigWithRegistry>;
42+type GenerationProviderContractKey =
43+| "imageGenerationProviders"
44+| "videoGenerationProviders"
45+| "musicGenerationProviders";
46+type ConfiguredGenerationProviderIds = Record<GenerationProviderContractKey, ReadonlySet<string>>;
42474348function isRecord(value: unknown): value is Record<string, unknown> {
4449return Boolean(value && typeof value === "object" && !Array.isArray(value));
@@ -209,6 +214,123 @@ function manifestOwnsConfiguredSpeechProvider(params: {
209214});
210215}
211216217+function listModelProviderRefs(value: unknown): string[] {
218+if (typeof value === "string") {
219+return [value];
220+}
221+if (!isRecord(value)) {
222+return [];
223+}
224+const refs: string[] = [];
225+if (typeof value.primary === "string") {
226+refs.push(value.primary);
227+}
228+if (Array.isArray(value.fallbacks)) {
229+for (const fallback of value.fallbacks) {
230+if (typeof fallback === "string") {
231+refs.push(fallback);
232+}
233+}
234+}
235+return refs;
236+}
237+238+function collectModelProviderIds(value: unknown): ReadonlySet<string> {
239+return new Set(
240+listModelProviderRefs(value)
241+.map((ref) => {
242+const slashIndex = ref.indexOf("/");
243+return slashIndex > 0 ? normalizeOptionalLowercaseString(ref.slice(0, slashIndex)) : "";
244+})
245+.filter((providerId): providerId is string => Boolean(providerId)),
246+);
247+}
248+249+function collectConfiguredGenerationProviderIds(
250+config: OpenClawConfig,
251+): ConfiguredGenerationProviderIds {
252+const defaults = config.agents?.defaults;
253+return {
254+imageGenerationProviders: collectModelProviderIds(defaults?.imageGenerationModel),
255+videoGenerationProviders: collectModelProviderIds(defaults?.videoGenerationModel),
256+musicGenerationProviders: collectModelProviderIds(defaults?.musicGenerationModel),
257+};
258+}
259+260+function manifestOwnsConfiguredGenerationProvider(params: {
261+manifest: PluginManifestRecord | undefined;
262+configuredGenerationProviderIds: ConfiguredGenerationProviderIds;
263+}): boolean {
264+for (const contractKey of [
265+"imageGenerationProviders",
266+"videoGenerationProviders",
267+"musicGenerationProviders",
268+] as const) {
269+const configuredProviderIds = params.configuredGenerationProviderIds[contractKey];
270+if (configuredProviderIds.size === 0) {
271+continue;
272+}
273+if (
274+(params.manifest?.contracts?.[contractKey] ?? []).some((providerId) => {
275+const normalized = normalizeOptionalLowercaseString(providerId);
276+return normalized ? configuredProviderIds.has(normalized) : false;
277+})
278+) {
279+return true;
280+}
281+}
282+return false;
283+}
284+285+function canStartConfiguredGenerationProviderPlugin(params: {
286+plugin: InstalledPluginIndexRecord;
287+manifest: PluginManifestRecord | undefined;
288+config: OpenClawConfig;
289+pluginsConfig: ReturnType<typeof normalizePluginsConfigWithRegistry>;
290+activationSource: {
291+plugins: ReturnType<typeof normalizePluginsConfigWithRegistry>;
292+rootConfig?: OpenClawConfig;
293+};
294+configuredGenerationProviderIds: ConfiguredGenerationProviderIds;
295+platform?: NodeJS.Platform;
296+}): boolean {
297+if (
298+!manifestOwnsConfiguredGenerationProvider({
299+manifest: params.manifest,
300+configuredGenerationProviderIds: params.configuredGenerationProviderIds,
301+})
302+) {
303+return false;
304+}
305+if (!params.pluginsConfig.enabled || !params.activationSource.plugins.enabled) {
306+return false;
307+}
308+if (
309+params.pluginsConfig.deny.includes(params.plugin.pluginId) ||
310+params.activationSource.plugins.deny.includes(params.plugin.pluginId)
311+) {
312+return false;
313+}
314+if (
315+params.pluginsConfig.entries[params.plugin.pluginId]?.enabled === false ||
316+params.activationSource.plugins.entries[params.plugin.pluginId]?.enabled === false
317+) {
318+return false;
319+}
320+const activationState = resolveEffectivePluginActivationState({
321+id: params.plugin.pluginId,
322+origin: params.plugin.origin,
323+config: params.pluginsConfig,
324+rootConfig: params.config,
325+enabledByDefault: isPluginEnabledByDefaultForPlatform(params.plugin, params.platform),
326+activationSource: params.activationSource,
327+});
328+return (
329+activationState.enabled &&
330+(params.plugin.origin === "bundled" || activationState.explicitlyEnabled)
331+);
332+}
333+212334function canStartConfiguredSpeechProviderPlugin(params: {
213335plugin: InstalledPluginIndexRecord;
214336manifest: PluginManifestRecord | undefined;
@@ -512,6 +634,8 @@ export function resolveGatewayStartupPluginPlanFromRegistry(params: {
512634const startupDreamingPluginIds = resolveGatewayStartupDreamingPluginIds(params.config);
513635const manifestLookup = createManifestRegistryLookup(params.manifestRegistry);
514636const configuredSpeechProviderIds = collectConfiguredSpeechProviderIds(activationSourceConfig);
637+const configuredGenerationProviderIds =
638+collectConfiguredGenerationProviderIds(activationSourceConfig);
515639const normalizePluginId = createPluginRegistryIdNormalizer(params.index, {
516640manifestRegistry: params.manifestRegistry,
517641});
@@ -581,6 +705,19 @@ export function resolveGatewayStartupPluginPlanFromRegistry(params: {
581705) {
582706return true;
583707}
708+if (
709+canStartConfiguredGenerationProviderPlugin({
710+ plugin,
711+ manifest,
712+config: params.config,
713+ pluginsConfig,
714+ activationSource,
715+ configuredGenerationProviderIds,
716+platform: params.platform,
717+})
718+) {
719+return true;
720+}
584721if (
585722canStartExplicitHookPlugin({
586723 plugin,
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。