refactor: route runtime seams through plugin sdk facades

This commit is contained in:
Peter Steinberger 2026-04-05 15:12:55 +01:00
parent 7ff7a27f61
commit 5da21bc2f7
No known key found for this signature in database
9 changed files with 144 additions and 28 deletions

View File

@ -1,6 +1,8 @@
import { describe, it, expect, vi } from "vitest";
import type { OpenClawConfig } from "../config/config.js";
import { resetLogger, setLoggerOverride } from "../logging/logger.js";
import { createEmptyPluginRegistry } from "../plugins/registry-empty.js";
import { setActivePluginRegistry } from "../plugins/runtime.js";
import {
buildAllowedModelSet,
inferUniqueProviderFromConfiguredModels,
@ -132,7 +134,17 @@ describe("model-selection", () => {
});
describe("isCliProvider", () => {
it("treats claude-cli as a CLI provider even without explicit cliBackends config", () => {
it("treats runtime-registered CLI backends as CLI providers", () => {
const registry = createEmptyPluginRegistry();
registry.cliBackends.push({
pluginId: "anthropic",
backend: {
id: "claude-cli",
label: "Claude CLI",
command: ["claude"],
},
});
setActivePluginRegistry(registry);
expect(isCliProvider("claude-cli", {} as OpenClawConfig)).toBe(true);
});
});

View File

@ -1,4 +1,3 @@
import { CLAUDE_CLI_BACKEND_ID } from "../../extensions/anthropic/cli-backend-api.js";
import { resolveThinkingDefaultForModel } from "../auto-reply/thinking.shared.js";
import type { OpenClawConfig } from "../config/config.js";
import {
@ -29,8 +28,6 @@ import { normalizeProviderModelIdWithRuntime } from "./provider-model-normalizat
let log: ReturnType<typeof createSubsystemLogger> | null = null;
const BUILTIN_CLI_PROVIDER_IDS = new Set([normalizeProviderId(CLAUDE_CLI_BACKEND_ID)]);
function getLog(): ReturnType<typeof createSubsystemLogger> {
log ??= createSubsystemLogger("model-selection");
return log;
@ -92,9 +89,6 @@ export {
export function isCliProvider(provider: string, cfg?: OpenClawConfig): boolean {
const normalized = normalizeProviderId(provider);
if (BUILTIN_CLI_PROVIDER_IDS.has(normalized)) {
return true;
}
const cliBackends = resolveRuntimeCliBackends();
if (cliBackends.some((backend) => normalizeProviderId(backend.id) === normalized)) {
return true;

View File

@ -6,12 +6,6 @@ import {
DefaultResourceLoader,
SessionManager,
} from "@mariozechner/pi-coding-agent";
import {
isOllamaCompatProvider,
resolveOllamaCompatNumCtxEnabled,
shouldInjectOllamaCompatNumCtx,
wrapOllamaCompatNumCtx,
} from "../../../../extensions/ollama/runtime-api.js";
import { resolveHeartbeatPrompt } from "../../../auto-reply/heartbeat.js";
import { resolveChannelCapabilities } from "../../../config/channel-capabilities.js";
import { getMachineDisplayName } from "../../../infra/machine-name.js";
@ -20,6 +14,12 @@ import {
ensureGlobalUndiciStreamTimeouts,
} from "../../../infra/net/undici-global-dispatcher.js";
import { MAX_IMAGE_BYTES } from "../../../media/constants.js";
import {
isOllamaCompatProvider,
resolveOllamaCompatNumCtxEnabled,
shouldInjectOllamaCompatNumCtx,
wrapOllamaCompatNumCtx,
} from "../../../plugin-sdk/ollama-runtime.js";
import { getGlobalHookRunner } from "../../../plugins/hook-runner-global.js";
import { resolveToolCallArgumentsEncoding } from "../../../plugins/provider-model-compat.js";
import { resolveProviderSystemPromptContribution } from "../../../plugins/provider-runtime.js";
@ -219,7 +219,7 @@ export {
resolveOllamaCompatNumCtxEnabled,
shouldInjectOllamaCompatNumCtx,
wrapOllamaCompatNumCtx,
} from "../../../../extensions/ollama/runtime-api.js";
} from "../../../plugin-sdk/ollama-runtime.js";
export {
decodeHtmlEntitiesInObject,
wrapStreamFnRepairMalformedToolCallArguments,

View File

@ -1,11 +1,4 @@
import fsSync from "node:fs";
import {
auditShortTermPromotionArtifacts,
getBuiltinMemoryEmbeddingProviderDoctorMetadata,
listBuiltinAutoSelectMemoryEmbeddingProviderDoctorMetadata,
repairShortTermPromotionArtifacts,
type ShortTermAuditSummary,
} from "../../extensions/memory-core/runtime-api.js";
import {
resolveAgentDir,
resolveAgentWorkspaceDir,
@ -18,6 +11,13 @@ import type { OpenClawConfig } from "../config/config.js";
import { DEFAULT_LOCAL_MODEL } from "../memory-host-sdk/engine-embeddings.js";
import { checkQmdBinaryAvailability } from "../memory-host-sdk/engine-qmd.js";
import { hasConfiguredMemorySecretInput } from "../memory-host-sdk/secret.js";
import {
auditShortTermPromotionArtifacts,
getBuiltinMemoryEmbeddingProviderDoctorMetadata,
listBuiltinAutoSelectMemoryEmbeddingProviderDoctorMetadata,
repairShortTermPromotionArtifacts,
type ShortTermAuditSummary,
} from "../plugin-sdk/memory-core-engine-runtime.js";
import {
getActiveMemorySearchManager,
resolveActiveMemoryBackendConfig,

View File

@ -1 +1 @@
export { redactCdpUrl } from "../../extensions/browser/browser-cdp.js";
export { redactCdpUrl } from "./browser-config.js";

View File

@ -1,5 +1,18 @@
export type { BrowserControlAuth } from "../../extensions/browser/browser-control-auth.js";
export {
ensureBrowserControlAuth,
resolveBrowserControlAuth,
} from "../../extensions/browser/browser-control-auth.js";
export type { BrowserControlAuth } from "./browser-config.js";
export { resolveBrowserControlAuth } from "./browser-config.js";
type BrowserControlAuthModule = typeof import("@openclaw/browser/browser-control-auth.js");
import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js";
function loadBrowserControlAuthModule(): BrowserControlAuthModule {
return loadBundledPluginPublicSurfaceModuleSync<BrowserControlAuthModule>({
dirName: "browser",
artifactBasename: "browser-control-auth.js",
});
}
export const ensureBrowserControlAuth: BrowserControlAuthModule["ensureBrowserControlAuth"] = ((
...args
) =>
loadBrowserControlAuthModule().ensureBrowserControlAuth(
...args,
)) as BrowserControlAuthModule["ensureBrowserControlAuth"];

View File

@ -18,3 +18,19 @@ export const createOllamaEmbeddingProvider: FacadeModule["createOllamaEmbeddingP
loadFacadeModule().createOllamaEmbeddingProvider(
...args,
)) as FacadeModule["createOllamaEmbeddingProvider"];
export const isOllamaCompatProvider: FacadeModule["isOllamaCompatProvider"] = ((...args) =>
loadFacadeModule().isOllamaCompatProvider(...args)) as FacadeModule["isOllamaCompatProvider"];
export const resolveOllamaCompatNumCtxEnabled: FacadeModule["resolveOllamaCompatNumCtxEnabled"] = ((
...args
) =>
loadFacadeModule().resolveOllamaCompatNumCtxEnabled(
...args,
)) as FacadeModule["resolveOllamaCompatNumCtxEnabled"];
export const shouldInjectOllamaCompatNumCtx: FacadeModule["shouldInjectOllamaCompatNumCtx"] = ((
...args
) =>
loadFacadeModule().shouldInjectOllamaCompatNumCtx(
...args,
)) as FacadeModule["shouldInjectOllamaCompatNumCtx"];
export const wrapOllamaCompatNumCtx: FacadeModule["wrapOllamaCompatNumCtx"] = ((...args) =>
loadFacadeModule().wrapOllamaCompatNumCtx(...args)) as FacadeModule["wrapOllamaCompatNumCtx"];

View File

@ -0,0 +1,81 @@
// Manual facade. Keep loader boundary explicit.
type FacadeModule = typeof import("@openclaw/speech-core/runtime-api.js");
import {
createLazyFacadeObjectValue,
loadActivatedBundledPluginPublicSurfaceModuleSync,
} from "./facade-runtime.js";
function loadFacadeModule(): FacadeModule {
return loadActivatedBundledPluginPublicSurfaceModuleSync<FacadeModule>({
dirName: "speech-core",
artifactBasename: "runtime-api.js",
});
}
export const _test: FacadeModule["_test"] = createLazyFacadeObjectValue(
() => loadFacadeModule()._test,
);
export const buildTtsSystemPromptHint: FacadeModule["buildTtsSystemPromptHint"] =
createLazyFacadeValue("buildTtsSystemPromptHint");
export const getLastTtsAttempt: FacadeModule["getLastTtsAttempt"] =
createLazyFacadeValue("getLastTtsAttempt");
export const getResolvedSpeechProviderConfig: FacadeModule["getResolvedSpeechProviderConfig"] =
createLazyFacadeValue("getResolvedSpeechProviderConfig");
export const getTtsMaxLength: FacadeModule["getTtsMaxLength"] =
createLazyFacadeValue("getTtsMaxLength");
export const getTtsProvider: FacadeModule["getTtsProvider"] =
createLazyFacadeValue("getTtsProvider");
export const isSummarizationEnabled: FacadeModule["isSummarizationEnabled"] =
createLazyFacadeValue("isSummarizationEnabled");
export const isTtsEnabled: FacadeModule["isTtsEnabled"] = createLazyFacadeValue("isTtsEnabled");
export const isTtsProviderConfigured: FacadeModule["isTtsProviderConfigured"] =
createLazyFacadeValue("isTtsProviderConfigured");
export const listSpeechVoices: FacadeModule["listSpeechVoices"] =
createLazyFacadeValue("listSpeechVoices");
export const maybeApplyTtsToPayload: FacadeModule["maybeApplyTtsToPayload"] =
createLazyFacadeValue("maybeApplyTtsToPayload");
export const resolveTtsAutoMode: FacadeModule["resolveTtsAutoMode"] =
createLazyFacadeValue("resolveTtsAutoMode");
export const resolveTtsConfig: FacadeModule["resolveTtsConfig"] =
createLazyFacadeValue("resolveTtsConfig");
export const resolveTtsPrefsPath: FacadeModule["resolveTtsPrefsPath"] =
createLazyFacadeValue("resolveTtsPrefsPath");
export const resolveTtsProviderOrder: FacadeModule["resolveTtsProviderOrder"] =
createLazyFacadeValue("resolveTtsProviderOrder");
export const setLastTtsAttempt: FacadeModule["setLastTtsAttempt"] =
createLazyFacadeValue("setLastTtsAttempt");
export const setSummarizationEnabled: FacadeModule["setSummarizationEnabled"] =
createLazyFacadeValue("setSummarizationEnabled");
export const setTtsAutoMode: FacadeModule["setTtsAutoMode"] =
createLazyFacadeValue("setTtsAutoMode");
export const setTtsEnabled: FacadeModule["setTtsEnabled"] = createLazyFacadeValue("setTtsEnabled");
export const setTtsMaxLength: FacadeModule["setTtsMaxLength"] =
createLazyFacadeValue("setTtsMaxLength");
export const setTtsProvider: FacadeModule["setTtsProvider"] =
createLazyFacadeValue("setTtsProvider");
export const synthesizeSpeech: FacadeModule["synthesizeSpeech"] =
createLazyFacadeValue("synthesizeSpeech");
export const textToSpeech: FacadeModule["textToSpeech"] = createLazyFacadeValue("textToSpeech");
export const textToSpeechTelephony: FacadeModule["textToSpeechTelephony"] =
createLazyFacadeValue("textToSpeechTelephony");
export type ResolvedTtsConfig = import("@openclaw/speech-core/runtime-api.js").ResolvedTtsConfig;
export type ResolvedTtsModelOverrides =
import("@openclaw/speech-core/runtime-api.js").ResolvedTtsModelOverrides;
export type TtsDirectiveOverrides =
import("@openclaw/speech-core/runtime-api.js").TtsDirectiveOverrides;
export type TtsDirectiveParseResult =
import("@openclaw/speech-core/runtime-api.js").TtsDirectiveParseResult;
export type TtsResult = import("@openclaw/speech-core/runtime-api.js").TtsResult;
export type TtsSynthesisResult = import("@openclaw/speech-core/runtime-api.js").TtsSynthesisResult;
export type TtsTelephonyResult = import("@openclaw/speech-core/runtime-api.js").TtsTelephonyResult;
function createLazyFacadeValue<K extends keyof FacadeModule>(key: K): FacadeModule[K] {
return ((...args: unknown[]) => {
const value = loadFacadeModule()[key];
if (typeof value !== "function") {
return value;
}
return (value as (...innerArgs: unknown[]) => unknown)(...args);
}) as FacadeModule[K];
}

View File

@ -30,4 +30,4 @@ export {
type TtsResult,
type TtsSynthesisResult,
type TtsTelephonyResult,
} from "../../extensions/speech-core/runtime-api.js";
} from "../plugin-sdk/tts-runtime.js";