mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-06 14:51:08 +00:00
feat(qwen): add qwen provider and video generation
This commit is contained in:
@@ -32,6 +32,7 @@ export type BuildPluginApiParams = {
|
||||
| "registerRealtimeVoiceProvider"
|
||||
| "registerMediaUnderstandingProvider"
|
||||
| "registerImageGenerationProvider"
|
||||
| "registerVideoGenerationProvider"
|
||||
| "registerWebFetchProvider"
|
||||
| "registerWebSearchProvider"
|
||||
| "registerInteractiveHandler"
|
||||
@@ -65,6 +66,8 @@ const noopRegisterMediaUnderstandingProvider: OpenClawPluginApi["registerMediaUn
|
||||
() => {};
|
||||
const noopRegisterImageGenerationProvider: OpenClawPluginApi["registerImageGenerationProvider"] =
|
||||
() => {};
|
||||
const noopRegisterVideoGenerationProvider: OpenClawPluginApi["registerVideoGenerationProvider"] =
|
||||
() => {};
|
||||
const noopRegisterWebFetchProvider: OpenClawPluginApi["registerWebFetchProvider"] = () => {};
|
||||
const noopRegisterWebSearchProvider: OpenClawPluginApi["registerWebSearchProvider"] = () => {};
|
||||
const noopRegisterInteractiveHandler: OpenClawPluginApi["registerInteractiveHandler"] = () => {};
|
||||
@@ -111,6 +114,8 @@ export function buildPluginApi(params: BuildPluginApiParams): OpenClawPluginApi
|
||||
handlers.registerMediaUnderstandingProvider ?? noopRegisterMediaUnderstandingProvider,
|
||||
registerImageGenerationProvider:
|
||||
handlers.registerImageGenerationProvider ?? noopRegisterImageGenerationProvider,
|
||||
registerVideoGenerationProvider:
|
||||
handlers.registerVideoGenerationProvider ?? noopRegisterVideoGenerationProvider,
|
||||
registerWebFetchProvider: handlers.registerWebFetchProvider ?? noopRegisterWebFetchProvider,
|
||||
registerWebSearchProvider: handlers.registerWebSearchProvider ?? noopRegisterWebSearchProvider,
|
||||
registerInteractiveHandler:
|
||||
|
||||
@@ -9,6 +9,7 @@ export type BundledPluginContractSnapshot = {
|
||||
realtimeVoiceProviderIds: string[];
|
||||
mediaUnderstandingProviderIds: string[];
|
||||
imageGenerationProviderIds: string[];
|
||||
videoGenerationProviderIds: string[];
|
||||
webFetchProviderIds: string[];
|
||||
webSearchProviderIds: string[];
|
||||
toolNames: string[];
|
||||
@@ -45,6 +46,7 @@ export const BUNDLED_PLUGIN_CONTRACT_SNAPSHOTS: readonly BundledPluginContractSn
|
||||
realtimeVoiceProviderIds: uniqueStrings(manifest.contracts?.realtimeVoiceProviders),
|
||||
mediaUnderstandingProviderIds: uniqueStrings(manifest.contracts?.mediaUnderstandingProviders),
|
||||
imageGenerationProviderIds: uniqueStrings(manifest.contracts?.imageGenerationProviders),
|
||||
videoGenerationProviderIds: uniqueStrings(manifest.contracts?.videoGenerationProviders),
|
||||
webFetchProviderIds: uniqueStrings(manifest.contracts?.webFetchProviders),
|
||||
webSearchProviderIds: uniqueStrings(manifest.contracts?.webSearchProviders),
|
||||
toolNames: uniqueStrings(manifest.contracts?.tools),
|
||||
@@ -58,6 +60,7 @@ export const BUNDLED_PLUGIN_CONTRACT_SNAPSHOTS: readonly BundledPluginContractSn
|
||||
entry.realtimeVoiceProviderIds.length > 0 ||
|
||||
entry.mediaUnderstandingProviderIds.length > 0 ||
|
||||
entry.imageGenerationProviderIds.length > 0 ||
|
||||
entry.videoGenerationProviderIds.length > 0 ||
|
||||
entry.webFetchProviderIds.length > 0 ||
|
||||
entry.webSearchProviderIds.length > 0 ||
|
||||
entry.toolNames.length > 0,
|
||||
@@ -92,6 +95,10 @@ export const BUNDLED_IMAGE_GENERATION_PLUGIN_IDS = collectPluginIds(
|
||||
(entry) => entry.imageGenerationProviderIds,
|
||||
);
|
||||
|
||||
export const BUNDLED_VIDEO_GENERATION_PLUGIN_IDS = collectPluginIds(
|
||||
(entry) => entry.videoGenerationProviderIds,
|
||||
);
|
||||
|
||||
export const BUNDLED_WEB_FETCH_PLUGIN_IDS = collectPluginIds((entry) => entry.webFetchProviderIds);
|
||||
|
||||
export const BUNDLED_RUNTIME_CONTRACT_PLUGIN_IDS = [
|
||||
@@ -104,6 +111,7 @@ export const BUNDLED_RUNTIME_CONTRACT_PLUGIN_IDS = [
|
||||
entry.realtimeVoiceProviderIds.length > 0 ||
|
||||
entry.mediaUnderstandingProviderIds.length > 0 ||
|
||||
entry.imageGenerationProviderIds.length > 0 ||
|
||||
entry.videoGenerationProviderIds.length > 0 ||
|
||||
entry.webFetchProviderIds.length > 0 ||
|
||||
entry.webSearchProviderIds.length > 0,
|
||||
).map((entry) => entry.pluginId),
|
||||
|
||||
@@ -126,6 +126,7 @@ function createCapabilityPluginRecord(params: {
|
||||
realtimeVoiceProviderIds: [],
|
||||
mediaUnderstandingProviderIds: [],
|
||||
imageGenerationProviderIds: [],
|
||||
videoGenerationProviderIds: [],
|
||||
webFetchProviderIds: [],
|
||||
webSearchProviderIds: [],
|
||||
gatewayMethods: [],
|
||||
@@ -286,6 +287,9 @@ export function loadBundledCapabilityRuntimeRegistry(params: {
|
||||
record.imageGenerationProviderIds.push(
|
||||
...captured.imageGenerationProviders.map((entry) => entry.id),
|
||||
);
|
||||
record.videoGenerationProviderIds.push(
|
||||
...captured.videoGenerationProviders.map((entry) => entry.id),
|
||||
);
|
||||
record.webFetchProviderIds.push(...captured.webFetchProviders.map((entry) => entry.id));
|
||||
record.webSearchProviderIds.push(...captured.webSearchProviders.map((entry) => entry.id));
|
||||
record.toolNames.push(...captured.tools.map((entry) => entry.name));
|
||||
@@ -353,6 +357,15 @@ export function loadBundledCapabilityRuntimeRegistry(params: {
|
||||
rootDir: record.rootDir,
|
||||
})),
|
||||
);
|
||||
registry.videoGenerationProviders.push(
|
||||
...captured.videoGenerationProviders.map((provider) => ({
|
||||
pluginId: record.id,
|
||||
pluginName: record.name,
|
||||
provider,
|
||||
source: record.source,
|
||||
rootDir: record.rootDir,
|
||||
})),
|
||||
);
|
||||
registry.webFetchProviders.push(
|
||||
...captured.webFetchProviders.map((provider) => ({
|
||||
pluginId: record.id,
|
||||
|
||||
@@ -23,7 +23,8 @@ export const BUNDLED_PROVIDER_AUTH_ENV_VAR_CANDIDATES = {
|
||||
minimax: ["MINIMAX_API_KEY"],
|
||||
"minimax-portal": ["MINIMAX_OAUTH_TOKEN", "MINIMAX_API_KEY"],
|
||||
mistral: ["MISTRAL_API_KEY"],
|
||||
modelstudio: ["MODELSTUDIO_API_KEY"],
|
||||
qwen: ["QWEN_API_KEY", "MODELSTUDIO_API_KEY", "DASHSCOPE_API_KEY"],
|
||||
modelstudio: ["QWEN_API_KEY", "MODELSTUDIO_API_KEY", "DASHSCOPE_API_KEY"],
|
||||
moonshot: ["MOONSHOT_API_KEY"],
|
||||
nvidia: ["NVIDIA_API_KEY"],
|
||||
ollama: ["OLLAMA_API_KEY"],
|
||||
|
||||
@@ -265,4 +265,44 @@ describe("resolvePluginCapabilityProviders", () => {
|
||||
config: expect.anything(),
|
||||
});
|
||||
});
|
||||
|
||||
it("loads bundled capability providers even without an explicit cfg", () => {
|
||||
const compatConfig = {
|
||||
plugins: {
|
||||
enabled: true,
|
||||
allow: ["google"],
|
||||
entries: { google: { enabled: true } },
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
const loaded = createEmptyPluginRegistry();
|
||||
loaded.mediaUnderstandingProviders.push({
|
||||
pluginId: "google",
|
||||
pluginName: "google",
|
||||
source: "test",
|
||||
provider: {
|
||||
id: "google",
|
||||
capabilities: ["image", "audio", "video"],
|
||||
describeImage: vi.fn(),
|
||||
transcribeAudio: vi.fn(),
|
||||
describeVideo: vi.fn(),
|
||||
autoPriority: { image: 30, audio: 40, video: 10 },
|
||||
nativeDocumentInputs: ["pdf"],
|
||||
},
|
||||
} as never);
|
||||
setBundledCapabilityFixture("mediaUnderstandingProviders");
|
||||
mocks.withBundledPluginEnablementCompat.mockReturnValue(compatConfig);
|
||||
mocks.withBundledPluginVitestCompat.mockReturnValue(compatConfig);
|
||||
mocks.resolveRuntimePluginRegistry.mockImplementation((params?: unknown) =>
|
||||
params === undefined ? undefined : loaded,
|
||||
);
|
||||
|
||||
const providers = resolvePluginCapabilityProviders({ key: "mediaUnderstandingProviders" });
|
||||
|
||||
expectResolvedCapabilityProviderIds(providers, ["google"]);
|
||||
expect(mocks.loadPluginManifestRegistry).toHaveBeenCalledWith({
|
||||
config: undefined,
|
||||
env: process.env,
|
||||
});
|
||||
expect(mocks.resolveRuntimePluginRegistry).toHaveBeenCalledWith({ config: compatConfig });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,14 +12,16 @@ type CapabilityProviderRegistryKey =
|
||||
| "realtimeTranscriptionProviders"
|
||||
| "realtimeVoiceProviders"
|
||||
| "mediaUnderstandingProviders"
|
||||
| "imageGenerationProviders";
|
||||
| "imageGenerationProviders"
|
||||
| "videoGenerationProviders";
|
||||
|
||||
type CapabilityContractKey =
|
||||
| "speechProviders"
|
||||
| "realtimeTranscriptionProviders"
|
||||
| "realtimeVoiceProviders"
|
||||
| "mediaUnderstandingProviders"
|
||||
| "imageGenerationProviders";
|
||||
| "imageGenerationProviders"
|
||||
| "videoGenerationProviders";
|
||||
|
||||
type CapabilityProviderForKey<K extends CapabilityProviderRegistryKey> =
|
||||
PluginRegistry[K][number] extends { provider: infer T } ? T : never;
|
||||
@@ -30,6 +32,7 @@ const CAPABILITY_CONTRACT_KEY: Record<CapabilityProviderRegistryKey, CapabilityC
|
||||
realtimeVoiceProviders: "realtimeVoiceProviders",
|
||||
mediaUnderstandingProviders: "mediaUnderstandingProviders",
|
||||
imageGenerationProviders: "imageGenerationProviders",
|
||||
videoGenerationProviders: "videoGenerationProviders",
|
||||
};
|
||||
|
||||
function resolveBundledCapabilityCompatPluginIds(params: {
|
||||
@@ -73,12 +76,8 @@ export function resolvePluginCapabilityProviders<K extends CapabilityProviderReg
|
||||
if (activeProviders.length > 0) {
|
||||
return activeProviders.map((entry) => entry.provider) as CapabilityProviderForKey<K>[];
|
||||
}
|
||||
const loadOptions =
|
||||
params.cfg === undefined
|
||||
? undefined
|
||||
: {
|
||||
config: resolveCapabilityProviderConfig({ key: params.key, cfg: params.cfg }),
|
||||
};
|
||||
const compatConfig = resolveCapabilityProviderConfig({ key: params.key, cfg: params.cfg });
|
||||
const loadOptions = compatConfig === undefined ? undefined : { config: compatConfig };
|
||||
const registry = resolveRuntimePluginRegistry(loadOptions);
|
||||
return (registry?.[params.key] ?? []).map(
|
||||
(entry) => entry.provider,
|
||||
|
||||
@@ -13,6 +13,7 @@ import type {
|
||||
RealtimeTranscriptionProviderPlugin,
|
||||
RealtimeVoiceProviderPlugin,
|
||||
SpeechProviderPlugin,
|
||||
VideoGenerationProviderPlugin,
|
||||
WebFetchProviderPlugin,
|
||||
WebSearchProviderPlugin,
|
||||
} from "./types.js";
|
||||
@@ -33,6 +34,7 @@ export type CapturedPluginRegistration = {
|
||||
realtimeVoiceProviders: RealtimeVoiceProviderPlugin[];
|
||||
mediaUnderstandingProviders: MediaUnderstandingProviderPlugin[];
|
||||
imageGenerationProviders: ImageGenerationProviderPlugin[];
|
||||
videoGenerationProviders: VideoGenerationProviderPlugin[];
|
||||
webFetchProviders: WebFetchProviderPlugin[];
|
||||
webSearchProviders: WebSearchProviderPlugin[];
|
||||
tools: AnyAgentTool[];
|
||||
@@ -50,6 +52,7 @@ export function createCapturedPluginRegistration(params?: {
|
||||
const realtimeVoiceProviders: RealtimeVoiceProviderPlugin[] = [];
|
||||
const mediaUnderstandingProviders: MediaUnderstandingProviderPlugin[] = [];
|
||||
const imageGenerationProviders: ImageGenerationProviderPlugin[] = [];
|
||||
const videoGenerationProviders: VideoGenerationProviderPlugin[] = [];
|
||||
const webFetchProviders: WebFetchProviderPlugin[] = [];
|
||||
const webSearchProviders: WebSearchProviderPlugin[] = [];
|
||||
const tools: AnyAgentTool[] = [];
|
||||
@@ -69,6 +72,7 @@ export function createCapturedPluginRegistration(params?: {
|
||||
realtimeVoiceProviders,
|
||||
mediaUnderstandingProviders,
|
||||
imageGenerationProviders,
|
||||
videoGenerationProviders,
|
||||
webFetchProviders,
|
||||
webSearchProviders,
|
||||
tools,
|
||||
@@ -126,6 +130,9 @@ export function createCapturedPluginRegistration(params?: {
|
||||
registerImageGenerationProvider(provider: ImageGenerationProviderPlugin) {
|
||||
imageGenerationProviders.push(provider);
|
||||
},
|
||||
registerVideoGenerationProvider(provider: VideoGenerationProviderPlugin) {
|
||||
videoGenerationProviders.push(provider);
|
||||
},
|
||||
registerWebFetchProvider(provider: WebFetchProviderPlugin) {
|
||||
webFetchProviders.push(provider);
|
||||
},
|
||||
|
||||
@@ -15,6 +15,7 @@ function hasRuntimeContractSurface(plugin: PluginManifestRecord): boolean {
|
||||
plugin.contracts?.speechProviders?.length ||
|
||||
plugin.contracts?.mediaUnderstandingProviders?.length ||
|
||||
plugin.contracts?.imageGenerationProviders?.length ||
|
||||
plugin.contracts?.videoGenerationProviders?.length ||
|
||||
plugin.contracts?.webFetchProviders?.length ||
|
||||
plugin.contracts?.webSearchProviders?.length ||
|
||||
hasKind(plugin.kind, "memory"),
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
BUNDLED_SPEECH_PLUGIN_IDS,
|
||||
BUNDLED_WEB_FETCH_PLUGIN_IDS,
|
||||
BUNDLED_WEB_SEARCH_PLUGIN_IDS,
|
||||
BUNDLED_VIDEO_GENERATION_PLUGIN_IDS,
|
||||
} from "../bundled-capability-metadata.js";
|
||||
import { loadBundledCapabilityRuntimeRegistry } from "../bundled-capability-runtime.js";
|
||||
import type {
|
||||
@@ -17,6 +18,7 @@ import type {
|
||||
RealtimeTranscriptionProviderPlugin,
|
||||
RealtimeVoiceProviderPlugin,
|
||||
SpeechProviderPlugin,
|
||||
VideoGenerationProviderPlugin,
|
||||
WebFetchProviderPlugin,
|
||||
WebSearchProviderPlugin,
|
||||
} from "../types.js";
|
||||
@@ -26,6 +28,7 @@ import {
|
||||
loadVitestRealtimeTranscriptionProviderContractRegistry,
|
||||
loadVitestRealtimeVoiceProviderContractRegistry,
|
||||
loadVitestSpeechProviderContractRegistry,
|
||||
loadVitestVideoGenerationProviderContractRegistry,
|
||||
} from "./speech-vitest-registry.js";
|
||||
|
||||
type BundledCapabilityRuntimeRegistry = ReturnType<typeof loadBundledCapabilityRuntimeRegistry>;
|
||||
@@ -50,6 +53,7 @@ type RealtimeVoiceProviderContractEntry = CapabilityContractEntry<RealtimeVoiceP
|
||||
type MediaUnderstandingProviderContractEntry =
|
||||
CapabilityContractEntry<MediaUnderstandingProviderPlugin>;
|
||||
type ImageGenerationProviderContractEntry = CapabilityContractEntry<ImageGenerationProviderPlugin>;
|
||||
type VideoGenerationProviderContractEntry = CapabilityContractEntry<VideoGenerationProviderPlugin>;
|
||||
|
||||
type PluginRegistrationContractEntry = {
|
||||
pluginId: string;
|
||||
@@ -60,6 +64,7 @@ type PluginRegistrationContractEntry = {
|
||||
realtimeVoiceProviderIds: string[];
|
||||
mediaUnderstandingProviderIds: string[];
|
||||
imageGenerationProviderIds: string[];
|
||||
videoGenerationProviderIds: string[];
|
||||
webFetchProviderIds: string[];
|
||||
webSearchProviderIds: string[];
|
||||
toolNames: string[];
|
||||
@@ -114,6 +119,8 @@ let mediaUnderstandingProviderContractRegistryCache:
|
||||
| null = null;
|
||||
let imageGenerationProviderContractRegistryCache: ImageGenerationProviderContractEntry[] | null =
|
||||
null;
|
||||
let videoGenerationProviderContractRegistryCache: VideoGenerationProviderContractEntry[] | null =
|
||||
null;
|
||||
const providerContractPluginIdsByProviderId = createProviderContractPluginIdsByProviderId();
|
||||
|
||||
export let providerContractLoadError: Error | undefined;
|
||||
@@ -462,6 +469,21 @@ function loadImageGenerationProviderContractRegistry(): ImageGenerationProviderC
|
||||
return imageGenerationProviderContractRegistryCache;
|
||||
}
|
||||
|
||||
function loadVideoGenerationProviderContractRegistry(): VideoGenerationProviderContractEntry[] {
|
||||
if (!videoGenerationProviderContractRegistryCache) {
|
||||
videoGenerationProviderContractRegistryCache = process.env.VITEST
|
||||
? loadVitestVideoGenerationProviderContractRegistry()
|
||||
: loadBundledCapabilityRuntimeRegistry({
|
||||
pluginIds: BUNDLED_VIDEO_GENERATION_PLUGIN_IDS,
|
||||
pluginSdkResolution: "dist",
|
||||
}).videoGenerationProviders.map((entry) => ({
|
||||
pluginId: entry.pluginId,
|
||||
provider: entry.provider,
|
||||
}));
|
||||
}
|
||||
return videoGenerationProviderContractRegistryCache;
|
||||
}
|
||||
|
||||
function createLazyArrayView<T>(load: () => T[]): T[] {
|
||||
return new Proxy([] as T[], {
|
||||
get(_target, prop) {
|
||||
@@ -576,6 +598,9 @@ export const mediaUnderstandingProviderContractRegistry: MediaUnderstandingProvi
|
||||
export const imageGenerationProviderContractRegistry: ImageGenerationProviderContractEntry[] =
|
||||
createLazyArrayView(loadImageGenerationProviderContractRegistry);
|
||||
|
||||
export const videoGenerationProviderContractRegistry: VideoGenerationProviderContractEntry[] =
|
||||
createLazyArrayView(loadVideoGenerationProviderContractRegistry);
|
||||
|
||||
function loadPluginRegistrationContractRegistry(): PluginRegistrationContractEntry[] {
|
||||
return BUNDLED_PLUGIN_CONTRACT_SNAPSHOTS.map((entry) => ({
|
||||
pluginId: entry.pluginId,
|
||||
@@ -586,6 +611,7 @@ function loadPluginRegistrationContractRegistry(): PluginRegistrationContractEnt
|
||||
realtimeVoiceProviderIds: uniqueStrings(entry.realtimeVoiceProviderIds),
|
||||
mediaUnderstandingProviderIds: uniqueStrings(entry.mediaUnderstandingProviderIds),
|
||||
imageGenerationProviderIds: uniqueStrings(entry.imageGenerationProviderIds),
|
||||
videoGenerationProviderIds: uniqueStrings(entry.videoGenerationProviderIds),
|
||||
webFetchProviderIds: uniqueStrings(entry.webFetchProviderIds),
|
||||
webSearchProviderIds: uniqueStrings(entry.webSearchProviderIds),
|
||||
toolNames: uniqueStrings(entry.toolNames),
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
BUNDLED_REALTIME_TRANSCRIPTION_PLUGIN_IDS,
|
||||
BUNDLED_REALTIME_VOICE_PLUGIN_IDS,
|
||||
BUNDLED_SPEECH_PLUGIN_IDS,
|
||||
BUNDLED_VIDEO_GENERATION_PLUGIN_IDS,
|
||||
} from "../bundled-capability-metadata.js";
|
||||
import { loadBundledCapabilityRuntimeRegistry } from "../bundled-capability-runtime.js";
|
||||
import { loadPluginManifestRegistry } from "../manifest-registry.js";
|
||||
@@ -18,6 +19,7 @@ import type {
|
||||
RealtimeTranscriptionProviderPlugin,
|
||||
RealtimeVoiceProviderPlugin,
|
||||
SpeechProviderPlugin,
|
||||
VideoGenerationProviderPlugin,
|
||||
} from "../types.js";
|
||||
|
||||
export type SpeechProviderContractEntry = {
|
||||
@@ -45,6 +47,11 @@ export type ImageGenerationProviderContractEntry = {
|
||||
provider: ImageGenerationProviderPlugin;
|
||||
};
|
||||
|
||||
export type VideoGenerationProviderContractEntry = {
|
||||
pluginId: string;
|
||||
provider: VideoGenerationProviderPlugin;
|
||||
};
|
||||
|
||||
function buildVitestCapabilityAliasMap(modulePath: string): Record<string, string> {
|
||||
const { ["openclaw/plugin-sdk"]: _ignoredRootAlias, ...scopedAliasMap } =
|
||||
buildPluginLoaderAliasMap(modulePath, process.argv[1], import.meta.url, "dist");
|
||||
@@ -338,3 +345,48 @@ export function loadVitestImageGenerationProviderContractRegistry(): ImageGenera
|
||||
);
|
||||
return registrations;
|
||||
}
|
||||
|
||||
export function loadVitestVideoGenerationProviderContractRegistry(): VideoGenerationProviderContractEntry[] {
|
||||
const registrations: VideoGenerationProviderContractEntry[] = [];
|
||||
const { manifests, unresolvedPluginIds } = resolveTestApiModuleRecords(
|
||||
BUNDLED_VIDEO_GENERATION_PLUGIN_IDS,
|
||||
);
|
||||
|
||||
for (const plugin of manifests) {
|
||||
if (!plugin.rootDir) {
|
||||
continue;
|
||||
}
|
||||
const testApiPath = path.join(plugin.rootDir, "test-api.ts");
|
||||
if (!fs.existsSync(testApiPath)) {
|
||||
continue;
|
||||
}
|
||||
const builder = resolveNamedBuilder<VideoGenerationProviderPlugin>(
|
||||
createVitestCapabilityLoader(testApiPath)(testApiPath),
|
||||
/^build.+VideoGenerationProvider$/u,
|
||||
);
|
||||
if (!builder) {
|
||||
continue;
|
||||
}
|
||||
registrations.push({
|
||||
pluginId: plugin.id,
|
||||
provider: builder(),
|
||||
});
|
||||
unresolvedPluginIds.delete(plugin.id);
|
||||
}
|
||||
|
||||
if (unresolvedPluginIds.size === 0) {
|
||||
return registrations;
|
||||
}
|
||||
|
||||
const runtimeRegistry = loadBundledCapabilityRuntimeRegistry({
|
||||
pluginIds: [...unresolvedPluginIds],
|
||||
pluginSdkResolution: "dist",
|
||||
});
|
||||
registrations.push(
|
||||
...runtimeRegistry.videoGenerationProviders.map((entry) => ({
|
||||
pluginId: entry.pluginId,
|
||||
provider: entry.provider,
|
||||
})),
|
||||
);
|
||||
return registrations;
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ export function createMockPluginRegistry(
|
||||
speechProviders: [],
|
||||
mediaUnderstandingProviders: [],
|
||||
imageGenerationProviders: [],
|
||||
videoGenerationProviders: [],
|
||||
webSearchProviders: [],
|
||||
httpRoutes: [],
|
||||
gatewayHandlers: {},
|
||||
|
||||
@@ -594,6 +594,7 @@ function createPluginRecord(params: {
|
||||
realtimeVoiceProviderIds: [],
|
||||
mediaUnderstandingProviderIds: [],
|
||||
imageGenerationProviderIds: [],
|
||||
videoGenerationProviderIds: [],
|
||||
webFetchProviderIds: [],
|
||||
webSearchProviderIds: [],
|
||||
gatewayMethods: [],
|
||||
|
||||
@@ -76,6 +76,7 @@ export type PluginManifestContracts = {
|
||||
realtimeVoiceProviders?: string[];
|
||||
mediaUnderstandingProviders?: string[];
|
||||
imageGenerationProviders?: string[];
|
||||
videoGenerationProviders?: string[];
|
||||
webFetchProviders?: string[];
|
||||
webSearchProviders?: string[];
|
||||
tools?: string[];
|
||||
@@ -155,6 +156,7 @@ function normalizeManifestContracts(value: unknown): PluginManifestContracts | u
|
||||
const realtimeVoiceProviders = normalizeStringList(value.realtimeVoiceProviders);
|
||||
const mediaUnderstandingProviders = normalizeStringList(value.mediaUnderstandingProviders);
|
||||
const imageGenerationProviders = normalizeStringList(value.imageGenerationProviders);
|
||||
const videoGenerationProviders = normalizeStringList(value.videoGenerationProviders);
|
||||
const webFetchProviders = normalizeStringList(value.webFetchProviders);
|
||||
const webSearchProviders = normalizeStringList(value.webSearchProviders);
|
||||
const tools = normalizeStringList(value.tools);
|
||||
@@ -164,6 +166,7 @@ function normalizeManifestContracts(value: unknown): PluginManifestContracts | u
|
||||
...(realtimeVoiceProviders.length > 0 ? { realtimeVoiceProviders } : {}),
|
||||
...(mediaUnderstandingProviders.length > 0 ? { mediaUnderstandingProviders } : {}),
|
||||
...(imageGenerationProviders.length > 0 ? { imageGenerationProviders } : {}),
|
||||
...(videoGenerationProviders.length > 0 ? { videoGenerationProviders } : {}),
|
||||
...(webFetchProviders.length > 0 ? { webFetchProviders } : {}),
|
||||
...(webSearchProviders.length > 0 ? { webSearchProviders } : {}),
|
||||
...(tools.length > 0 ? { tools } : {}),
|
||||
|
||||
@@ -85,6 +85,7 @@ const {
|
||||
setTogetherApiKey,
|
||||
setHuggingfaceApiKey,
|
||||
setQianfanApiKey,
|
||||
setQwenApiKey,
|
||||
setModelStudioApiKey,
|
||||
setXaiApiKey,
|
||||
setMistralApiKey,
|
||||
@@ -110,7 +111,8 @@ const {
|
||||
setTogetherApiKey: { provider: "together" },
|
||||
setHuggingfaceApiKey: { provider: "huggingface" },
|
||||
setQianfanApiKey: { provider: "qianfan" },
|
||||
setModelStudioApiKey: { provider: "modelstudio" },
|
||||
setQwenApiKey: { provider: "qwen" },
|
||||
setModelStudioApiKey: { provider: "qwen" },
|
||||
setXaiApiKey: { provider: "xai" },
|
||||
setMistralApiKey: { provider: "mistral" },
|
||||
setKilocodeApiKey: { provider: "kilocode" },
|
||||
@@ -134,6 +136,7 @@ export {
|
||||
setTogetherApiKey,
|
||||
setHuggingfaceApiKey,
|
||||
setQianfanApiKey,
|
||||
setQwenApiKey,
|
||||
setModelStudioApiKey,
|
||||
setXaiApiKey,
|
||||
setMistralApiKey,
|
||||
|
||||
@@ -16,16 +16,19 @@ const applyPluginAutoEnableMock = vi.fn<ApplyPluginAutoEnable>();
|
||||
|
||||
let resolveOwningPluginIdsForProvider: typeof import("./providers.js").resolveOwningPluginIdsForProvider;
|
||||
let resolveOwningPluginIdsForModelRef: typeof import("./providers.js").resolveOwningPluginIdsForModelRef;
|
||||
let resolveEnabledProviderPluginIds: typeof import("./providers.js").resolveEnabledProviderPluginIds;
|
||||
let resolvePluginProviders: typeof import("./providers.runtime.js").resolvePluginProviders;
|
||||
|
||||
function createManifestProviderPlugin(params: {
|
||||
id: string;
|
||||
providerIds: string[];
|
||||
origin?: "bundled" | "workspace";
|
||||
enabledByDefault?: boolean;
|
||||
modelSupport?: { modelPrefixes?: string[]; modelPatterns?: string[] };
|
||||
}): PluginManifestRecord {
|
||||
return {
|
||||
id: params.id,
|
||||
enabledByDefault: params.enabledByDefault,
|
||||
channels: [],
|
||||
cliBackends: [],
|
||||
providers: params.providerIds,
|
||||
@@ -230,8 +233,11 @@ describe("resolvePluginProviders", () => {
|
||||
loadPluginManifestRegistry: (...args: Parameters<LoadPluginManifestRegistry>) =>
|
||||
loadPluginManifestRegistryMock(...args),
|
||||
}));
|
||||
({ resolveOwningPluginIdsForProvider, resolveOwningPluginIdsForModelRef } =
|
||||
await import("./providers.js"));
|
||||
({
|
||||
resolveOwningPluginIdsForProvider,
|
||||
resolveOwningPluginIdsForModelRef,
|
||||
resolveEnabledProviderPluginIds,
|
||||
} = await import("./providers.js"));
|
||||
({ resolvePluginProviders } = await import("./providers.runtime.js"));
|
||||
});
|
||||
|
||||
@@ -255,10 +261,22 @@ describe("resolvePluginProviders", () => {
|
||||
}),
|
||||
);
|
||||
setManifestPlugins([
|
||||
createManifestProviderPlugin({ id: "google", providerIds: ["google"] }),
|
||||
createManifestProviderPlugin({
|
||||
id: "google",
|
||||
providerIds: ["google"],
|
||||
enabledByDefault: true,
|
||||
}),
|
||||
createManifestProviderPlugin({ id: "browser", providerIds: [] }),
|
||||
createManifestProviderPlugin({ id: "kilocode", providerIds: ["kilocode"] }),
|
||||
createManifestProviderPlugin({ id: "moonshot", providerIds: ["moonshot"] }),
|
||||
createManifestProviderPlugin({
|
||||
id: "kilocode",
|
||||
providerIds: ["kilocode"],
|
||||
enabledByDefault: true,
|
||||
}),
|
||||
createManifestProviderPlugin({
|
||||
id: "moonshot",
|
||||
providerIds: ["moonshot"],
|
||||
enabledByDefault: true,
|
||||
}),
|
||||
createManifestProviderPlugin({ id: "google-gemini-cli-auth", providerIds: [] }),
|
||||
createManifestProviderPlugin({
|
||||
id: "workspace-provider",
|
||||
@@ -292,6 +310,14 @@ describe("resolvePluginProviders", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps bundled provider plugins enabled when they default on outside Vitest compat", () => {
|
||||
expect(resolveEnabledProviderPluginIds({ config: {}, env: {} as NodeJS.ProcessEnv })).toEqual([
|
||||
"google",
|
||||
"kilocode",
|
||||
"moonshot",
|
||||
]);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{
|
||||
name: "can augment restrictive allowlists for bundled provider compatibility",
|
||||
|
||||
@@ -62,6 +62,7 @@ export function resolveEnabledProviderPluginIds(params: {
|
||||
origin: plugin.origin,
|
||||
config: normalizedConfig,
|
||||
rootConfig: params.config,
|
||||
enabledByDefault: plugin.enabledByDefault,
|
||||
}).activated,
|
||||
)
|
||||
.map((plugin) => plugin.id)
|
||||
|
||||
@@ -15,6 +15,7 @@ export function createEmptyPluginRegistry(): PluginRegistry {
|
||||
realtimeVoiceProviders: [],
|
||||
mediaUnderstandingProviders: [],
|
||||
imageGenerationProviders: [],
|
||||
videoGenerationProviders: [],
|
||||
webFetchProviders: [],
|
||||
webSearchProviders: [],
|
||||
gatewayHandlers: {},
|
||||
|
||||
@@ -69,6 +69,7 @@ import type {
|
||||
PluginHookHandlerMap,
|
||||
PluginHookRegistration as TypedPluginHookRegistration,
|
||||
SpeechProviderPlugin,
|
||||
VideoGenerationProviderPlugin,
|
||||
WebFetchProviderPlugin,
|
||||
WebSearchProviderPlugin,
|
||||
} from "./types.js";
|
||||
@@ -153,6 +154,8 @@ export type PluginMediaUnderstandingProviderRegistration =
|
||||
PluginOwnedProviderRegistration<MediaUnderstandingProviderPlugin>;
|
||||
export type PluginImageGenerationProviderRegistration =
|
||||
PluginOwnedProviderRegistration<ImageGenerationProviderPlugin>;
|
||||
export type PluginVideoGenerationProviderRegistration =
|
||||
PluginOwnedProviderRegistration<VideoGenerationProviderPlugin>;
|
||||
export type PluginWebFetchProviderRegistration =
|
||||
PluginOwnedProviderRegistration<WebFetchProviderPlugin>;
|
||||
export type PluginWebSearchProviderRegistration =
|
||||
@@ -224,6 +227,7 @@ export type PluginRecord = {
|
||||
realtimeVoiceProviderIds: string[];
|
||||
mediaUnderstandingProviderIds: string[];
|
||||
imageGenerationProviderIds: string[];
|
||||
videoGenerationProviderIds: string[];
|
||||
webFetchProviderIds: string[];
|
||||
webSearchProviderIds: string[];
|
||||
gatewayMethods: string[];
|
||||
@@ -252,6 +256,7 @@ export type PluginRegistry = {
|
||||
realtimeVoiceProviders: PluginRealtimeVoiceProviderRegistration[];
|
||||
mediaUnderstandingProviders: PluginMediaUnderstandingProviderRegistration[];
|
||||
imageGenerationProviders: PluginImageGenerationProviderRegistration[];
|
||||
videoGenerationProviders: PluginVideoGenerationProviderRegistration[];
|
||||
webFetchProviders: PluginWebFetchProviderRegistration[];
|
||||
webSearchProviders: PluginWebSearchProviderRegistration[];
|
||||
gatewayHandlers: GatewayRequestHandlers;
|
||||
@@ -772,6 +777,19 @@ export function createPluginRegistry(registryParams: PluginRegistryParams) {
|
||||
});
|
||||
};
|
||||
|
||||
const registerVideoGenerationProvider = (
|
||||
record: PluginRecord,
|
||||
provider: VideoGenerationProviderPlugin,
|
||||
) => {
|
||||
registerUniqueProviderLike({
|
||||
record,
|
||||
provider,
|
||||
kindLabel: "video-generation provider",
|
||||
registrations: registry.videoGenerationProviders,
|
||||
ownedIds: record.videoGenerationProviderIds,
|
||||
});
|
||||
};
|
||||
|
||||
const registerWebFetchProvider = (record: PluginRecord, provider: WebFetchProviderPlugin) => {
|
||||
registerUniqueProviderLike({
|
||||
record,
|
||||
@@ -1064,6 +1082,8 @@ export function createPluginRegistry(registryParams: PluginRegistryParams) {
|
||||
registerMediaUnderstandingProvider(record, provider),
|
||||
registerImageGenerationProvider: (provider) =>
|
||||
registerImageGenerationProvider(record, provider),
|
||||
registerVideoGenerationProvider: (provider) =>
|
||||
registerVideoGenerationProvider(record, provider),
|
||||
registerWebFetchProvider: (provider) => registerWebFetchProvider(record, provider),
|
||||
registerWebSearchProvider: (provider) => registerWebSearchProvider(record, provider),
|
||||
registerGatewayMethod: (method, handler, opts) =>
|
||||
@@ -1253,6 +1273,7 @@ export function createPluginRegistry(registryParams: PluginRegistryParams) {
|
||||
registerRealtimeVoiceProvider,
|
||||
registerMediaUnderstandingProvider,
|
||||
registerImageGenerationProvider,
|
||||
registerVideoGenerationProvider,
|
||||
registerWebSearchProvider,
|
||||
registerGatewayMethod,
|
||||
registerCli,
|
||||
|
||||
@@ -203,6 +203,7 @@ describe("setActivePluginRegistry", () => {
|
||||
realtimeVoiceProviderIds: [],
|
||||
mediaUnderstandingProviderIds: [],
|
||||
imageGenerationProviderIds: [],
|
||||
videoGenerationProviderIds: [],
|
||||
webFetchProviderIds: [],
|
||||
webSearchProviderIds: [],
|
||||
gatewayMethods: [],
|
||||
@@ -231,6 +232,7 @@ describe("setActivePluginRegistry", () => {
|
||||
realtimeVoiceProviderIds: [],
|
||||
mediaUnderstandingProviderIds: [],
|
||||
imageGenerationProviderIds: [],
|
||||
videoGenerationProviderIds: [],
|
||||
webFetchProviderIds: [],
|
||||
webSearchProviderIds: [],
|
||||
gatewayMethods: [],
|
||||
|
||||
@@ -58,6 +58,12 @@ type RuntimeImageGenerationModule = Pick<
|
||||
>;
|
||||
let cachedRuntimeImageGenerationModule: RuntimeImageGenerationModule | null = null;
|
||||
|
||||
type RuntimeVideoGenerationModule = Pick<
|
||||
typeof import("../../plugin-sdk/video-generation-runtime.js"),
|
||||
"generateVideo" | "listRuntimeVideoGenerationProviders"
|
||||
>;
|
||||
let cachedRuntimeVideoGenerationModule: RuntimeVideoGenerationModule | null = null;
|
||||
|
||||
function loadRuntimeImageGenerationModule(): RuntimeImageGenerationModule {
|
||||
cachedRuntimeImageGenerationModule ??=
|
||||
loadBundledPluginPublicSurfaceModuleSync<RuntimeImageGenerationModule>({
|
||||
@@ -75,6 +81,23 @@ function createRuntimeImageGeneration(): PluginRuntime["imageGeneration"] {
|
||||
};
|
||||
}
|
||||
|
||||
function loadRuntimeVideoGenerationModule(): RuntimeVideoGenerationModule {
|
||||
cachedRuntimeVideoGenerationModule ??=
|
||||
loadBundledPluginPublicSurfaceModuleSync<RuntimeVideoGenerationModule>({
|
||||
dirName: "video-generation-core",
|
||||
artifactBasename: "runtime-api.js",
|
||||
});
|
||||
return cachedRuntimeVideoGenerationModule;
|
||||
}
|
||||
|
||||
function createRuntimeVideoGeneration(): PluginRuntime["videoGeneration"] {
|
||||
return {
|
||||
generate: (params) => loadRuntimeVideoGenerationModule().generateVideo(params),
|
||||
listProviders: (params) =>
|
||||
loadRuntimeVideoGenerationModule().listRuntimeVideoGenerationProviders(params),
|
||||
};
|
||||
}
|
||||
|
||||
function createRuntimeModelAuth(): PluginRuntime["modelAuth"] {
|
||||
const getApiKeyForModel = createLazyRuntimeMethod(
|
||||
loadModelAuthRuntime,
|
||||
@@ -213,10 +236,13 @@ export function createPluginRuntime(_options: CreatePluginRuntimeOptions = {}):
|
||||
taskFlow,
|
||||
} satisfies Omit<
|
||||
PluginRuntime,
|
||||
"tts" | "mediaUnderstanding" | "stt" | "modelAuth" | "imageGeneration"
|
||||
"tts" | "mediaUnderstanding" | "stt" | "modelAuth" | "imageGeneration" | "videoGeneration"
|
||||
> &
|
||||
Partial<
|
||||
Pick<PluginRuntime, "tts" | "mediaUnderstanding" | "stt" | "modelAuth" | "imageGeneration">
|
||||
Pick<
|
||||
PluginRuntime,
|
||||
"tts" | "mediaUnderstanding" | "stt" | "modelAuth" | "imageGeneration" | "videoGeneration"
|
||||
>
|
||||
>;
|
||||
|
||||
defineCachedValue(runtime, "tts", createRuntimeTts);
|
||||
@@ -226,6 +252,7 @@ export function createPluginRuntime(_options: CreatePluginRuntimeOptions = {}):
|
||||
}));
|
||||
defineCachedValue(runtime, "modelAuth", createRuntimeModelAuth);
|
||||
defineCachedValue(runtime, "imageGeneration", createRuntimeImageGeneration);
|
||||
defineCachedValue(runtime, "videoGeneration", createRuntimeVideoGeneration);
|
||||
|
||||
return runtime as PluginRuntime;
|
||||
}
|
||||
|
||||
@@ -82,6 +82,10 @@ export type PluginRuntimeCore = {
|
||||
generate: typeof import("../../plugin-sdk/image-generation-runtime.js").generateImage;
|
||||
listProviders: typeof import("../../plugin-sdk/image-generation-runtime.js").listRuntimeImageGenerationProviders;
|
||||
};
|
||||
videoGeneration: {
|
||||
generate: typeof import("../../plugin-sdk/video-generation-runtime.js").generateVideo;
|
||||
listProviders: typeof import("../../plugin-sdk/video-generation-runtime.js").listRuntimeVideoGenerationProviders;
|
||||
};
|
||||
webSearch: {
|
||||
listProviders: typeof import("../../web-search/runtime.js").listWebSearchProviders;
|
||||
search: typeof import("../../web-search/runtime.js").runWebSearch;
|
||||
|
||||
@@ -55,6 +55,7 @@ export function createPluginRecord(
|
||||
realtimeVoiceProviderIds: [],
|
||||
mediaUnderstandingProviderIds: [],
|
||||
imageGenerationProviderIds: [],
|
||||
videoGenerationProviderIds: [],
|
||||
webFetchProviderIds: [],
|
||||
webSearchProviderIds: [],
|
||||
gatewayMethods: [],
|
||||
@@ -119,6 +120,7 @@ export function createPluginLoadResult(
|
||||
speechProviders: [],
|
||||
mediaUnderstandingProviders: [],
|
||||
imageGenerationProviders: [],
|
||||
videoGenerationProviders: [],
|
||||
webFetchProviders: [],
|
||||
webSearchProviders: [],
|
||||
tools: [],
|
||||
|
||||
@@ -69,6 +69,7 @@ import type {
|
||||
SpeechVoiceOption,
|
||||
} from "../tts/provider-types.js";
|
||||
import type { DeliveryContext } from "../utils/delivery-context.js";
|
||||
import type { VideoGenerationProvider } from "../video-generation/types.js";
|
||||
import type { WizardPrompter } from "../wizard/prompts.js";
|
||||
import type { SecretInputMode } from "./provider-auth-types.js";
|
||||
import type { createVpsAwareOAuthHandlers } from "./provider-oauth-flow.js";
|
||||
@@ -1698,6 +1699,7 @@ export type PluginRealtimeVoiceProviderEntry = RealtimeVoiceProviderPlugin & {
|
||||
|
||||
export type MediaUnderstandingProviderPlugin = MediaUnderstandingProvider;
|
||||
export type ImageGenerationProviderPlugin = ImageGenerationProvider;
|
||||
export type VideoGenerationProviderPlugin = VideoGenerationProvider;
|
||||
|
||||
export type OpenClawPluginGatewayMethod = {
|
||||
method: string;
|
||||
@@ -2035,6 +2037,8 @@ export type OpenClawPluginApi = {
|
||||
registerMediaUnderstandingProvider: (provider: MediaUnderstandingProviderPlugin) => void;
|
||||
/** Register an image generation provider (image generation capability). */
|
||||
registerImageGenerationProvider: (provider: ImageGenerationProviderPlugin) => void;
|
||||
/** Register a video generation provider (video generation capability). */
|
||||
registerVideoGenerationProvider: (provider: VideoGenerationProviderPlugin) => void;
|
||||
/** Register a web fetch provider (web fetch capability). */
|
||||
registerWebFetchProvider: (provider: WebFetchProviderPlugin) => void;
|
||||
/** Register a web search provider (web search capability). */
|
||||
|
||||
Reference in New Issue
Block a user