mirror of
https://github.com/moltbot/moltbot.git
synced 2026-04-24 07:01:49 +00:00
perf: avoid plugin loader on provider fast paths
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { logVerbose } from "../../globals.js";
|
||||
import { listSpeechProviders, normalizeSpeechProviderId } from "../../tts/provider-registry.js";
|
||||
import { getSpeechProvider, normalizeSpeechProviderId } from "../../tts/provider-registry.js";
|
||||
import {
|
||||
getLastTtsAttempt,
|
||||
getTtsMaxLength,
|
||||
@@ -178,8 +178,7 @@ export const handleTtsCommands: CommandHandler = async (params, allowTextCommand
|
||||
}
|
||||
|
||||
const requested = args.trim().toLowerCase();
|
||||
const knownProviders = new Set(listSpeechProviders(params.cfg).map((provider) => provider.id));
|
||||
if (requested !== "edge" && !knownProviders.has(requested)) {
|
||||
if (requested !== "edge" && !getSpeechProvider(requested, params.cfg)) {
|
||||
return { shouldContinue: false, reply: ttsUsage() };
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { loadConfig } from "../../config/config.js";
|
||||
import { listSpeechProviders, normalizeSpeechProviderId } from "../../tts/provider-registry.js";
|
||||
import {
|
||||
getSpeechProvider,
|
||||
listSpeechProviders,
|
||||
normalizeSpeechProviderId,
|
||||
} from "../../tts/provider-registry.js";
|
||||
import {
|
||||
OPENAI_TTS_MODELS,
|
||||
OPENAI_TTS_VOICES,
|
||||
@@ -104,8 +108,7 @@ export const ttsHandlers: GatewayRequestHandlers = {
|
||||
typeof params.provider === "string" ? params.provider.trim() : "",
|
||||
);
|
||||
const cfg = loadConfig();
|
||||
const knownProviders = new Set(listSpeechProviders(cfg).map((entry) => entry.id));
|
||||
if (!provider || !knownProviders.has(provider)) {
|
||||
if (!provider || !getSpeechProvider(provider, cfg)) {
|
||||
respond(
|
||||
false,
|
||||
undefined,
|
||||
|
||||
52
src/image-generation/provider-registry.test.ts
Normal file
52
src/image-generation/provider-registry.test.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { createEmptyPluginRegistry } from "../plugins/registry.js";
|
||||
import { resetPluginRuntimeStateForTest, setActivePluginRegistry } from "../plugins/runtime.js";
|
||||
|
||||
const { loadOpenClawPluginsMock } = vi.hoisted(() => ({
|
||||
loadOpenClawPluginsMock: vi.fn(() => createEmptyPluginRegistry()),
|
||||
}));
|
||||
|
||||
vi.mock("../plugins/loader.js", () => ({
|
||||
loadOpenClawPlugins: loadOpenClawPluginsMock,
|
||||
}));
|
||||
|
||||
import { getImageGenerationProvider, listImageGenerationProviders } from "./provider-registry.js";
|
||||
|
||||
describe("image-generation provider registry", () => {
|
||||
afterEach(() => {
|
||||
loadOpenClawPluginsMock.mockReset();
|
||||
loadOpenClawPluginsMock.mockReturnValue(createEmptyPluginRegistry());
|
||||
resetPluginRuntimeStateForTest();
|
||||
});
|
||||
|
||||
it("does not load plugins when listing without config", () => {
|
||||
expect(listImageGenerationProviders()).toEqual([]);
|
||||
expect(loadOpenClawPluginsMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("uses active plugin providers without loading from disk", () => {
|
||||
const registry = createEmptyPluginRegistry();
|
||||
registry.imageGenerationProviders.push({
|
||||
pluginId: "custom-image",
|
||||
pluginName: "Custom Image",
|
||||
source: "test",
|
||||
provider: {
|
||||
id: "custom-image",
|
||||
label: "Custom Image",
|
||||
capabilities: {
|
||||
generate: {},
|
||||
edit: { enabled: false },
|
||||
},
|
||||
generateImage: async () => ({
|
||||
images: [{ buffer: Buffer.from("image"), mimeType: "image/png" }],
|
||||
}),
|
||||
},
|
||||
});
|
||||
setActivePluginRegistry(registry);
|
||||
|
||||
const provider = getImageGenerationProvider("custom-image");
|
||||
|
||||
expect(provider?.id).toBe("custom-image");
|
||||
expect(loadOpenClawPluginsMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -15,11 +15,13 @@ function resolvePluginImageGenerationProviders(
|
||||
cfg?: OpenClawConfig,
|
||||
): ImageGenerationProviderPlugin[] {
|
||||
const active = getActivePluginRegistry();
|
||||
const registry =
|
||||
(active?.imageGenerationProviders?.length ?? 0) > 0 || !cfg
|
||||
? active
|
||||
: loadOpenClawPlugins({ config: cfg });
|
||||
return registry?.imageGenerationProviders?.map((entry) => entry.provider) ?? [];
|
||||
const activeEntries = active?.imageGenerationProviders?.map((entry) => entry.provider) ?? [];
|
||||
if (activeEntries.length > 0 || !cfg) {
|
||||
return activeEntries;
|
||||
}
|
||||
return loadOpenClawPlugins({ config: cfg }).imageGenerationProviders.map(
|
||||
(entry) => entry.provider,
|
||||
);
|
||||
}
|
||||
|
||||
function buildProviderMaps(cfg?: OpenClawConfig): {
|
||||
|
||||
@@ -1,11 +1,22 @@
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { createEmptyPluginRegistry } from "../../plugins/registry.js";
|
||||
import { setActivePluginRegistry } from "../../plugins/runtime.js";
|
||||
import { resetPluginRuntimeStateForTest, setActivePluginRegistry } from "../../plugins/runtime.js";
|
||||
|
||||
const { loadOpenClawPluginsMock } = vi.hoisted(() => ({
|
||||
loadOpenClawPluginsMock: vi.fn(() => createEmptyPluginRegistry()),
|
||||
}));
|
||||
|
||||
vi.mock("../../plugins/loader.js", () => ({
|
||||
loadOpenClawPlugins: loadOpenClawPluginsMock,
|
||||
}));
|
||||
|
||||
import { buildMediaUnderstandingRegistry, getMediaUnderstandingProvider } from "./index.js";
|
||||
|
||||
describe("media-understanding provider registry", () => {
|
||||
afterEach(() => {
|
||||
setActivePluginRegistry(createEmptyPluginRegistry());
|
||||
loadOpenClawPluginsMock.mockReset();
|
||||
loadOpenClawPluginsMock.mockReturnValue(createEmptyPluginRegistry());
|
||||
resetPluginRuntimeStateForTest();
|
||||
});
|
||||
|
||||
it("keeps core-owned fallback providers registered by default", () => {
|
||||
@@ -60,4 +71,11 @@ describe("media-understanding provider registry", () => {
|
||||
|
||||
expect(provider?.id).toBe("google");
|
||||
});
|
||||
|
||||
it("does not load plugins when config is absent and no runtime registry is active", () => {
|
||||
const registry = buildMediaUnderstandingRegistry();
|
||||
|
||||
expect([...registry.keys()]).toEqual(["groq", "deepgram"]);
|
||||
expect(loadOpenClawPluginsMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -41,13 +41,15 @@ export function buildMediaUnderstandingRegistry(
|
||||
mergeProviderIntoRegistry(registry, provider);
|
||||
}
|
||||
const active = getActivePluginRegistry();
|
||||
const pluginRegistry =
|
||||
(active?.mediaUnderstandingProviders?.length ?? 0) > 0
|
||||
? active
|
||||
: loadOpenClawPlugins({ config: cfg });
|
||||
for (const entry of pluginRegistry?.mediaUnderstandingProviders ?? []) {
|
||||
const activeEntries = active?.mediaUnderstandingProviders ?? [];
|
||||
for (const entry of activeEntries) {
|
||||
mergeProviderIntoRegistry(registry, entry.provider);
|
||||
}
|
||||
if (activeEntries.length === 0 && cfg) {
|
||||
for (const entry of loadOpenClawPlugins({ config: cfg }).mediaUnderstandingProviders) {
|
||||
mergeProviderIntoRegistry(registry, entry.provider);
|
||||
}
|
||||
}
|
||||
if (overrides) {
|
||||
for (const [key, provider] of Object.entries(overrides)) {
|
||||
const normalizedKey = normalizeMediaProviderId(key);
|
||||
|
||||
@@ -494,7 +494,7 @@ export async function resolveAutoImageModel(params: {
|
||||
agentDir?: string;
|
||||
activeModel?: ActiveMediaModel;
|
||||
}): Promise<ActiveMediaModel | null> {
|
||||
const providerRegistry = buildProviderRegistry();
|
||||
const providerRegistry = buildProviderRegistry(undefined, params.cfg);
|
||||
const toActive = (entry: MediaUnderstandingModelConfig | null): ActiveMediaModel | null => {
|
||||
if (!entry || entry.type === "cli") {
|
||||
return null;
|
||||
|
||||
62
src/tts/provider-registry.test.ts
Normal file
62
src/tts/provider-registry.test.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { createEmptyPluginRegistry } from "../plugins/registry.js";
|
||||
import { resetPluginRuntimeStateForTest, setActivePluginRegistry } from "../plugins/runtime.js";
|
||||
|
||||
const { loadOpenClawPluginsMock } = vi.hoisted(() => ({
|
||||
loadOpenClawPluginsMock: vi.fn(() => createEmptyPluginRegistry()),
|
||||
}));
|
||||
|
||||
vi.mock("../plugins/loader.js", () => ({
|
||||
loadOpenClawPlugins: loadOpenClawPluginsMock,
|
||||
}));
|
||||
|
||||
import { getSpeechProvider, listSpeechProviders } from "./provider-registry.js";
|
||||
|
||||
describe("speech provider registry", () => {
|
||||
afterEach(() => {
|
||||
loadOpenClawPluginsMock.mockReset();
|
||||
loadOpenClawPluginsMock.mockReturnValue(createEmptyPluginRegistry());
|
||||
resetPluginRuntimeStateForTest();
|
||||
});
|
||||
|
||||
it("does not load plugins for builtin provider lookup", () => {
|
||||
const provider = getSpeechProvider("openai", {} as OpenClawConfig);
|
||||
|
||||
expect(provider?.id).toBe("openai");
|
||||
expect(loadOpenClawPluginsMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not load plugins when listing without config", () => {
|
||||
const providers = listSpeechProviders();
|
||||
|
||||
expect(providers.map((provider) => provider.id)).toEqual(["openai", "elevenlabs", "microsoft"]);
|
||||
expect(loadOpenClawPluginsMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("uses active plugin speech providers without loading from disk", () => {
|
||||
const registry = createEmptyPluginRegistry();
|
||||
registry.speechProviders.push({
|
||||
pluginId: "custom-speech",
|
||||
pluginName: "Custom Speech",
|
||||
source: "test",
|
||||
provider: {
|
||||
id: "custom-speech",
|
||||
label: "Custom Speech",
|
||||
isConfigured: () => true,
|
||||
synthesize: async () => ({
|
||||
audioBuffer: Buffer.from("audio"),
|
||||
outputFormat: "mp3",
|
||||
fileExtension: ".mp3",
|
||||
voiceCompatible: false,
|
||||
}),
|
||||
},
|
||||
});
|
||||
setActivePluginRegistry(registry);
|
||||
|
||||
const provider = getSpeechProvider("custom-speech");
|
||||
|
||||
expect(provider?.id).toBe("custom-speech");
|
||||
expect(loadOpenClawPluginsMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -30,11 +30,32 @@ export function normalizeSpeechProviderId(
|
||||
|
||||
function resolveSpeechProviderPluginEntries(cfg?: OpenClawConfig): SpeechProviderPlugin[] {
|
||||
const active = getActivePluginRegistry();
|
||||
const registry =
|
||||
(active?.speechProviders?.length ?? 0) > 0 || !cfg
|
||||
? active
|
||||
: loadOpenClawPlugins({ config: cfg });
|
||||
return registry?.speechProviders?.map((entry) => entry.provider) ?? [];
|
||||
const activeEntries = active?.speechProviders?.map((entry) => entry.provider) ?? [];
|
||||
if (activeEntries.length > 0 || !cfg) {
|
||||
return activeEntries;
|
||||
}
|
||||
return loadOpenClawPlugins({ config: cfg }).speechProviders.map((entry) => entry.provider);
|
||||
}
|
||||
|
||||
function registerSpeechProvider(
|
||||
maps: {
|
||||
canonical: Map<string, SpeechProviderPlugin>;
|
||||
aliases: Map<string, SpeechProviderPlugin>;
|
||||
},
|
||||
provider: SpeechProviderPlugin,
|
||||
): void {
|
||||
const id = normalizeSpeechProviderId(provider.id);
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
maps.canonical.set(id, provider);
|
||||
maps.aliases.set(id, provider);
|
||||
for (const alias of provider.aliases ?? []) {
|
||||
const normalizedAlias = normalizeSpeechProviderId(alias);
|
||||
if (normalizedAlias) {
|
||||
maps.aliases.set(normalizedAlias, provider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function buildProviderMaps(cfg?: OpenClawConfig): {
|
||||
@@ -43,29 +64,16 @@ function buildProviderMaps(cfg?: OpenClawConfig): {
|
||||
} {
|
||||
const canonical = new Map<string, SpeechProviderPlugin>();
|
||||
const aliases = new Map<string, SpeechProviderPlugin>();
|
||||
const register = (provider: SpeechProviderPlugin) => {
|
||||
const id = normalizeSpeechProviderId(provider.id);
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
canonical.set(id, provider);
|
||||
aliases.set(id, provider);
|
||||
for (const alias of provider.aliases ?? []) {
|
||||
const normalizedAlias = normalizeSpeechProviderId(alias);
|
||||
if (normalizedAlias) {
|
||||
aliases.set(normalizedAlias, provider);
|
||||
}
|
||||
}
|
||||
};
|
||||
const maps = { canonical, aliases };
|
||||
|
||||
for (const buildProvider of BUILTIN_SPEECH_PROVIDER_BUILDERS) {
|
||||
register(buildProvider());
|
||||
registerSpeechProvider(maps, buildProvider());
|
||||
}
|
||||
for (const provider of resolveSpeechProviderPluginEntries(cfg)) {
|
||||
register(provider);
|
||||
registerSpeechProvider(maps, provider);
|
||||
}
|
||||
|
||||
return { canonical, aliases };
|
||||
return maps;
|
||||
}
|
||||
|
||||
export function listSpeechProviders(cfg?: OpenClawConfig): SpeechProviderPlugin[] {
|
||||
@@ -80,5 +88,10 @@ export function getSpeechProvider(
|
||||
if (!normalized) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const local = buildProviderMaps().aliases.get(normalized);
|
||||
if (local || !cfg) {
|
||||
return local;
|
||||
}
|
||||
return buildProviderMaps(cfg).aliases.get(normalized);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user