diff --git a/apps/sim/app/workspace/[workspaceId]/logs/components/sidebar/sidebar.tsx b/apps/sim/app/workspace/[workspaceId]/logs/components/sidebar/sidebar.tsx index c8bce63b7..eaeb89c49 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/components/sidebar/sidebar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/logs/components/sidebar/sidebar.tsx @@ -6,6 +6,7 @@ import { Button } from '@/components/ui/button' import { CopyButton } from '@/components/ui/copy-button' import { ScrollArea } from '@/components/ui/scroll-area' import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip' +import { BASE_EXECUTION_CHARGE } from '@/lib/billing/constants' import { redactApiKeys } from '@/lib/utils' import { FrozenCanvasModal } from '@/app/workspace/[workspaceId]/logs/components/frozen-canvas/frozen-canvas-modal' import LogMarkdownRenderer from '@/app/workspace/[workspaceId]/logs/components/sidebar/components/markdown-renderer' @@ -254,14 +255,10 @@ export function Sidebar({ }, [log]) // Helper to determine if we have cost information to display + // All workflow executions now have cost info (base charge + any model costs) const hasCostInfo = useMemo(() => { - return !!( - log?.metadata?.cost && - ((log.metadata.cost.input && log.metadata.cost.input > 0) || - (log.metadata.cost.output && log.metadata.cost.output > 0) || - (log.metadata.cost.total && log.metadata.cost.total > 0)) - ) - }, [log]) + return isWorkflowExecutionLog && log?.metadata?.cost + }, [log, isWorkflowExecutionLog]) const isWorkflowWithCost = useMemo(() => { return isWorkflowExecutionLog && hasCostInfo @@ -492,49 +489,6 @@ export function Sidebar({ )} - {/* Enhanced Cost - only show for enhanced logs with actual cost data */} - {log.metadata?.enhanced && hasCostInfo && ( -
-

- Cost Breakdown -

-
- {(log.metadata?.cost?.total ?? 0) > 0 && ( -
- Total Cost: - - ${log.metadata?.cost?.total?.toFixed(4)} - -
- )} - {(log.metadata?.cost?.input ?? 0) > 0 && ( -
- Input Cost: - - ${log.metadata?.cost?.input?.toFixed(4)} - -
- )} - {(log.metadata?.cost?.output ?? 0) > 0 && ( -
- Output Cost: - - ${log.metadata?.cost?.output?.toFixed(4)} - -
- )} - {(log.metadata?.cost?.tokens?.total ?? 0) > 0 && ( -
- Total Tokens: - - {log.metadata?.cost?.tokens?.total?.toLocaleString()} - -
- )} -
-
- )} - {/* Frozen Canvas Button - only show for workflow execution logs with execution ID */} {isWorkflowExecutionLog && log.executionId && (
@@ -588,17 +542,23 @@ export function Sidebar({ {/* Cost Information (moved to bottom) */} {hasCostInfo && (
-

Models

+

+ Cost Breakdown +

- Input: + Base Execution: + {formatCost(BASE_EXECUTION_CHARGE)} +
+
+ Model Input: {formatCost(log.metadata?.cost?.input || 0)}
- Output: + Model Output: {formatCost(log.metadata?.cost?.output || 0)} @@ -677,8 +637,8 @@ export function Sidebar({ {isWorkflowWithCost && (

- This is the total cost for all LLM-based blocks in this workflow - execution. + Total cost includes a base execution charge of{' '} + {formatCost(BASE_EXECUTION_CHARGE)} plus any model usage costs.

)} diff --git a/apps/sim/lib/billing/constants.ts b/apps/sim/lib/billing/constants.ts new file mode 100644 index 000000000..33c3e8b3c --- /dev/null +++ b/apps/sim/lib/billing/constants.ts @@ -0,0 +1,9 @@ +/** + * Billing and cost constants shared between client and server code + */ + +/** + * Base charge applied to every workflow execution + * This charge is applied regardless of whether the workflow uses AI models + */ +export const BASE_EXECUTION_CHARGE = 0.001 diff --git a/apps/sim/lib/logs/execution/logger.ts b/apps/sim/lib/logs/execution/logger.ts index 8c04a58ab..acc68e34c 100644 --- a/apps/sim/lib/logs/execution/logger.ts +++ b/apps/sim/lib/logs/execution/logger.ts @@ -119,6 +119,8 @@ export class ExecutionLogger implements IExecutionLoggerService { totalTokens: number totalPromptTokens: number totalCompletionTokens: number + baseExecutionCharge: number + modelCost: number models: Record< string, { @@ -263,6 +265,8 @@ export class ExecutionLogger implements IExecutionLoggerService { totalTokens: number totalPromptTokens: number totalCompletionTokens: number + baseExecutionCharge: number + modelCost: number }, trigger: ExecutionTrigger['type'] ): Promise { @@ -286,7 +290,8 @@ export class ExecutionLogger implements IExecutionLoggerService { const userId = workflowRecord.userId const costMultiplier = getCostMultiplier() - const costToStore = costSummary.totalCost * costMultiplier + // Apply cost multiplier only to model costs, not base execution charge + const costToStore = costSummary.baseExecutionCharge + costSummary.modelCost * costMultiplier // Check if user stats record exists const userStatsRecords = await db.select().from(userStats).where(eq(userStats.userId, userId)) diff --git a/apps/sim/lib/logs/execution/logging-factory.ts b/apps/sim/lib/logs/execution/logging-factory.ts index ed912c5b1..ba208619e 100644 --- a/apps/sim/lib/logs/execution/logging-factory.ts +++ b/apps/sim/lib/logs/execution/logging-factory.ts @@ -1,3 +1,4 @@ +import { BASE_EXECUTION_CHARGE } from '@/lib/billing/constants' import type { ExecutionEnvironment, ExecutionTrigger, WorkflowState } from '@/lib/logs/types' import { loadWorkflowFromNormalizedTables } from '@/lib/workflows/db-helpers' @@ -53,6 +54,8 @@ export function calculateCostSummary(traceSpans: any[]): { totalTokens: number totalPromptTokens: number totalCompletionTokens: number + baseExecutionCharge: number + modelCost: number models: Record< string, { @@ -65,12 +68,14 @@ export function calculateCostSummary(traceSpans: any[]): { } { if (!traceSpans || traceSpans.length === 0) { return { - totalCost: 0, + totalCost: BASE_EXECUTION_CHARGE, totalInputCost: 0, totalOutputCost: 0, totalTokens: 0, totalPromptTokens: 0, totalCompletionTokens: 0, + baseExecutionCharge: BASE_EXECUTION_CHARGE, + modelCost: 0, models: {}, } } @@ -139,6 +144,9 @@ export function calculateCostSummary(traceSpans: any[]): { } } + const modelCost = totalCost + totalCost += BASE_EXECUTION_CHARGE + return { totalCost, totalInputCost, @@ -146,6 +154,8 @@ export function calculateCostSummary(traceSpans: any[]): { totalTokens, totalPromptTokens, totalCompletionTokens, + baseExecutionCharge: BASE_EXECUTION_CHARGE, + modelCost, models, } } diff --git a/apps/sim/lib/logs/execution/logging-session.ts b/apps/sim/lib/logs/execution/logging-session.ts index 003c2c3c2..c4abadb7c 100644 --- a/apps/sim/lib/logs/execution/logging-session.ts +++ b/apps/sim/lib/logs/execution/logging-session.ts @@ -1,3 +1,4 @@ +import { BASE_EXECUTION_CHARGE } from '@/lib/billing/constants' import { createLogger } from '@/lib/logs/console/logger' import { executionLogger } from '@/lib/logs/execution/logger' import { @@ -117,12 +118,14 @@ export class LoggingSession { async completeWithError(error?: any): Promise { try { const costSummary = { - totalCost: 0, + totalCost: BASE_EXECUTION_CHARGE, totalInputCost: 0, totalOutputCost: 0, totalTokens: 0, totalPromptTokens: 0, totalCompletionTokens: 0, + baseExecutionCharge: BASE_EXECUTION_CHARGE, + modelCost: 0, models: {}, }