mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-02 19:15:16 +00:00
refactor: share fal provider http auth
This commit is contained in:
48
extensions/fal/http-config.ts
Normal file
48
extensions/fal/http-config.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { type AuthProfileStore, type OpenClawConfig } from "openclaw/plugin-sdk/provider-auth";
|
||||
import { resolveApiKeyForProvider } from "openclaw/plugin-sdk/provider-auth-runtime";
|
||||
import {
|
||||
resolveProviderHttpRequestConfig,
|
||||
type ProviderRequestCapability,
|
||||
} from "openclaw/plugin-sdk/provider-http";
|
||||
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||
|
||||
const DEFAULT_FAL_BASE_URL = "https://fal.run";
|
||||
|
||||
type FalAuthenticatedRequest = {
|
||||
cfg?: OpenClawConfig;
|
||||
agentDir?: string;
|
||||
authStore?: AuthProfileStore;
|
||||
};
|
||||
|
||||
function resolveFalConfiguredBaseUrl(cfg?: OpenClawConfig): string | undefined {
|
||||
return normalizeOptionalString(cfg?.models?.providers?.fal?.baseUrl);
|
||||
}
|
||||
|
||||
export async function resolveFalHttpRequestConfig(params: {
|
||||
req: FalAuthenticatedRequest;
|
||||
baseUrl?: string;
|
||||
capability: ProviderRequestCapability;
|
||||
}): Promise<ReturnType<typeof resolveProviderHttpRequestConfig>> {
|
||||
const auth = await resolveApiKeyForProvider({
|
||||
provider: "fal",
|
||||
cfg: params.req.cfg,
|
||||
agentDir: params.req.agentDir,
|
||||
store: params.req.authStore,
|
||||
});
|
||||
if (!auth.apiKey) {
|
||||
throw new Error("fal API key missing");
|
||||
}
|
||||
|
||||
return resolveProviderHttpRequestConfig({
|
||||
baseUrl: params.baseUrl ?? resolveFalConfiguredBaseUrl(params.req.cfg),
|
||||
defaultBaseUrl: DEFAULT_FAL_BASE_URL,
|
||||
allowPrivateNetwork: false,
|
||||
defaultHeaders: {
|
||||
Authorization: `Key ${auth.apiKey}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
provider: "fal",
|
||||
capability: params.capability,
|
||||
transport: "http",
|
||||
});
|
||||
}
|
||||
@@ -8,11 +8,9 @@ import {
|
||||
toImageDataUrl,
|
||||
} from "openclaw/plugin-sdk/image-generation";
|
||||
import { isProviderApiKeyConfigured } from "openclaw/plugin-sdk/provider-auth";
|
||||
import { resolveApiKeyForProvider } from "openclaw/plugin-sdk/provider-auth-runtime";
|
||||
import {
|
||||
assertOkOrThrowHttpError,
|
||||
assertOkOrThrowProviderError,
|
||||
resolveProviderHttpRequestConfig,
|
||||
} from "openclaw/plugin-sdk/provider-http";
|
||||
import {
|
||||
buildHostnameAllowlistPolicyFromSuffixAllowlist,
|
||||
@@ -26,8 +24,8 @@ import {
|
||||
normalizeLowercaseStringOrEmpty,
|
||||
normalizeOptionalString,
|
||||
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||
import { resolveFalHttpRequestConfig } from "./http-config.js";
|
||||
|
||||
const DEFAULT_FAL_BASE_URL = "https://fal.run";
|
||||
const DEFAULT_FAL_IMAGE_MODEL = "fal-ai/flux/dev";
|
||||
const DEFAULT_FAL_EDIT_SUBPATH = "image-to-image";
|
||||
const FAL_KREA_2_MODEL_PREFIX = "krea/v2/";
|
||||
@@ -551,15 +549,6 @@ export function buildFalImageGenerationProvider(): ImageGenerationProvider {
|
||||
},
|
||||
},
|
||||
async generateImage(req) {
|
||||
const auth = await resolveApiKeyForProvider({
|
||||
provider: "fal",
|
||||
cfg: req.cfg,
|
||||
agentDir: req.agentDir,
|
||||
store: req.authStore,
|
||||
});
|
||||
if (!auth.apiKey) {
|
||||
throw new Error("fal API key missing");
|
||||
}
|
||||
const inputImageCount = req.inputImages?.length ?? 0;
|
||||
const hasInputImages = inputImageCount > 0;
|
||||
const requestedModel = req.model?.trim() || DEFAULT_FAL_IMAGE_MODEL;
|
||||
@@ -588,20 +577,8 @@ export function buildFalImageGenerationProvider(): ImageGenerationProvider {
|
||||
if (!schema.supportsOutputFormat && req.outputFormat) {
|
||||
throw new Error(`fal ${requestedModel} does not support outputFormat overrides`);
|
||||
}
|
||||
const explicitBaseUrl = req.cfg?.models?.providers?.fal?.baseUrl?.trim();
|
||||
const { baseUrl, allowPrivateNetwork, headers, dispatcherPolicy } =
|
||||
resolveProviderHttpRequestConfig({
|
||||
baseUrl: explicitBaseUrl,
|
||||
defaultBaseUrl: DEFAULT_FAL_BASE_URL,
|
||||
allowPrivateNetwork: false,
|
||||
defaultHeaders: {
|
||||
Authorization: `Key ${auth.apiKey}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
provider: "fal",
|
||||
capability: "image",
|
||||
transport: "http",
|
||||
});
|
||||
await resolveFalHttpRequestConfig({ req, capability: "image" });
|
||||
const networkPolicy = resolveFalNetworkPolicy({ baseUrl, allowPrivateNetwork });
|
||||
const requestBody: Record<string, unknown> = {
|
||||
prompt: req.prompt,
|
||||
|
||||
@@ -5,15 +5,10 @@ import {
|
||||
type MusicGenerationRequest,
|
||||
} from "openclaw/plugin-sdk/music-generation";
|
||||
import { isProviderApiKeyConfigured } from "openclaw/plugin-sdk/provider-auth";
|
||||
import { resolveApiKeyForProvider } from "openclaw/plugin-sdk/provider-auth-runtime";
|
||||
import {
|
||||
assertOkOrThrowHttpError,
|
||||
postJsonRequest,
|
||||
resolveProviderHttpRequestConfig,
|
||||
} from "openclaw/plugin-sdk/provider-http";
|
||||
import { assertOkOrThrowHttpError, postJsonRequest } from "openclaw/plugin-sdk/provider-http";
|
||||
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||
import { resolveFalHttpRequestConfig } from "./http-config.js";
|
||||
|
||||
const DEFAULT_FAL_BASE_URL = "https://fal.run";
|
||||
const DEFAULT_FAL_MUSIC_MODEL = "fal-ai/minimax-music/v2.6";
|
||||
const FAL_ACE_STEP_MODEL = "fal-ai/ace-step/prompt-to-audio";
|
||||
const FAL_STABLE_AUDIO_MODEL = "fal-ai/stable-audio-25/text-to-audio";
|
||||
@@ -29,10 +24,6 @@ function resolveFalMusicModel(model: string | undefined): string {
|
||||
return normalizeOptionalString(model) ?? DEFAULT_FAL_MUSIC_MODEL;
|
||||
}
|
||||
|
||||
function resolveFalMusicBaseUrl(req: MusicGenerationRequest): string | undefined {
|
||||
return normalizeOptionalString(req.cfg?.models?.providers?.fal?.baseUrl);
|
||||
}
|
||||
|
||||
function buildFalMinimaxBody(req: MusicGenerationRequest): Record<string, unknown> {
|
||||
const lyrics = normalizeOptionalString(req.lyrics);
|
||||
if (lyrics && req.instrumental === true) {
|
||||
@@ -145,29 +136,8 @@ export function buildFalMusicGenerationProvider(): MusicGenerationProvider {
|
||||
throw new Error("fal music generation does not support image reference inputs.");
|
||||
}
|
||||
|
||||
const auth = await resolveApiKeyForProvider({
|
||||
provider: "fal",
|
||||
cfg: req.cfg,
|
||||
agentDir: req.agentDir,
|
||||
store: req.authStore,
|
||||
});
|
||||
if (!auth.apiKey) {
|
||||
throw new Error("fal API key missing");
|
||||
}
|
||||
|
||||
const { baseUrl, allowPrivateNetwork, headers, dispatcherPolicy } =
|
||||
resolveProviderHttpRequestConfig({
|
||||
baseUrl: resolveFalMusicBaseUrl(req),
|
||||
defaultBaseUrl: DEFAULT_FAL_BASE_URL,
|
||||
allowPrivateNetwork: false,
|
||||
defaultHeaders: {
|
||||
Authorization: `Key ${auth.apiKey}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
provider: "fal",
|
||||
capability: "audio",
|
||||
transport: "http",
|
||||
});
|
||||
await resolveFalHttpRequestConfig({ req, capability: "audio" });
|
||||
const model = resolveFalMusicModel(req.model);
|
||||
const { response, release } = await postJsonRequest({
|
||||
url: `${baseUrl}/${model}`,
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import { extensionForMime } from "openclaw/plugin-sdk/media-mime";
|
||||
import { isProviderApiKeyConfigured } from "openclaw/plugin-sdk/provider-auth";
|
||||
import { resolveApiKeyForProvider } from "openclaw/plugin-sdk/provider-auth-runtime";
|
||||
import {
|
||||
assertOkOrThrowHttpError,
|
||||
resolveProviderHttpRequestConfig,
|
||||
} from "openclaw/plugin-sdk/provider-http";
|
||||
import { assertOkOrThrowHttpError } from "openclaw/plugin-sdk/provider-http";
|
||||
import {
|
||||
fetchWithSsrFGuard,
|
||||
type SsrFPolicy,
|
||||
@@ -20,8 +16,8 @@ import type {
|
||||
VideoGenerationProvider,
|
||||
VideoGenerationRequest,
|
||||
} from "openclaw/plugin-sdk/video-generation";
|
||||
import { resolveFalHttpRequestConfig } from "./http-config.js";
|
||||
|
||||
const DEFAULT_FAL_BASE_URL = "https://fal.run";
|
||||
const DEFAULT_FAL_QUEUE_BASE_URL = "https://queue.fal.run";
|
||||
const DEFAULT_FAL_VIDEO_MODEL = "fal-ai/minimax/video-01-live";
|
||||
const HEYGEN_VIDEO_AGENT_MODEL = "fal-ai/heygen/v2/video-agent";
|
||||
@@ -573,28 +569,8 @@ export function buildFalVideoGenerationProvider(): VideoGenerationProvider {
|
||||
async generateVideo(req) {
|
||||
const model = normalizeOptionalString(req.model) || DEFAULT_FAL_VIDEO_MODEL;
|
||||
validateFalVideoReferenceInputs({ req, model });
|
||||
const auth = await resolveApiKeyForProvider({
|
||||
provider: "fal",
|
||||
cfg: req.cfg,
|
||||
agentDir: req.agentDir,
|
||||
store: req.authStore,
|
||||
});
|
||||
if (!auth.apiKey) {
|
||||
throw new Error("fal API key missing");
|
||||
}
|
||||
const { baseUrl, allowPrivateNetwork, headers, dispatcherPolicy } =
|
||||
resolveProviderHttpRequestConfig({
|
||||
baseUrl: normalizeOptionalString(req.cfg?.models?.providers?.fal?.baseUrl),
|
||||
defaultBaseUrl: DEFAULT_FAL_BASE_URL,
|
||||
allowPrivateNetwork: false,
|
||||
defaultHeaders: {
|
||||
Authorization: `Key ${auth.apiKey}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
provider: "fal",
|
||||
capability: "video",
|
||||
transport: "http",
|
||||
});
|
||||
await resolveFalHttpRequestConfig({ req, capability: "video" });
|
||||
const requestBody = buildFalVideoRequestBody({ req, model });
|
||||
const policy = buildPolicy(allowPrivateNetwork);
|
||||
const queueBaseUrl = resolveFalQueueBaseUrl(baseUrl);
|
||||
|
||||
Reference in New Issue
Block a user