mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 18:50:42 +00:00
perf(test): route speech provider registry through unit-fast
This commit is contained in:
58
src/tts/provider-registry-core.ts
Normal file
58
src/tts/provider-registry-core.ts
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import type { OpenClawConfig } from "../config/types.js";
|
||||||
|
import {
|
||||||
|
buildCapabilityProviderMaps,
|
||||||
|
normalizeCapabilityProviderId,
|
||||||
|
} from "../plugins/provider-registry-shared.js";
|
||||||
|
import type { SpeechProviderPlugin } from "../plugins/types.js";
|
||||||
|
import type { SpeechProviderId } from "./provider-types.js";
|
||||||
|
|
||||||
|
export type SpeechProviderRegistryResolver = {
|
||||||
|
getProvider: (providerId: string, cfg?: OpenClawConfig) => SpeechProviderPlugin | undefined;
|
||||||
|
listProviders: (cfg?: OpenClawConfig) => SpeechProviderPlugin[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export function normalizeSpeechProviderId(
|
||||||
|
providerId: string | undefined,
|
||||||
|
): SpeechProviderId | undefined {
|
||||||
|
return normalizeCapabilityProviderId(providerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createSpeechProviderRegistry(resolver: SpeechProviderRegistryResolver) {
|
||||||
|
const buildResolvedProviderMaps = (cfg?: OpenClawConfig) =>
|
||||||
|
buildCapabilityProviderMaps(resolver.listProviders(cfg));
|
||||||
|
|
||||||
|
const listProviders = (cfg?: OpenClawConfig): SpeechProviderPlugin[] => [
|
||||||
|
...buildResolvedProviderMaps(cfg).canonical.values(),
|
||||||
|
];
|
||||||
|
|
||||||
|
const getProvider = (
|
||||||
|
providerId: string | undefined,
|
||||||
|
cfg?: OpenClawConfig,
|
||||||
|
): SpeechProviderPlugin | undefined => {
|
||||||
|
const normalized = normalizeSpeechProviderId(providerId);
|
||||||
|
if (!normalized) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
resolver.getProvider(normalized, cfg) ??
|
||||||
|
buildResolvedProviderMaps(cfg).aliases.get(normalized)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const canonicalizeProviderId = (
|
||||||
|
providerId: string | undefined,
|
||||||
|
cfg?: OpenClawConfig,
|
||||||
|
): SpeechProviderId | undefined => {
|
||||||
|
const normalized = normalizeSpeechProviderId(providerId);
|
||||||
|
if (!normalized) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return getProvider(normalized, cfg)?.id ?? normalized;
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
canonicalizeSpeechProviderId: canonicalizeProviderId,
|
||||||
|
getSpeechProvider: getProvider,
|
||||||
|
listSpeechProviders: listProviders,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,19 +1,10 @@
|
|||||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
import { beforeEach, describe, expect, it } from "vitest";
|
||||||
import type { OpenClawConfig } from "../config/types.js";
|
import type { OpenClawConfig } from "../config/types.js";
|
||||||
import type { SpeechProviderPlugin } from "../plugins/types.js";
|
import type { SpeechProviderPlugin } from "../plugins/types.js";
|
||||||
|
import {
|
||||||
const resolvePluginCapabilityProviderMock = vi.hoisted(() => vi.fn());
|
createSpeechProviderRegistry,
|
||||||
const resolvePluginCapabilityProvidersMock = vi.hoisted(() => vi.fn());
|
normalizeSpeechProviderId,
|
||||||
|
} from "./provider-registry-core.js";
|
||||||
vi.mock("../plugins/capability-provider-runtime.js", () => ({
|
|
||||||
resolvePluginCapabilityProvider: resolvePluginCapabilityProviderMock,
|
|
||||||
resolvePluginCapabilityProviders: resolvePluginCapabilityProvidersMock,
|
|
||||||
}));
|
|
||||||
|
|
||||||
let getSpeechProvider: typeof import("./provider-registry.js").getSpeechProvider;
|
|
||||||
let listSpeechProviders: typeof import("./provider-registry.js").listSpeechProviders;
|
|
||||||
let canonicalizeSpeechProviderId: typeof import("./provider-registry.js").canonicalizeSpeechProviderId;
|
|
||||||
let normalizeSpeechProviderId: typeof import("./provider-registry.js").normalizeSpeechProviderId;
|
|
||||||
|
|
||||||
function createSpeechProvider(id: string, aliases?: string[]): SpeechProviderPlugin {
|
function createSpeechProvider(id: string, aliases?: string[]): SpeechProviderPlugin {
|
||||||
return {
|
return {
|
||||||
@@ -31,59 +22,57 @@ function createSpeechProvider(id: string, aliases?: string[]): SpeechProviderPlu
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe("speech provider registry", () => {
|
describe("speech provider registry", () => {
|
||||||
beforeAll(async () => {
|
const getProviderCalls: Array<{ providerId: string; cfg?: OpenClawConfig }> = [];
|
||||||
vi.resetModules();
|
const listProvidersCalls: Array<{ cfg?: OpenClawConfig }> = [];
|
||||||
({
|
let providers: SpeechProviderPlugin[] = [];
|
||||||
getSpeechProvider,
|
let directProvider: SpeechProviderPlugin | undefined;
|
||||||
listSpeechProviders,
|
let registry: ReturnType<typeof createSpeechProviderRegistry>;
|
||||||
canonicalizeSpeechProviderId,
|
|
||||||
normalizeSpeechProviderId,
|
|
||||||
} = await import("./provider-registry.js"));
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
resolvePluginCapabilityProviderMock.mockReset();
|
providers = [];
|
||||||
resolvePluginCapabilityProviderMock.mockReturnValue(undefined);
|
directProvider = undefined;
|
||||||
resolvePluginCapabilityProvidersMock.mockReset();
|
getProviderCalls.length = 0;
|
||||||
resolvePluginCapabilityProvidersMock.mockReturnValue([]);
|
listProvidersCalls.length = 0;
|
||||||
|
registry = createSpeechProviderRegistry({
|
||||||
|
getProvider: (providerId, cfg) => {
|
||||||
|
getProviderCalls.push({ providerId, cfg });
|
||||||
|
return directProvider;
|
||||||
|
},
|
||||||
|
listProviders: (cfg) => {
|
||||||
|
listProvidersCalls.push({ cfg });
|
||||||
|
return providers;
|
||||||
|
},
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("lists providers from the speech capability runtime", () => {
|
it("lists providers from the speech capability runtime", () => {
|
||||||
const cfg = {} as OpenClawConfig;
|
const cfg = {} as OpenClawConfig;
|
||||||
resolvePluginCapabilityProvidersMock.mockReturnValue([createSpeechProvider("demo-speech")]);
|
providers = [createSpeechProvider("demo-speech")];
|
||||||
|
|
||||||
expect(listSpeechProviders(cfg).map((provider) => provider.id)).toEqual(["demo-speech"]);
|
expect(registry.listSpeechProviders(cfg).map((provider) => provider.id)).toEqual([
|
||||||
expect(resolvePluginCapabilityProvidersMock).toHaveBeenCalledWith({
|
"demo-speech",
|
||||||
key: "speechProviders",
|
]);
|
||||||
cfg,
|
expect(listProvidersCalls).toEqual([{ cfg }]);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("gets providers by normalized id through the capability runtime", () => {
|
it("gets providers by normalized id through the capability runtime", () => {
|
||||||
const cfg = {} as OpenClawConfig;
|
const cfg = {} as OpenClawConfig;
|
||||||
const provider = createSpeechProvider("microsoft", ["edge"]);
|
directProvider = createSpeechProvider("microsoft", ["edge"]);
|
||||||
resolvePluginCapabilityProviderMock.mockReturnValue(provider);
|
|
||||||
|
|
||||||
expect(getSpeechProvider(" MICROSOFT ", cfg)).toBe(provider);
|
expect(registry.getSpeechProvider(" MICROSOFT ", cfg)).toBe(directProvider);
|
||||||
expect(resolvePluginCapabilityProviderMock).toHaveBeenCalledWith({
|
expect(getProviderCalls).toEqual([{ providerId: "microsoft", cfg }]);
|
||||||
key: "speechProviders",
|
|
||||||
providerId: "microsoft",
|
|
||||||
cfg,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("canonicalizes aliases from listed providers when direct lookup misses", () => {
|
it("canonicalizes aliases from listed providers when direct lookup misses", () => {
|
||||||
resolvePluginCapabilityProvidersMock.mockReturnValue([
|
providers = [createSpeechProvider("microsoft", ["edge"])];
|
||||||
createSpeechProvider("microsoft", ["edge"]),
|
|
||||||
]);
|
|
||||||
|
|
||||||
expect(normalizeSpeechProviderId("edge")).toBe("edge");
|
expect(normalizeSpeechProviderId("edge")).toBe("edge");
|
||||||
expect(canonicalizeSpeechProviderId("edge")).toBe("microsoft");
|
expect(registry.canonicalizeSpeechProviderId("edge")).toBe("microsoft");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns empty results when the capability runtime has no speech providers", () => {
|
it("returns empty results when the capability runtime has no speech providers", () => {
|
||||||
expect(listSpeechProviders()).toEqual([]);
|
expect(registry.listSpeechProviders()).toEqual([]);
|
||||||
expect(getSpeechProvider("demo-speech")).toBeUndefined();
|
expect(registry.getSpeechProvider("demo-speech")).toBeUndefined();
|
||||||
expect(canonicalizeSpeechProviderId("demo-speech")).toBe("demo-speech");
|
expect(registry.canonicalizeSpeechProviderId("demo-speech")).toBe("demo-speech");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,18 +3,12 @@ import {
|
|||||||
resolvePluginCapabilityProvider,
|
resolvePluginCapabilityProvider,
|
||||||
resolvePluginCapabilityProviders,
|
resolvePluginCapabilityProviders,
|
||||||
} from "../plugins/capability-provider-runtime.js";
|
} from "../plugins/capability-provider-runtime.js";
|
||||||
import {
|
|
||||||
buildCapabilityProviderMaps,
|
|
||||||
normalizeCapabilityProviderId,
|
|
||||||
} from "../plugins/provider-registry-shared.js";
|
|
||||||
import type { SpeechProviderPlugin } from "../plugins/types.js";
|
import type { SpeechProviderPlugin } from "../plugins/types.js";
|
||||||
import type { SpeechProviderId } from "./provider-types.js";
|
export { normalizeSpeechProviderId } from "./provider-registry-core.js";
|
||||||
|
import {
|
||||||
export function normalizeSpeechProviderId(
|
createSpeechProviderRegistry,
|
||||||
providerId: string | undefined,
|
type SpeechProviderRegistryResolver,
|
||||||
): SpeechProviderId | undefined {
|
} from "./provider-registry-core.js";
|
||||||
return normalizeCapabilityProviderId(providerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
function resolveSpeechProviderPluginEntries(cfg?: OpenClawConfig): SpeechProviderPlugin[] {
|
function resolveSpeechProviderPluginEntries(cfg?: OpenClawConfig): SpeechProviderPlugin[] {
|
||||||
return resolvePluginCapabilityProviders({
|
return resolvePluginCapabilityProviders({
|
||||||
@@ -23,41 +17,21 @@ function resolveSpeechProviderPluginEntries(cfg?: OpenClawConfig): SpeechProvide
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildProviderMaps(cfg?: OpenClawConfig): {
|
const defaultSpeechProviderRegistryResolver: SpeechProviderRegistryResolver = {
|
||||||
canonical: Map<string, SpeechProviderPlugin>;
|
getProvider: (providerId, cfg) =>
|
||||||
aliases: Map<string, SpeechProviderPlugin>;
|
|
||||||
} {
|
|
||||||
return buildCapabilityProviderMaps(resolveSpeechProviderPluginEntries(cfg));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function listSpeechProviders(cfg?: OpenClawConfig): SpeechProviderPlugin[] {
|
|
||||||
return [...buildProviderMaps(cfg).canonical.values()];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getSpeechProvider(
|
|
||||||
providerId: string | undefined,
|
|
||||||
cfg?: OpenClawConfig,
|
|
||||||
): SpeechProviderPlugin | undefined {
|
|
||||||
const normalized = normalizeSpeechProviderId(providerId);
|
|
||||||
if (!normalized) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
resolvePluginCapabilityProvider({
|
resolvePluginCapabilityProvider({
|
||||||
key: "speechProviders",
|
key: "speechProviders",
|
||||||
providerId: normalized,
|
providerId,
|
||||||
cfg,
|
cfg,
|
||||||
}) ?? buildProviderMaps(cfg).aliases.get(normalized)
|
}),
|
||||||
);
|
listProviders: resolveSpeechProviderPluginEntries,
|
||||||
}
|
};
|
||||||
|
|
||||||
export function canonicalizeSpeechProviderId(
|
const defaultSpeechProviderRegistry = createSpeechProviderRegistry(
|
||||||
providerId: string | undefined,
|
defaultSpeechProviderRegistryResolver,
|
||||||
cfg?: OpenClawConfig,
|
);
|
||||||
): SpeechProviderId | undefined {
|
|
||||||
const normalized = normalizeSpeechProviderId(providerId);
|
export const listSpeechProviders = defaultSpeechProviderRegistry.listSpeechProviders;
|
||||||
if (!normalized) {
|
export const getSpeechProvider = defaultSpeechProviderRegistry.getSpeechProvider;
|
||||||
return undefined;
|
export const canonicalizeSpeechProviderId =
|
||||||
}
|
defaultSpeechProviderRegistry.canonicalizeSpeechProviderId;
|
||||||
return getSpeechProvider(normalized, cfg)?.id ?? normalized;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ export const forcedUnitFastTestFiles = [
|
|||||||
"src/security/windows-acl.test.ts",
|
"src/security/windows-acl.test.ts",
|
||||||
"src/realtime-transcription/websocket-session.test.ts",
|
"src/realtime-transcription/websocket-session.test.ts",
|
||||||
"src/trajectory/export.test.ts",
|
"src/trajectory/export.test.ts",
|
||||||
|
"src/tts/provider-registry.test.ts",
|
||||||
"src/tts/status-config.test.ts",
|
"src/tts/status-config.test.ts",
|
||||||
"src/version.test.ts",
|
"src/version.test.ts",
|
||||||
];
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user