Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -492,49 +489,6 @@ export function Sidebar({
</div>
)}

{/* Enhanced Cost - only show for enhanced logs with actual cost data */}
{log.metadata?.enhanced && hasCostInfo && (
<div>
<h3 className='mb-1 font-medium text-muted-foreground text-xs'>
Cost Breakdown
</h3>
<div className='space-y-1 text-sm'>
{(log.metadata?.cost?.total ?? 0) > 0 && (
<div className='flex justify-between'>
<span>Total Cost:</span>
<span className='font-medium'>
${log.metadata?.cost?.total?.toFixed(4)}
</span>
</div>
)}
{(log.metadata?.cost?.input ?? 0) > 0 && (
<div className='flex justify-between'>
<span>Input Cost:</span>
<span className='text-muted-foreground'>
${log.metadata?.cost?.input?.toFixed(4)}
</span>
</div>
)}
{(log.metadata?.cost?.output ?? 0) > 0 && (
<div className='flex justify-between'>
<span>Output Cost:</span>
<span className='text-muted-foreground'>
${log.metadata?.cost?.output?.toFixed(4)}
</span>
</div>
)}
{(log.metadata?.cost?.tokens?.total ?? 0) > 0 && (
<div className='flex justify-between'>
<span>Total Tokens:</span>
<span className='text-muted-foreground'>
{log.metadata?.cost?.tokens?.total?.toLocaleString()}
</span>
</div>
)}
</div>
</div>
)}

{/* Frozen Canvas Button - only show for workflow execution logs with execution ID */}
{isWorkflowExecutionLog && log.executionId && (
<div>
Expand Down Expand Up @@ -588,17 +542,23 @@ export function Sidebar({
{/* Cost Information (moved to bottom) */}
{hasCostInfo && (
<div>
<h3 className='mb-1 font-medium text-muted-foreground text-xs'>Models</h3>
<h3 className='mb-1 font-medium text-muted-foreground text-xs'>
Cost Breakdown
</h3>
<div className='overflow-hidden rounded-md border'>
<div className='space-y-2 p-3'>
<div className='flex items-center justify-between'>
<span className='text-muted-foreground text-sm'>Input:</span>
<span className='text-muted-foreground text-sm'>Base Execution:</span>
<span className='text-sm'>{formatCost(BASE_EXECUTION_CHARGE)}</span>
</div>
<div className='flex items-center justify-between'>
<span className='text-muted-foreground text-sm'>Model Input:</span>
<span className='text-sm'>
{formatCost(log.metadata?.cost?.input || 0)}
</span>
</div>
<div className='flex items-center justify-between'>
<span className='text-muted-foreground text-sm'>Output:</span>
<span className='text-muted-foreground text-sm'>Model Output:</span>
<span className='text-sm'>
{formatCost(log.metadata?.cost?.output || 0)}
</span>
Expand Down Expand Up @@ -677,8 +637,8 @@ export function Sidebar({
{isWorkflowWithCost && (
<div className='border-t bg-muted p-3 text-muted-foreground text-xs'>
<p>
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.
</p>
</div>
)}
Expand Down
9 changes: 9 additions & 0 deletions apps/sim/lib/billing/constants.ts
Original file line number Diff line number Diff line change
@@ -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
7 changes: 6 additions & 1 deletion apps/sim/lib/logs/execution/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ export class ExecutionLogger implements IExecutionLoggerService {
totalTokens: number
totalPromptTokens: number
totalCompletionTokens: number
baseExecutionCharge: number
modelCost: number
models: Record<
string,
{
Expand Down Expand Up @@ -263,6 +265,8 @@ export class ExecutionLogger implements IExecutionLoggerService {
totalTokens: number
totalPromptTokens: number
totalCompletionTokens: number
baseExecutionCharge: number
modelCost: number
},
trigger: ExecutionTrigger['type']
): Promise<void> {
Expand All @@ -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))
Expand Down
12 changes: 11 additions & 1 deletion apps/sim/lib/logs/execution/logging-factory.ts
Original file line number Diff line number Diff line change
@@ -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'

Expand Down Expand Up @@ -53,6 +54,8 @@ export function calculateCostSummary(traceSpans: any[]): {
totalTokens: number
totalPromptTokens: number
totalCompletionTokens: number
baseExecutionCharge: number
modelCost: number
models: Record<
string,
{
Expand All @@ -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: {},
}
}
Expand Down Expand Up @@ -139,13 +144,18 @@ export function calculateCostSummary(traceSpans: any[]): {
}
}

const modelCost = totalCost
totalCost += BASE_EXECUTION_CHARGE

return {
totalCost,
totalInputCost,
totalOutputCost,
totalTokens,
totalPromptTokens,
totalCompletionTokens,
baseExecutionCharge: BASE_EXECUTION_CHARGE,
modelCost,
models,
}
}
5 changes: 4 additions & 1 deletion apps/sim/lib/logs/execution/logging-session.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -117,12 +118,14 @@ export class LoggingSession {
async completeWithError(error?: any): Promise<void> {
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: {},
}

Expand Down