refactor: dedupe provider registry helpers

This commit is contained in:
Peter Steinberger
2026-04-06 19:57:13 +01:00
parent a5b5632809
commit 5ac49b01c6
4 changed files with 70 additions and 56 deletions

View File

@@ -0,0 +1,24 @@
import { describe, expect, it } from "vitest";
import {
buildCapabilityProviderMaps,
normalizeCapabilityProviderId,
} from "./provider-registry-shared.js";
describe("provider registry shared", () => {
it("normalizes provider ids case-insensitively", () => {
expect(normalizeCapabilityProviderId(" OpenAI ")).toBe("openai");
expect(normalizeCapabilityProviderId(" ")).toBeUndefined();
});
it("indexes providers by id and alias", () => {
const { canonical, aliases } = buildCapabilityProviderMaps([
{ id: "Microsoft", aliases: [" EDGE ", "ms"] },
{ id: "OpenAI" },
]);
expect([...canonical.keys()]).toEqual(["microsoft", "openai"]);
expect(aliases.get("edge")?.id).toBe("Microsoft");
expect(aliases.get("ms")?.id).toBe("Microsoft");
expect(aliases.get("openai")?.id).toBe("OpenAI");
});
});

View File

@@ -0,0 +1,34 @@
export function normalizeCapabilityProviderId(providerId: string | undefined): string | undefined {
const trimmed = providerId?.trim().toLowerCase();
return trimmed ? trimmed : undefined;
}
export function buildCapabilityProviderMaps<T extends { id: string; aliases?: readonly string[] }>(
providers: readonly T[],
normalizeId: (
providerId: string | undefined,
) => string | undefined = normalizeCapabilityProviderId,
): {
canonical: Map<string, T>;
aliases: Map<string, T>;
} {
const canonical = new Map<string, T>();
const aliases = new Map<string, T>();
for (const provider of providers) {
const id = normalizeId(provider.id);
if (!id) {
continue;
}
canonical.set(id, provider);
aliases.set(id, provider);
for (const alias of provider.aliases ?? []) {
const normalizedAlias = normalizeId(alias);
if (normalizedAlias) {
aliases.set(normalizedAlias, provider);
}
}
}
return { canonical, aliases };
}

View File

@@ -1,17 +1,16 @@
import type { OpenClawConfig } from "../config/config.js";
import { resolvePluginCapabilityProviders } from "../plugins/capability-provider-runtime.js";
import {
buildCapabilityProviderMaps,
normalizeCapabilityProviderId,
} from "../plugins/provider-registry-shared.js";
import type { RealtimeVoiceProviderPlugin } from "../plugins/types.js";
import type { RealtimeVoiceProviderId } from "./provider-types.js";
function trimToUndefined(value: string | undefined): string | undefined {
const trimmed = value?.trim().toLowerCase();
return trimmed ? trimmed : undefined;
}
export function normalizeRealtimeVoiceProviderId(
providerId: string | undefined,
): RealtimeVoiceProviderId | undefined {
return trimToUndefined(providerId);
return normalizeCapabilityProviderId(providerId);
}
function resolveRealtimeVoiceProviderEntries(cfg?: OpenClawConfig): RealtimeVoiceProviderPlugin[] {
@@ -25,28 +24,7 @@ function buildProviderMaps(cfg?: OpenClawConfig): {
canonical: Map<string, RealtimeVoiceProviderPlugin>;
aliases: Map<string, RealtimeVoiceProviderPlugin>;
} {
const canonical = new Map<string, RealtimeVoiceProviderPlugin>();
const aliases = new Map<string, RealtimeVoiceProviderPlugin>();
const register = (provider: RealtimeVoiceProviderPlugin) => {
const id = normalizeRealtimeVoiceProviderId(provider.id);
if (!id) {
return;
}
canonical.set(id, provider);
aliases.set(id, provider);
for (const alias of provider.aliases ?? []) {
const normalizedAlias = normalizeRealtimeVoiceProviderId(alias);
if (normalizedAlias) {
aliases.set(normalizedAlias, provider);
}
}
};
for (const provider of resolveRealtimeVoiceProviderEntries(cfg)) {
register(provider);
}
return { canonical, aliases };
return buildCapabilityProviderMaps(resolveRealtimeVoiceProviderEntries(cfg));
}
export function listRealtimeVoiceProviders(cfg?: OpenClawConfig): RealtimeVoiceProviderPlugin[] {

View File

@@ -1,17 +1,16 @@
import type { OpenClawConfig } from "../config/config.js";
import { resolvePluginCapabilityProviders } from "../plugins/capability-provider-runtime.js";
import {
buildCapabilityProviderMaps,
normalizeCapabilityProviderId,
} from "../plugins/provider-registry-shared.js";
import type { SpeechProviderPlugin } from "../plugins/types.js";
import type { SpeechProviderId } from "./provider-types.js";
function trimToUndefined(value: string | undefined): string | undefined {
const trimmed = value?.trim().toLowerCase();
return trimmed ? trimmed : undefined;
}
export function normalizeSpeechProviderId(
providerId: string | undefined,
): SpeechProviderId | undefined {
return trimToUndefined(providerId);
return normalizeCapabilityProviderId(providerId);
}
function resolveSpeechProviderPluginEntries(cfg?: OpenClawConfig): SpeechProviderPlugin[] {
@@ -25,28 +24,7 @@ function buildProviderMaps(cfg?: OpenClawConfig): {
canonical: Map<string, SpeechProviderPlugin>;
aliases: Map<string, SpeechProviderPlugin>;
} {
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);
}
}
};
for (const provider of resolveSpeechProviderPluginEntries(cfg)) {
register(provider);
}
return { canonical, aliases };
return buildCapabilityProviderMaps(resolveSpeechProviderPluginEntries(cfg));
}
export function listSpeechProviders(cfg?: OpenClawConfig): SpeechProviderPlugin[] {