feat: Add first-class infer CLI for inference workflows (#62129)

* refresh infer branch onto latest main

* flatten infer media commands

* fix tts runtime facade export

* validate explicit web search providers

* fix infer auth logout persistence
This commit is contained in:
Tak Hoffman
2026-04-07 07:11:19 -05:00
committed by GitHub
parent dfb6c9c920
commit 97c031a8db
28 changed files with 3724 additions and 28 deletions

View File

@@ -25,8 +25,8 @@ import type { ReplyPayload } from "openclaw/plugin-sdk/reply-runtime";
import { isVerbose, logVerbose } from "openclaw/plugin-sdk/runtime-env";
import { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/sandbox";
import {
CONFIG_DIR,
normalizeOptionalString,
resolveConfigDir,
resolveUserPath,
stripMarkdown,
} from "openclaw/plugin-sdk/text-runtime";
@@ -41,6 +41,7 @@ import {
summarizeText,
type SpeechModelOverridePolicy,
type SpeechProviderConfig,
type SpeechProviderOverrides,
type SpeechVoiceOption,
type TtsDirectiveOverrides,
type TtsDirectiveParseResult,
@@ -173,7 +174,7 @@ function resolveTtsPrefsPathValue(prefsPath: string | undefined): string {
if (envPath) {
return resolveUserPath(envPath);
}
return path.join(CONFIG_DIR, "settings", "tts.json");
return path.join(resolveConfigDir(process.env), "settings", "tts.json");
}
function resolveModelOverridePolicy(
@@ -502,6 +503,66 @@ export function setTtsProvider(prefsPath: string, provider: TtsProvider): void {
});
}
export function resolveExplicitTtsOverrides(params: {
cfg: OpenClawConfig;
prefsPath?: string;
provider?: string;
modelId?: string;
voiceId?: string;
}): TtsDirectiveOverrides {
const providerInput = params.provider?.trim();
const modelId = params.modelId?.trim();
const voiceId = params.voiceId?.trim();
const config = resolveTtsConfig(params.cfg);
const prefsPath = params.prefsPath ?? resolveTtsPrefsPath(config);
const selectedProvider =
canonicalizeSpeechProviderId(providerInput, params.cfg) ??
(modelId || voiceId ? getTtsProvider(config, prefsPath) : undefined);
if (providerInput && !selectedProvider) {
throw new Error(`Unknown TTS provider "${providerInput}".`);
}
if (!modelId && !voiceId) {
return selectedProvider ? { provider: selectedProvider } : {};
}
if (!selectedProvider) {
throw new Error("TTS model or voice overrides require a resolved provider.");
}
const provider = getSpeechProvider(selectedProvider, params.cfg);
if (!provider) {
throw new Error(`speech provider ${selectedProvider} is not registered`);
}
if (!provider.resolveTalkOverrides) {
throw new Error(
`TTS provider "${selectedProvider}" does not support model or voice overrides.`,
);
}
const providerOverrides = provider.resolveTalkOverrides({
talkProviderConfig: {},
params: {
...(voiceId ? { voiceId } : {}),
...(modelId ? { modelId } : {}),
},
});
if ((voiceId || modelId) && (!providerOverrides || Object.keys(providerOverrides).length === 0)) {
throw new Error(
`TTS provider "${selectedProvider}" ignored the requested model or voice overrides.`,
);
}
const overridesRecord = providerOverrides as SpeechProviderOverrides;
return {
provider: selectedProvider,
providerOverrides: {
[provider.id]: overridesRecord,
},
};
}
export function getTtsMaxLength(prefsPath: string): number {
const prefs = readPrefs(prefsPath);
return prefs.tts?.maxLength ?? DEFAULT_TTS_MAX_LENGTH;