feat(qwen): add qwen provider and video generation

This commit is contained in:
Peter Steinberger
2026-04-04 17:43:15 +01:00
parent 759373e887
commit e3ac0f43df
104 changed files with 2477 additions and 483 deletions

View File

@@ -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),

View File

@@ -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;
}