mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-16 17:20:46 +00:00
Reduce WebUI/Gateway latency churn by avoiding redundant session reloads, carrying session keys through transcript update events, and deferring explicit media provider discovery. Includes changelog attribution and closes the referenced runtime latency issues.
140 lines
4.9 KiB
TypeScript
140 lines
4.9 KiB
TypeScript
import type { FallbackAttempt } from "../agents/model-fallback.types.js";
|
|
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
|
import { createSubsystemLogger } from "../logging/subsystem.js";
|
|
import {
|
|
buildMediaGenerationNormalizationMetadata,
|
|
buildNoCapabilityModelConfiguredMessage,
|
|
recordCapabilityCandidateFailure,
|
|
resolveCapabilityModelCandidates,
|
|
throwCapabilityGenerationFailure,
|
|
} from "../media-generation/runtime-shared.js";
|
|
import { getProviderEnvVars } from "../secrets/provider-env-vars.js";
|
|
import { parseMusicGenerationModelRef } from "./model-ref.js";
|
|
import { resolveMusicGenerationOverrides } from "./normalization.js";
|
|
import { getMusicGenerationProvider, listMusicGenerationProviders } from "./provider-registry.js";
|
|
import type { GenerateMusicParams, GenerateMusicRuntimeResult } from "./runtime-types.js";
|
|
import type { MusicGenerationResult } from "./types.js";
|
|
|
|
const log = createSubsystemLogger("music-generation");
|
|
|
|
export type MusicGenerationRuntimeDeps = {
|
|
getProvider?: typeof getMusicGenerationProvider;
|
|
listProviders?: typeof listMusicGenerationProviders;
|
|
getProviderEnvVars?: typeof getProviderEnvVars;
|
|
log?: Pick<typeof log, "debug">;
|
|
};
|
|
|
|
export type { GenerateMusicParams, GenerateMusicRuntimeResult } from "./runtime-types.js";
|
|
|
|
export function listRuntimeMusicGenerationProviders(
|
|
params?: { config?: OpenClawConfig },
|
|
deps: MusicGenerationRuntimeDeps = {},
|
|
) {
|
|
return (deps.listProviders ?? listMusicGenerationProviders)(params?.config);
|
|
}
|
|
|
|
export async function generateMusic(
|
|
params: GenerateMusicParams,
|
|
deps: MusicGenerationRuntimeDeps = {},
|
|
): Promise<GenerateMusicRuntimeResult> {
|
|
const getProvider = deps.getProvider ?? getMusicGenerationProvider;
|
|
const listProviders = deps.listProviders ?? listMusicGenerationProviders;
|
|
const logger = deps.log ?? log;
|
|
const candidates = resolveCapabilityModelCandidates({
|
|
cfg: params.cfg,
|
|
modelConfig: params.cfg.agents?.defaults?.musicGenerationModel,
|
|
modelOverride: params.modelOverride,
|
|
parseModelRef: parseMusicGenerationModelRef,
|
|
agentDir: params.agentDir,
|
|
listProviders,
|
|
autoProviderFallback: params.autoProviderFallback,
|
|
});
|
|
if (candidates.length === 0) {
|
|
throw new Error(
|
|
buildNoCapabilityModelConfiguredMessage({
|
|
capabilityLabel: "music-generation",
|
|
modelConfigKey: "musicGenerationModel",
|
|
providers: listProviders(params.cfg),
|
|
fallbackSampleRef: "google/lyria-3-clip-preview",
|
|
getProviderEnvVars: deps.getProviderEnvVars,
|
|
}),
|
|
);
|
|
}
|
|
|
|
const attempts: FallbackAttempt[] = [];
|
|
let lastError: unknown;
|
|
|
|
for (const candidate of candidates) {
|
|
const provider = getProvider(candidate.provider, params.cfg);
|
|
if (!provider) {
|
|
const error = `No music-generation provider registered for ${candidate.provider}`;
|
|
attempts.push({
|
|
provider: candidate.provider,
|
|
model: candidate.model,
|
|
error,
|
|
});
|
|
lastError = new Error(error);
|
|
continue;
|
|
}
|
|
|
|
try {
|
|
const sanitized = resolveMusicGenerationOverrides({
|
|
provider,
|
|
model: candidate.model,
|
|
lyrics: params.lyrics,
|
|
instrumental: params.instrumental,
|
|
durationSeconds: params.durationSeconds,
|
|
format: params.format,
|
|
inputImages: params.inputImages,
|
|
});
|
|
const result: MusicGenerationResult = await provider.generateMusic({
|
|
provider: candidate.provider,
|
|
model: candidate.model,
|
|
prompt: params.prompt,
|
|
cfg: params.cfg,
|
|
agentDir: params.agentDir,
|
|
authStore: params.authStore,
|
|
lyrics: sanitized.lyrics,
|
|
instrumental: sanitized.instrumental,
|
|
durationSeconds: sanitized.durationSeconds,
|
|
format: sanitized.format,
|
|
inputImages: params.inputImages,
|
|
...(params.timeoutMs !== undefined ? { timeoutMs: params.timeoutMs } : {}),
|
|
});
|
|
if (!Array.isArray(result.tracks) || result.tracks.length === 0) {
|
|
throw new Error("Music generation provider returned no tracks.");
|
|
}
|
|
return {
|
|
tracks: result.tracks,
|
|
provider: candidate.provider,
|
|
model: result.model ?? candidate.model,
|
|
attempts,
|
|
lyrics: result.lyrics,
|
|
normalization: sanitized.normalization,
|
|
metadata: {
|
|
...result.metadata,
|
|
...buildMediaGenerationNormalizationMetadata({
|
|
normalization: sanitized.normalization,
|
|
}),
|
|
},
|
|
ignoredOverrides: sanitized.ignoredOverrides,
|
|
};
|
|
} catch (err) {
|
|
lastError = err;
|
|
recordCapabilityCandidateFailure({
|
|
attempts,
|
|
provider: candidate.provider,
|
|
model: candidate.model,
|
|
error: err,
|
|
});
|
|
logger.debug(`music-generation candidate failed: ${candidate.provider}/${candidate.model}`);
|
|
}
|
|
}
|
|
|
|
return throwCapabilityGenerationFailure({
|
|
capabilityLabel: "music generation",
|
|
attempts,
|
|
lastError,
|
|
});
|
|
}
|