feat(plugins): add media understanding provider registration

This commit is contained in:
Peter Steinberger
2026-03-16 20:42:00 -07:00
parent 14907d3de0
commit 3e010e280a
26 changed files with 222 additions and 8 deletions

View File

@@ -23,6 +23,7 @@ import {
import { buildTokenProfileId, validateAnthropicSetupToken } from "../../src/commands/auth-token.js";
import { applyAuthProfileConfig } from "../../src/commands/onboard-auth.js";
import { fetchClaudeUsage } from "../../src/infra/provider-usage.fetch.js";
import { anthropicProvider } from "../../src/media-understanding/providers/anthropic/index.js";
import { createProviderApiKeyAuthMethod } from "../../src/plugins/provider-api-key-auth.js";
import type { ProviderAuthResult } from "../../src/plugins/types.js";
import { normalizeSecretInput } from "../../src/utils/normalize-secret-input.js";
@@ -394,6 +395,7 @@ const anthropicPlugin = {
profileId: ctx.profileId,
}),
});
api.registerMediaUnderstandingProvider(anthropicProvider);
},
};

View File

@@ -7,6 +7,7 @@ import {
GOOGLE_GEMINI_DEFAULT_MODEL,
applyGoogleGeminiModelDefault,
} from "../../src/commands/google-gemini-model-default.js";
import { googleProvider } from "../../src/media-understanding/providers/google/index.js";
import { emptyPluginConfigSchema } from "../../src/plugins/config-schema.js";
import { createProviderApiKeyAuthMethod } from "../../src/plugins/provider-api-key-auth.js";
import type { OpenClawPluginApi } from "../../src/plugins/types.js";
@@ -51,6 +52,7 @@ const googlePlugin = {
isModernModelRef: ({ modelId }) => isModernGoogleModel(modelId),
});
registerGoogleGeminiCliProvider(api);
api.registerMediaUnderstandingProvider(googleProvider);
api.registerWebSearchProvider(
createPluginBackedWebSearchProvider({
id: "gemini",

View File

@@ -45,6 +45,7 @@ function fakeApi(overrides: Partial<OpenClawPluginApi> = {}): OpenClawPluginApi
registerService() {},
registerProvider() {},
registerSpeechProvider() {},
registerMediaUnderstandingProvider() {},
registerWebSearchProvider() {},
registerInteractiveHandler() {},
registerHook() {},

View File

@@ -9,6 +9,10 @@ import {
import { ensureAuthProfileStore, listProfilesForProvider } from "../../src/agents/auth-profiles.js";
import { MINIMAX_OAUTH_MARKER } from "../../src/agents/model-auth-markers.js";
import { fetchMinimaxUsage } from "../../src/infra/provider-usage.fetch.js";
import {
minimaxPortalProvider,
minimaxProvider,
} from "../../src/media-understanding/providers/minimax/index.js";
import { createProviderApiKeyAuthMethod } from "../../src/plugins/provider-api-key-auth.js";
import { loginMiniMaxPortalOAuth, type MiniMaxRegion } from "./oauth.js";
import { applyMinimaxApiConfig, applyMinimaxApiConfigCn } from "./onboard.js";
@@ -270,6 +274,8 @@ const minimaxPlugin = {
],
isModernModelRef: ({ modelId }) => isModernMiniMaxModel(modelId),
});
api.registerMediaUnderstandingProvider(minimaxProvider);
api.registerMediaUnderstandingProvider(minimaxPortalProvider);
},
};

View File

@@ -1,4 +1,5 @@
import { emptyPluginConfigSchema, type OpenClawPluginApi } from "openclaw/plugin-sdk/core";
import { mistralProvider } from "../../src/media-understanding/providers/mistral/index.js";
import { createProviderApiKeyAuthMethod } from "../../src/plugins/provider-api-key-auth.js";
import { applyMistralConfig, MISTRAL_DEFAULT_MODEL_REF } from "./onboard.js";
@@ -50,6 +51,7 @@ const mistralPlugin = {
],
},
});
api.registerMediaUnderstandingProvider(mistralProvider);
},
};

View File

@@ -7,6 +7,7 @@ import {
getScopedCredentialValue,
setScopedCredentialValue,
} from "../../src/agents/tools/web-search-plugin-factory.js";
import { moonshotProvider } from "../../src/media-understanding/providers/moonshot/index.js";
import { emptyPluginConfigSchema } from "../../src/plugins/config-schema.js";
import { createProviderApiKeyAuthMethod } from "../../src/plugins/provider-api-key-auth.js";
import type { OpenClawPluginApi } from "../../src/plugins/types.js";
@@ -99,6 +100,7 @@ const moonshotPlugin = {
return createMoonshotThinkingWrapper(ctx.streamFn, thinkingType);
},
});
api.registerMediaUnderstandingProvider(moonshotProvider);
api.registerWebSearchProvider(
createPluginBackedWebSearchProvider({
id: "kimi",

View File

@@ -1,4 +1,5 @@
import { emptyPluginConfigSchema, type OpenClawPluginApi } from "openclaw/plugin-sdk/core";
import { openaiProvider } from "../../src/media-understanding/providers/openai/index.js";
import { buildOpenAISpeechProvider } from "../../src/tts/providers/openai.js";
import { buildOpenAICodexProviderPlugin } from "./openai-codex-provider.js";
import { buildOpenAIProvider } from "./openai-provider.js";
@@ -12,6 +13,7 @@ const openAIPlugin = {
api.registerProvider(buildOpenAIProvider());
api.registerProvider(buildOpenAICodexProviderPlugin());
api.registerSpeechProvider(buildOpenAISpeechProvider());
api.registerMediaUnderstandingProvider(openaiProvider);
},
};

View File

@@ -16,6 +16,7 @@ export function createTestPluginApi(api: TestPluginApiInput): OpenClawPluginApi
registerService() {},
registerProvider() {},
registerSpeechProvider() {},
registerMediaUnderstandingProvider() {},
registerWebSearchProvider() {},
registerInteractiveHandler() {},
registerCommand() {},

View File

@@ -24,6 +24,7 @@ import { applyAuthProfileConfig } from "../../src/commands/onboard-auth.js";
import type { SecretInput } from "../../src/config/types.secrets.js";
import { resolveRequiredHomeDir } from "../../src/infra/home-dir.js";
import { fetchZaiUsage } from "../../src/infra/provider-usage.fetch.js";
import { zaiProvider } from "../../src/media-understanding/providers/zai/index.js";
import { normalizeOptionalSecretInput } from "../../src/utils/normalize-secret-input.js";
import { detectZaiEndpoint, type ZaiEndpointId } from "./detect.js";
import { applyZaiConfig, applyZaiProviderConfig, ZAI_DEFAULT_MODEL_REF } from "./onboard.js";
@@ -334,6 +335,7 @@ const zaiPlugin = {
fetchUsageSnapshot: async (ctx) => await fetchZaiUsage(ctx.token, ctx.timeoutMs, ctx.fetchFn),
isCacheTtlEligible: () => true,
});
api.registerMediaUnderstandingProvider(zaiProvider);
},
};

View File

@@ -92,6 +92,7 @@ const createRegistry = (channels: PluginRegistry["channels"]): PluginRegistry =>
})),
providers: [],
speechProviders: [],
mediaUnderstandingProviders: [],
webSearchProviders: [],
gatewayHandlers: {},
httpRoutes: [],

View File

@@ -338,6 +338,7 @@ describe("ensureChannelSetupPluginInstalled", () => {
channelIds: [],
providerIds: [],
speechProviderIds: [],
mediaUnderstandingProviderIds: [],
webSearchProviderIds: [],
gatewayMethods: [],
cliCommands: [],

View File

@@ -30,6 +30,7 @@ const createRegistry = (diagnostics: PluginDiagnostic[]): PluginRegistry => ({
commands: [],
providers: [],
speechProviders: [],
mediaUnderstandingProviders: [],
webSearchProviders: [],
gatewayHandlers: {},
httpRoutes: [],

View File

@@ -147,6 +147,7 @@ const createStubPluginRegistry = (): PluginRegistry => ({
channelSetups: [],
providers: [],
speechProviders: [],
mediaUnderstandingProviders: [],
webSearchProviders: [],
gatewayHandlers: {},
httpRoutes: [],

View File

@@ -1,7 +1,13 @@
import { describe, expect, it } from "vitest";
import { afterEach, describe, expect, it } from "vitest";
import { createEmptyPluginRegistry } from "../../plugins/registry.js";
import { setActivePluginRegistry } from "../../plugins/runtime.js";
import { buildMediaUnderstandingRegistry, getMediaUnderstandingProvider } from "./index.js";
describe("media-understanding provider registry", () => {
afterEach(() => {
setActivePluginRegistry(createEmptyPluginRegistry());
});
it("registers the Mistral provider", () => {
const registry = buildMediaUnderstandingRegistry();
const provider = getMediaUnderstandingProvider("mistral", registry);
@@ -32,4 +38,27 @@ describe("media-understanding provider registry", () => {
expect(provider?.id).toBe("minimax-portal");
expect(provider?.capabilities).toEqual(["image"]);
});
it("merges plugin-registered media providers into the active registry", async () => {
const pluginRegistry = createEmptyPluginRegistry();
pluginRegistry.mediaUnderstandingProviders.push({
pluginId: "google",
pluginName: "Google Plugin",
source: "test",
provider: {
id: "google",
capabilities: ["image", "audio", "video"],
describeImage: async () => ({ text: "plugin image" }),
transcribeAudio: async () => ({ text: "plugin audio" }),
describeVideo: async () => ({ text: "plugin video" }),
},
});
setActivePluginRegistry(pluginRegistry);
const registry = buildMediaUnderstandingRegistry();
const provider = getMediaUnderstandingProvider("gemini", registry);
expect(provider?.id).toBe("google");
expect(await provider?.describeVideo?.({} as never)).toEqual({ text: "plugin video" });
});
});

View File

@@ -1,4 +1,5 @@
import { normalizeProviderId } from "../../agents/model-selection.js";
import { getActivePluginRegistry } from "../../plugins/runtime.js";
import type { MediaUnderstandingProvider } from "../types.js";
import { anthropicProvider } from "./anthropic/index.js";
import { deepgramProvider } from "./deepgram/index.js";
@@ -23,6 +24,22 @@ const PROVIDERS: MediaUnderstandingProvider[] = [
deepgramProvider,
];
function mergeProviderIntoRegistry(
registry: Map<string, MediaUnderstandingProvider>,
provider: MediaUnderstandingProvider,
) {
const normalizedKey = normalizeMediaProviderId(provider.id);
const existing = registry.get(normalizedKey);
const merged = existing
? {
...existing,
...provider,
capabilities: provider.capabilities ?? existing.capabilities,
}
: provider;
registry.set(normalizedKey, merged);
}
export function normalizeMediaProviderId(id: string): string {
const normalized = normalizeProviderId(id);
if (normalized === "gemini") {
@@ -36,7 +53,10 @@ export function buildMediaUnderstandingRegistry(
): Map<string, MediaUnderstandingProvider> {
const registry = new Map<string, MediaUnderstandingProvider>();
for (const provider of PROVIDERS) {
registry.set(normalizeMediaProviderId(provider.id), provider);
mergeProviderIntoRegistry(registry, provider);
}
for (const entry of getActivePluginRegistry()?.mediaUnderstandingProviders ?? []) {
mergeProviderIntoRegistry(registry, entry.provider);
}
if (overrides) {
for (const [key, provider] of Object.entries(overrides)) {

View File

@@ -1,5 +1,6 @@
export type {
AnyAgentTool,
MediaUnderstandingProviderPlugin,
OpenClawPluginConfigSchema,
ProviderDiscoveryContext,
ProviderCatalogContext,

View File

@@ -108,6 +108,7 @@ export { ACP_ERROR_CODES, AcpRuntimeError } from "../acp/runtime/errors.js";
export type { AcpRuntimeErrorCode } from "../acp/runtime/errors.js";
export type {
AnyAgentTool,
MediaUnderstandingProviderPlugin,
OpenClawPluginConfigSchema,
OpenClawPluginApi,
OpenClawPluginService,

View File

@@ -19,6 +19,7 @@ describe("plugin loader contract", () => {
loadOpenClawPluginsMock.mockReset();
loadOpenClawPluginsMock.mockReturnValue({
providers: [],
mediaUnderstandingProviders: [],
webSearchProviders: [],
});
});

View File

@@ -1,5 +1,6 @@
import { describe, expect, it } from "vitest";
import {
mediaUnderstandingProviderContractRegistry,
pluginRegistrationContractRegistry,
providerContractRegistry,
speechProviderContractRegistry,
@@ -35,6 +36,13 @@ function findSpeechProviderForPlugin(pluginId: string) {
return entry.provider;
}
function findMediaUnderstandingProviderIdsForPlugin(pluginId: string) {
return mediaUnderstandingProviderContractRegistry
.filter((entry) => entry.pluginId === pluginId)
.map((entry) => entry.provider.id)
.toSorted((left, right) => left.localeCompare(right));
}
function findRegistrationForPlugin(pluginId: string) {
const entry = pluginRegistrationContractRegistry.find(
(candidate) => candidate.pluginId === pluginId,
@@ -61,6 +69,11 @@ describe("plugin contract registry", () => {
expect(ids).toEqual([...new Set(ids)]);
});
it("does not duplicate bundled media provider ids", () => {
const ids = mediaUnderstandingProviderContractRegistry.map((entry) => entry.provider.id);
expect(ids).toEqual([...new Set(ids)]);
});
it("keeps multi-provider plugin ownership explicit", () => {
expect(findProviderIdsForPlugin("google")).toEqual(["google", "google-gemini-cli"]);
expect(findProviderIdsForPlugin("minimax")).toEqual(["minimax", "minimax-portal"]);
@@ -82,10 +95,24 @@ describe("plugin contract registry", () => {
expect(findSpeechProviderIdsForPlugin("openai")).toEqual(["openai"]);
});
it("keeps bundled media-understanding ownership explicit", () => {
expect(findMediaUnderstandingProviderIdsForPlugin("anthropic")).toEqual(["anthropic"]);
expect(findMediaUnderstandingProviderIdsForPlugin("google")).toEqual(["google"]);
expect(findMediaUnderstandingProviderIdsForPlugin("minimax")).toEqual([
"minimax",
"minimax-portal",
]);
expect(findMediaUnderstandingProviderIdsForPlugin("mistral")).toEqual(["mistral"]);
expect(findMediaUnderstandingProviderIdsForPlugin("moonshot")).toEqual(["moonshot"]);
expect(findMediaUnderstandingProviderIdsForPlugin("openai")).toEqual(["openai"]);
expect(findMediaUnderstandingProviderIdsForPlugin("zai")).toEqual(["zai"]);
});
it("keeps bundled provider and web search tool ownership explicit", () => {
expect(findRegistrationForPlugin("firecrawl")).toMatchObject({
providerIds: [],
speechProviderIds: [],
mediaUnderstandingProviderIds: [],
webSearchProviderIds: ["firecrawl"],
toolNames: ["firecrawl_search", "firecrawl_scrape"],
});
@@ -95,14 +122,17 @@ describe("plugin contract registry", () => {
expect(findRegistrationForPlugin("openai")).toMatchObject({
providerIds: ["openai", "openai-codex"],
speechProviderIds: ["openai"],
mediaUnderstandingProviderIds: ["openai"],
});
expect(findRegistrationForPlugin("elevenlabs")).toMatchObject({
providerIds: [],
speechProviderIds: ["elevenlabs"],
mediaUnderstandingProviderIds: [],
});
expect(findRegistrationForPlugin("microsoft")).toMatchObject({
providerIds: [],
speechProviderIds: ["microsoft"],
mediaUnderstandingProviderIds: [],
});
});

View File

@@ -35,7 +35,12 @@ import xaiPlugin from "../../../extensions/xai/index.js";
import xiaomiPlugin from "../../../extensions/xiaomi/index.js";
import zaiPlugin from "../../../extensions/zai/index.js";
import { createCapturedPluginRegistration } from "../../test-utils/plugin-registration.js";
import type { ProviderPlugin, SpeechProviderPlugin, WebSearchProviderPlugin } from "../types.js";
import type {
MediaUnderstandingProviderPlugin,
ProviderPlugin,
SpeechProviderPlugin,
WebSearchProviderPlugin,
} from "../types.js";
type RegistrablePlugin = {
id: string;
@@ -58,10 +63,16 @@ type SpeechProviderContractEntry = {
provider: SpeechProviderPlugin;
};
type MediaUnderstandingProviderContractEntry = {
pluginId: string;
provider: MediaUnderstandingProviderPlugin;
};
type PluginRegistrationContractEntry = {
pluginId: string;
providerIds: string[];
speechProviderIds: string[];
mediaUnderstandingProviderIds: string[];
webSearchProviderIds: string[];
toolNames: string[];
};
@@ -111,6 +122,16 @@ const bundledWebSearchPlugins: Array<RegistrablePlugin & { credentialValue: unkn
const bundledSpeechPlugins: RegistrablePlugin[] = [elevenLabsPlugin, microsoftPlugin, openAIPlugin];
const bundledMediaUnderstandingPlugins: RegistrablePlugin[] = [
anthropicPlugin,
googlePlugin,
minimaxPlugin,
mistralPlugin,
moonshotPlugin,
openAIPlugin,
zaiPlugin,
];
function captureRegistrations(plugin: RegistrablePlugin) {
const captured = createCapturedPluginRegistration();
plugin.register(captured.api);
@@ -146,11 +167,23 @@ export const speechProviderContractRegistry: SpeechProviderContractEntry[] =
}));
});
export const mediaUnderstandingProviderContractRegistry: MediaUnderstandingProviderContractEntry[] =
bundledMediaUnderstandingPlugins.flatMap((plugin) => {
const captured = captureRegistrations(plugin);
return captured.mediaUnderstandingProviders.map((provider) => ({
pluginId: plugin.id,
provider,
}));
});
const bundledPluginRegistrationList = [
...new Map(
[...bundledProviderPlugins, ...bundledSpeechPlugins, ...bundledWebSearchPlugins].map(
(plugin) => [plugin.id, plugin],
),
[
...bundledProviderPlugins,
...bundledSpeechPlugins,
...bundledMediaUnderstandingPlugins,
...bundledWebSearchPlugins,
].map((plugin) => [plugin.id, plugin]),
).values(),
];
@@ -161,6 +194,9 @@ export const pluginRegistrationContractRegistry: PluginRegistrationContractEntry
pluginId: plugin.id,
providerIds: captured.providers.map((provider) => provider.id),
speechProviderIds: captured.speechProviders.map((provider) => provider.id),
mediaUnderstandingProviderIds: captured.mediaUnderstandingProviders.map(
(provider) => provider.id,
),
webSearchProviderIds: captured.webSearchProviders.map((provider) => provider.id),
toolNames: captured.tools.map((tool) => tool.name),
};

View File

@@ -17,6 +17,9 @@ export function createMockPluginRegistry(
hookNames: [],
channelIds: [],
providerIds: [],
speechProviderIds: [],
mediaUnderstandingProviderIds: [],
webSearchProviderIds: [],
gatewayMethods: [],
cliCommands: [],
services: [],
@@ -35,13 +38,18 @@ export function createMockPluginRegistry(
source: "test",
})),
tools: [],
channels: [],
channelSetups: [],
providers: [],
speechProviders: [],
mediaUnderstandingProviders: [],
webSearchProviders: [],
httpRoutes: [],
channelRegistrations: [],
gatewayHandlers: {},
cliRegistrars: [],
services: [],
providers: [],
commands: [],
diagnostics: [],
} as unknown as PluginRegistry;
}

View File

@@ -495,6 +495,7 @@ function createPluginRecord(params: {
channelIds: [],
providerIds: [],
speechProviderIds: [],
mediaUnderstandingProviderIds: [],
webSearchProviderIds: [],
gatewayMethods: [],
cliCommands: [],

View File

@@ -31,6 +31,7 @@ import type {
OpenClawPluginHttpRouteHandler,
OpenClawPluginHttpRouteParams,
OpenClawPluginHookOptions,
MediaUnderstandingProviderPlugin,
ProviderPlugin,
OpenClawPluginService,
OpenClawPluginToolContext,
@@ -119,6 +120,14 @@ export type PluginSpeechProviderRegistration = {
rootDir?: string;
};
export type PluginMediaUnderstandingProviderRegistration = {
pluginId: string;
pluginName?: string;
provider: MediaUnderstandingProviderPlugin;
source: string;
rootDir?: string;
};
export type PluginHookRegistration = {
pluginId: string;
entry: HookEntry;
@@ -164,6 +173,7 @@ export type PluginRecord = {
channelIds: string[];
providerIds: string[];
speechProviderIds: string[];
mediaUnderstandingProviderIds: string[];
webSearchProviderIds: string[];
gatewayMethods: string[];
cliCommands: string[];
@@ -185,6 +195,7 @@ export type PluginRegistry = {
channelSetups: PluginChannelSetupRegistration[];
providers: PluginProviderRegistration[];
speechProviders: PluginSpeechProviderRegistration[];
mediaUnderstandingProviders: PluginMediaUnderstandingProviderRegistration[];
webSearchProviders: PluginWebSearchProviderRegistration[];
gatewayHandlers: GatewayRequestHandlers;
httpRoutes: PluginHttpRouteRegistration[];
@@ -231,6 +242,7 @@ export function createEmptyPluginRegistry(): PluginRegistry {
channelSetups: [],
providers: [],
speechProviders: [],
mediaUnderstandingProviders: [],
webSearchProviders: [],
gatewayHandlers: {},
httpRoutes: [],
@@ -593,6 +605,40 @@ export function createPluginRegistry(registryParams: PluginRegistryParams) {
});
};
const registerMediaUnderstandingProvider = (
record: PluginRecord,
provider: MediaUnderstandingProviderPlugin,
) => {
const id = provider.id.trim();
if (!id) {
pushDiagnostic({
level: "error",
pluginId: record.id,
source: record.source,
message: "media provider registration missing id",
});
return;
}
const existing = registry.mediaUnderstandingProviders.find((entry) => entry.provider.id === id);
if (existing) {
pushDiagnostic({
level: "error",
pluginId: record.id,
source: record.source,
message: `media provider already registered: ${id} (${existing.pluginId})`,
});
return;
}
record.mediaUnderstandingProviderIds.push(id);
registry.mediaUnderstandingProviders.push({
pluginId: record.id,
pluginName: record.name,
provider,
source: record.source,
rootDir: record.rootDir,
});
};
const registerWebSearchProvider = (record: PluginRecord, provider: WebSearchProviderPlugin) => {
const id = provider.id.trim();
if (!id) {
@@ -836,6 +882,10 @@ export function createPluginRegistry(registryParams: PluginRegistryParams) {
registrationMode === "full"
? (provider) => registerSpeechProvider(record, provider)
: () => {},
registerMediaUnderstandingProvider:
registrationMode === "full"
? (provider) => registerMediaUnderstandingProvider(record, provider)
: () => {},
registerWebSearchProvider:
registrationMode === "full"
? (provider) => registerWebSearchProvider(record, provider)
@@ -910,6 +960,7 @@ export function createPluginRegistry(registryParams: PluginRegistryParams) {
registerChannel,
registerProvider,
registerSpeechProvider,
registerMediaUnderstandingProvider,
registerWebSearchProvider,
registerGatewayMethod,
registerCli,

View File

@@ -25,6 +25,7 @@ import type { GatewayRequestHandler } from "../gateway/server-methods/types.js";
import type { InternalHookHandler } from "../hooks/internal-hooks.js";
import type { HookEntry } from "../hooks/types.js";
import type { ProviderUsageSnapshot } from "../infra/provider-usage.types.js";
import type { MediaUnderstandingProvider } from "../media-understanding/types.js";
import type { RuntimeEnv } from "../runtime.js";
import type { RuntimeWebSearchMetadata } from "../secrets/runtime-web-tools.types.js";
import type {
@@ -881,6 +882,8 @@ export type PluginSpeechProviderEntry = SpeechProviderPlugin & {
pluginId: string;
};
export type MediaUnderstandingProviderPlugin = MediaUnderstandingProvider;
export type OpenClawPluginGatewayMethod = {
method: string;
handler: GatewayRequestHandler;
@@ -1240,6 +1243,7 @@ export type OpenClawPluginApi = {
registerService: (service: OpenClawPluginService) => void;
registerProvider: (provider: ProviderPlugin) => void;
registerSpeechProvider: (provider: SpeechProviderPlugin) => void;
registerMediaUnderstandingProvider: (provider: MediaUnderstandingProviderPlugin) => void;
registerWebSearchProvider: (provider: WebSearchProviderPlugin) => void;
registerInteractiveHandler: (registration: PluginInteractiveHandlerRegistration) => void;
/**

View File

@@ -27,6 +27,7 @@ export const createTestRegistry = (channels: TestChannelRegistration[] = []): Pl
})),
providers: [],
speechProviders: [],
mediaUnderstandingProviders: [],
webSearchProviders: [],
gatewayHandlers: {},
httpRoutes: [],

View File

@@ -1,5 +1,6 @@
import type {
AnyAgentTool,
MediaUnderstandingProviderPlugin,
OpenClawPluginApi,
ProviderPlugin,
SpeechProviderPlugin,
@@ -10,6 +11,7 @@ export type CapturedPluginRegistration = {
api: OpenClawPluginApi;
providers: ProviderPlugin[];
speechProviders: SpeechProviderPlugin[];
mediaUnderstandingProviders: MediaUnderstandingProviderPlugin[];
webSearchProviders: WebSearchProviderPlugin[];
tools: AnyAgentTool[];
};
@@ -17,12 +19,14 @@ export type CapturedPluginRegistration = {
export function createCapturedPluginRegistration(): CapturedPluginRegistration {
const providers: ProviderPlugin[] = [];
const speechProviders: SpeechProviderPlugin[] = [];
const mediaUnderstandingProviders: MediaUnderstandingProviderPlugin[] = [];
const webSearchProviders: WebSearchProviderPlugin[] = [];
const tools: AnyAgentTool[] = [];
return {
providers,
speechProviders,
mediaUnderstandingProviders,
webSearchProviders,
tools,
api: {
@@ -32,6 +36,9 @@ export function createCapturedPluginRegistration(): CapturedPluginRegistration {
registerSpeechProvider(provider: SpeechProviderPlugin) {
speechProviders.push(provider);
},
registerMediaUnderstandingProvider(provider: MediaUnderstandingProviderPlugin) {
mediaUnderstandingProviders.push(provider);
},
registerWebSearchProvider(provider: WebSearchProviderPlugin) {
webSearchProviders.push(provider);
},