mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:20:43 +00:00
fix(plugins): restore disabled TTS provider fallback
This commit is contained in:
@@ -11,6 +11,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Fixes
|
||||
|
||||
- TTS/providers: keep bundled speech-provider compat fallback available when plugins are globally disabled, so cold gateway and CLI startup can still resolve fallback speech providers instead of leaving explicit TTS provider selection with no registered providers. Thanks @vincentkoc.
|
||||
- Discord: collapse repeated native slash-command deploy rate-limit startup logs into one non-fatal warning while keeping per-request REST timing in verbose output. Thanks @discord.
|
||||
- Providers/OpenAI Codex: preserve existing wrapped Codex streams during OpenAI attribution so PI OAuth bearer injection reaches ChatGPT/Codex Responses, and strip native Codex-only unsupported payload fields without touching custom compatible endpoints. (#75111) Thanks @keshavbotagent.
|
||||
- Agents/tool-result guard: use the resolved runtime context token budget for non-context-engine tool-result overflow checks, so long tool-heavy sessions no longer compact early when `contextTokens` is larger than native `contextWindow`. Fixes #74917. Thanks @kAIborg24.
|
||||
|
||||
@@ -657,10 +657,10 @@ describe("resolvePluginCapabilityProviders", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("does not load bundled capability providers when plugins are globally disabled", () => {
|
||||
it("uses active capability providers when plugins are globally disabled", () => {
|
||||
const cfg = { plugins: { enabled: false, allow: ["custom-plugin"] } } as OpenClawConfig;
|
||||
const loaded = createEmptyPluginRegistry();
|
||||
loaded.mediaUnderstandingProviders.push({
|
||||
const active = createEmptyPluginRegistry();
|
||||
active.mediaUnderstandingProviders.push({
|
||||
pluginId: "openai",
|
||||
pluginName: "openai",
|
||||
source: "test",
|
||||
@@ -669,20 +669,99 @@ describe("resolvePluginCapabilityProviders", () => {
|
||||
capabilities: ["image"],
|
||||
},
|
||||
} as never);
|
||||
mocks.resolveRuntimePluginRegistry.mockReturnValue(loaded);
|
||||
mocks.resolveRuntimePluginRegistry.mockReturnValue(active);
|
||||
|
||||
expectNoResolvedCapabilityProviders(
|
||||
resolvePluginCapabilityProviders({
|
||||
key: "mediaUnderstandingProviders",
|
||||
cfg,
|
||||
}),
|
||||
);
|
||||
const providers = resolvePluginCapabilityProviders({
|
||||
key: "mediaUnderstandingProviders",
|
||||
cfg,
|
||||
});
|
||||
|
||||
expectResolvedCapabilityProviderIds(providers, ["openai"]);
|
||||
expect(mocks.loadPluginManifestRegistry).not.toHaveBeenCalled();
|
||||
expect(mocks.withBundledPluginAllowlistCompat).not.toHaveBeenCalled();
|
||||
expect(mocks.withBundledPluginEnablementCompat).not.toHaveBeenCalled();
|
||||
expect(mocks.withBundledPluginVitestCompat).not.toHaveBeenCalled();
|
||||
expect(mocks.resolveRuntimePluginRegistry).not.toHaveBeenCalled();
|
||||
expect(mocks.resolveRuntimePluginRegistry).toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
it("loads bundled speech providers through compat when plugins are globally disabled", () => {
|
||||
const cfg = {
|
||||
plugins: { enabled: false },
|
||||
messages: { tts: { provider: "mistral" } },
|
||||
} as OpenClawConfig;
|
||||
const allowlistCompat = {
|
||||
...cfg,
|
||||
plugins: {
|
||||
enabled: false,
|
||||
allow: ["microsoft"],
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
const compatConfig = {
|
||||
...cfg,
|
||||
plugins: {
|
||||
enabled: true,
|
||||
allow: ["microsoft"],
|
||||
entries: { microsoft: { enabled: true } },
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
const loaded = createEmptyPluginRegistry();
|
||||
loaded.speechProviders.push({
|
||||
pluginId: "microsoft",
|
||||
pluginName: "microsoft",
|
||||
source: "test",
|
||||
provider: {
|
||||
id: "microsoft",
|
||||
label: "microsoft",
|
||||
aliases: ["edge"],
|
||||
isConfigured: () => true,
|
||||
synthesize: async () => ({
|
||||
audioBuffer: Buffer.from("x"),
|
||||
outputFormat: "mp3",
|
||||
voiceCompatible: false,
|
||||
fileExtension: ".mp3",
|
||||
}),
|
||||
},
|
||||
} as never);
|
||||
mocks.loadPluginManifestRegistry.mockReturnValue({
|
||||
plugins: [
|
||||
{
|
||||
id: "microsoft",
|
||||
origin: "bundled",
|
||||
contracts: { speechProviders: ["microsoft"] },
|
||||
},
|
||||
] as never,
|
||||
diagnostics: [],
|
||||
});
|
||||
mocks.withBundledPluginEnablementCompat.mockReturnValue(compatConfig);
|
||||
mocks.withBundledPluginVitestCompat.mockReturnValue(compatConfig);
|
||||
mocks.resolveRuntimePluginRegistry.mockImplementation((params?: unknown) =>
|
||||
params === undefined ? undefined : loaded,
|
||||
);
|
||||
|
||||
const providers = resolvePluginCapabilityProviders({
|
||||
key: "speechProviders",
|
||||
cfg,
|
||||
});
|
||||
|
||||
expectResolvedCapabilityProviderIds(providers, ["microsoft"]);
|
||||
expect(mocks.loadPluginManifestRegistry).toHaveBeenCalledWith({
|
||||
config: cfg,
|
||||
env: process.env,
|
||||
});
|
||||
expect(mocks.withBundledPluginAllowlistCompat).toHaveBeenCalledWith({
|
||||
config: cfg,
|
||||
pluginIds: ["microsoft"],
|
||||
});
|
||||
expect(mocks.withBundledPluginEnablementCompat).toHaveBeenCalledWith({
|
||||
config: allowlistCompat,
|
||||
pluginIds: ["microsoft"],
|
||||
});
|
||||
expect(mocks.resolveRuntimePluginRegistry).toHaveBeenCalledWith();
|
||||
expect(mocks.resolveRuntimePluginRegistry).toHaveBeenCalledWith({
|
||||
config: compatConfig,
|
||||
onlyPluginIds: ["microsoft"],
|
||||
activate: false,
|
||||
});
|
||||
});
|
||||
|
||||
it.each([
|
||||
@@ -845,8 +924,21 @@ describe("resolvePluginCapabilityProviders", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("does not load targeted bundled capability providers when plugins are globally disabled", () => {
|
||||
it("loads targeted bundled capability providers through compat when plugins are globally disabled", () => {
|
||||
const cfg = { plugins: { enabled: false, allow: ["custom-plugin"] } } as OpenClawConfig;
|
||||
const allowlistCompat = {
|
||||
plugins: {
|
||||
enabled: false,
|
||||
allow: ["custom-plugin", "google"],
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
const enablementCompat = {
|
||||
plugins: {
|
||||
enabled: true,
|
||||
allow: ["custom-plugin", "google"],
|
||||
entries: { google: { enabled: true } },
|
||||
},
|
||||
};
|
||||
const loaded = createEmptyPluginRegistry();
|
||||
loaded.memoryEmbeddingProviders.push({
|
||||
pluginId: "google",
|
||||
@@ -857,7 +949,26 @@ describe("resolvePluginCapabilityProviders", () => {
|
||||
create: async () => ({ provider: null }),
|
||||
},
|
||||
} as never);
|
||||
mocks.resolveRuntimePluginRegistry.mockReturnValue(loaded);
|
||||
mocks.loadPluginManifestRegistry.mockReturnValue({
|
||||
plugins: [
|
||||
{
|
||||
id: "google",
|
||||
origin: "bundled",
|
||||
contracts: { memoryEmbeddingProviders: ["gemini"] },
|
||||
},
|
||||
{
|
||||
id: "openai",
|
||||
origin: "bundled",
|
||||
contracts: { memoryEmbeddingProviders: ["openai"] },
|
||||
},
|
||||
] as never,
|
||||
diagnostics: [],
|
||||
});
|
||||
mocks.withBundledPluginEnablementCompat.mockReturnValue(enablementCompat);
|
||||
mocks.withBundledPluginVitestCompat.mockReturnValue(enablementCompat);
|
||||
mocks.resolveRuntimePluginRegistry.mockImplementation((params?: unknown) =>
|
||||
params === undefined ? undefined : loaded,
|
||||
);
|
||||
|
||||
const provider = resolvePluginCapabilityProvider({
|
||||
key: "memoryEmbeddingProviders",
|
||||
@@ -865,11 +976,20 @@ describe("resolvePluginCapabilityProviders", () => {
|
||||
cfg,
|
||||
});
|
||||
|
||||
expect(provider).toBeUndefined();
|
||||
expect(mocks.loadPluginManifestRegistry).not.toHaveBeenCalled();
|
||||
expect(mocks.withBundledPluginAllowlistCompat).not.toHaveBeenCalled();
|
||||
expect(mocks.withBundledPluginEnablementCompat).not.toHaveBeenCalled();
|
||||
expect(mocks.withBundledPluginVitestCompat).not.toHaveBeenCalled();
|
||||
expect(mocks.resolveRuntimePluginRegistry).not.toHaveBeenCalled();
|
||||
expect(provider?.id).toBe("gemini");
|
||||
expect(mocks.withBundledPluginAllowlistCompat).toHaveBeenCalledWith({
|
||||
config: cfg,
|
||||
pluginIds: ["google"],
|
||||
});
|
||||
expect(mocks.withBundledPluginEnablementCompat).toHaveBeenCalledWith({
|
||||
config: allowlistCompat,
|
||||
pluginIds: ["google"],
|
||||
});
|
||||
expect(mocks.resolveRuntimePluginRegistry).toHaveBeenCalledWith();
|
||||
expect(mocks.resolveRuntimePluginRegistry).toHaveBeenCalledWith({
|
||||
config: enablementCompat,
|
||||
onlyPluginIds: ["google"],
|
||||
activate: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -101,10 +101,6 @@ function createCapabilityProviderFallbackLoadOptions(params: {
|
||||
return loadOptions;
|
||||
}
|
||||
|
||||
function arePluginsGloballyDisabled(cfg: OpenClawConfig | undefined): boolean {
|
||||
return cfg?.plugins?.enabled === false;
|
||||
}
|
||||
|
||||
function findProviderById<K extends CapabilityProviderRegistryKey>(
|
||||
entries: PluginRegistry[K],
|
||||
providerId: string,
|
||||
@@ -225,10 +221,6 @@ export function resolvePluginCapabilityProvider<K extends CapabilityProviderRegi
|
||||
cfg?: OpenClawConfig;
|
||||
installBundledRuntimeDeps?: boolean;
|
||||
}): CapabilityProviderForKey<K> | undefined {
|
||||
if (arePluginsGloballyDisabled(params.cfg)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const activeRegistry = resolveRuntimePluginRegistry();
|
||||
const activeProvider = findProviderById(activeRegistry?.[params.key] ?? [], params.providerId);
|
||||
if (activeProvider) {
|
||||
@@ -263,10 +255,6 @@ export function resolvePluginCapabilityProviders<K extends CapabilityProviderReg
|
||||
cfg?: OpenClawConfig;
|
||||
installBundledRuntimeDeps?: boolean;
|
||||
}): CapabilityProviderForKey<K>[] {
|
||||
if (arePluginsGloballyDisabled(params.cfg)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const activeRegistry = resolveRuntimePluginRegistry();
|
||||
const activeProviders = activeRegistry?.[params.key] ?? [];
|
||||
if (
|
||||
|
||||
Reference in New Issue
Block a user