{
if (!tool.hosting) return { isUsingHostedKey: false }
if (!isHosted) return { isUsingHostedKey: false }
+ if (tool.hosting.enabled && !tool.hosting.enabled(params)) {
+ return { isUsingHostedKey: false }
+ }
const { envKeyPrefix, apiKeyParam, byokProviderId, rateLimit } = tool.hosting
+ const userProvidedKey = params[apiKeyParam]
+ if (typeof userProvidedKey === 'string' && userProvidedKey.trim().length > 0) {
+ return { isUsingHostedKey: false }
+ }
const { workspaceId, userId, workflowId } = resolveToolScope(params, executionContext)
@@ -299,6 +306,7 @@ async function injectHostedKeyIfNeeded(
}
params[apiKeyParam] = acquireResult.key
+ params.__usingHostedKey = true
logger.info(`[${requestId}] Using hosted key for ${tool.id} (${acquireResult.envVarName})`, {
keyIndex: acquireResult.keyIndex,
provider,
diff --git a/apps/sim/tools/types.ts b/apps/sim/tools/types.ts
index 5cd428c941e..6b39f863954 100644
--- a/apps/sim/tools/types.ts
+++ b/apps/sim/tools/types.ts
@@ -8,6 +8,7 @@ export type BYOKProviderId =
| 'google'
| 'mistral'
| 'fireworks'
+ | 'falai'
| 'firecrawl'
| 'exa'
| 'serper'
@@ -307,6 +308,8 @@ export type ToolHostingPricing> = PerRequestPricing
* no code changes needed.
*/
interface ToolHostingConfig
> {
+ /** Optional predicate for tools where hosted keys only apply to some parameter combinations. */
+ enabled?: (params: P) => boolean
/**
* Env var name prefix for hosted keys.
* At runtime, `{envKeyPrefix}_COUNT` is read to determine how many keys exist,
diff --git a/apps/sim/tools/video/falai.ts b/apps/sim/tools/video/falai.ts
index 77999d87820..b2314472f4d 100644
--- a/apps/sim/tools/video/falai.ts
+++ b/apps/sim/tools/video/falai.ts
@@ -1,3 +1,4 @@
+import { FALAI_HOSTED_KEY_MARKUP_MULTIPLIER } from '@/lib/tools/falai-pricing'
import type { ToolConfig } from '@/tools/types'
import type { VideoParams, VideoResponse } from '@/tools/video/types'
import { parseBooleanParam, parseBooleanParamWithDefault } from '@/tools/video/utils'
@@ -68,6 +69,37 @@ export const falaiVideoTool: ToolConfig = {
},
},
+ hosting: {
+ envKeyPrefix: 'FALAI_API_KEY',
+ apiKeyParam: 'apiKey',
+ byokProviderId: 'falai',
+ pricing: {
+ type: 'custom',
+ getCost: (_params, output) => {
+ const providerCostDollars = output.__falaiCostDollars
+ if (typeof providerCostDollars !== 'number' || Number.isNaN(providerCostDollars)) {
+ throw new Error('Fal.ai video response missing cost data')
+ }
+
+ return {
+ cost: providerCostDollars * FALAI_HOSTED_KEY_MARKUP_MULTIPLIER,
+ metadata: {
+ ...(typeof output.__falaiBilling === 'object' && output.__falaiBilling !== null
+ ? (output.__falaiBilling as Record)
+ : {}),
+ providerCostDollars,
+ markupMultiplier: FALAI_HOSTED_KEY_MARKUP_MULTIPLIER,
+ },
+ }
+ },
+ },
+ rateLimit: {
+ mode: 'per_request',
+ requestsPerMinute: 40,
+ burstMultiplier: 1,
+ },
+ },
+
request: {
url: '/api/tools/video',
method: 'POST',
@@ -77,6 +109,7 @@ export const falaiVideoTool: ToolConfig = {
body: (
params: VideoParams & {
_context?: { workspaceId?: string; workflowId?: string; executionId?: string }
+ __usingHostedKey?: boolean
}
) => ({
provider: 'falai',
@@ -91,6 +124,7 @@ export const falaiVideoTool: ToolConfig = {
workspaceId: params._context?.workspaceId,
workflowId: params._context?.workflowId,
executionId: params._context?.executionId,
+ useHostedCostTracking: params.__usingHostedKey === true,
}),
},
@@ -128,6 +162,8 @@ export const falaiVideoTool: ToolConfig = {
provider: 'falai',
model: data.model,
jobId: data.jobId,
+ __falaiCostDollars: data.__falaiCostDollars,
+ __falaiBilling: data.__falaiBilling,
},
}
},
diff --git a/apps/sim/tools/video/types.ts b/apps/sim/tools/video/types.ts
index 5d31e29e2c7..861e9eabec4 100644
--- a/apps/sim/tools/video/types.ts
+++ b/apps/sim/tools/video/types.ts
@@ -33,6 +33,17 @@ export interface VideoResponse extends ToolResponse {
provider?: string
model?: string
jobId?: string
+ __falaiCostDollars?: number
+ __falaiBilling?: {
+ endpointId: string
+ requestId: string
+ source: 'billing_events' | 'historical_estimate' | 'fallback_floor'
+ outputUnits?: number | null
+ unitPrice?: number | null
+ percentDiscount?: number | null
+ currency?: string
+ error?: string
+ }
}
}