mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-09 15:07:55 -05:00
feat(execution): base execution charge of 0.001/execution (#817)
This commit is contained in:
committed by
GitHub
parent
08720d926c
commit
13608a8bbc
@@ -6,6 +6,7 @@ import { Button } from '@/components/ui/button'
|
|||||||
import { CopyButton } from '@/components/ui/copy-button'
|
import { CopyButton } from '@/components/ui/copy-button'
|
||||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
|
||||||
|
import { BASE_EXECUTION_CHARGE } from '@/lib/billing/constants'
|
||||||
import { redactApiKeys } from '@/lib/utils'
|
import { redactApiKeys } from '@/lib/utils'
|
||||||
import { FrozenCanvasModal } from '@/app/workspace/[workspaceId]/logs/components/frozen-canvas/frozen-canvas-modal'
|
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'
|
import LogMarkdownRenderer from '@/app/workspace/[workspaceId]/logs/components/sidebar/components/markdown-renderer'
|
||||||
@@ -254,14 +255,10 @@ export function Sidebar({
|
|||||||
}, [log])
|
}, [log])
|
||||||
|
|
||||||
// Helper to determine if we have cost information to display
|
// 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(() => {
|
const hasCostInfo = useMemo(() => {
|
||||||
return !!(
|
return isWorkflowExecutionLog && log?.metadata?.cost
|
||||||
log?.metadata?.cost &&
|
}, [log, isWorkflowExecutionLog])
|
||||||
((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])
|
|
||||||
|
|
||||||
const isWorkflowWithCost = useMemo(() => {
|
const isWorkflowWithCost = useMemo(() => {
|
||||||
return isWorkflowExecutionLog && hasCostInfo
|
return isWorkflowExecutionLog && hasCostInfo
|
||||||
@@ -492,49 +489,6 @@ export function Sidebar({
|
|||||||
</div>
|
</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 */}
|
{/* Frozen Canvas Button - only show for workflow execution logs with execution ID */}
|
||||||
{isWorkflowExecutionLog && log.executionId && (
|
{isWorkflowExecutionLog && log.executionId && (
|
||||||
<div>
|
<div>
|
||||||
@@ -588,17 +542,23 @@ export function Sidebar({
|
|||||||
{/* Cost Information (moved to bottom) */}
|
{/* Cost Information (moved to bottom) */}
|
||||||
{hasCostInfo && (
|
{hasCostInfo && (
|
||||||
<div>
|
<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='overflow-hidden rounded-md border'>
|
||||||
<div className='space-y-2 p-3'>
|
<div className='space-y-2 p-3'>
|
||||||
<div className='flex items-center justify-between'>
|
<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'>
|
<span className='text-sm'>
|
||||||
{formatCost(log.metadata?.cost?.input || 0)}
|
{formatCost(log.metadata?.cost?.input || 0)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center justify-between'>
|
<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'>
|
<span className='text-sm'>
|
||||||
{formatCost(log.metadata?.cost?.output || 0)}
|
{formatCost(log.metadata?.cost?.output || 0)}
|
||||||
</span>
|
</span>
|
||||||
@@ -677,8 +637,8 @@ export function Sidebar({
|
|||||||
{isWorkflowWithCost && (
|
{isWorkflowWithCost && (
|
||||||
<div className='border-t bg-muted p-3 text-muted-foreground text-xs'>
|
<div className='border-t bg-muted p-3 text-muted-foreground text-xs'>
|
||||||
<p>
|
<p>
|
||||||
This is the total cost for all LLM-based blocks in this workflow
|
Total cost includes a base execution charge of{' '}
|
||||||
execution.
|
{formatCost(BASE_EXECUTION_CHARGE)} plus any model usage costs.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
9
apps/sim/lib/billing/constants.ts
Normal file
9
apps/sim/lib/billing/constants.ts
Normal file
@@ -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
|
||||||
@@ -119,6 +119,8 @@ export class ExecutionLogger implements IExecutionLoggerService {
|
|||||||
totalTokens: number
|
totalTokens: number
|
||||||
totalPromptTokens: number
|
totalPromptTokens: number
|
||||||
totalCompletionTokens: number
|
totalCompletionTokens: number
|
||||||
|
baseExecutionCharge: number
|
||||||
|
modelCost: number
|
||||||
models: Record<
|
models: Record<
|
||||||
string,
|
string,
|
||||||
{
|
{
|
||||||
@@ -263,6 +265,8 @@ export class ExecutionLogger implements IExecutionLoggerService {
|
|||||||
totalTokens: number
|
totalTokens: number
|
||||||
totalPromptTokens: number
|
totalPromptTokens: number
|
||||||
totalCompletionTokens: number
|
totalCompletionTokens: number
|
||||||
|
baseExecutionCharge: number
|
||||||
|
modelCost: number
|
||||||
},
|
},
|
||||||
trigger: ExecutionTrigger['type']
|
trigger: ExecutionTrigger['type']
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
@@ -286,7 +290,8 @@ export class ExecutionLogger implements IExecutionLoggerService {
|
|||||||
|
|
||||||
const userId = workflowRecord.userId
|
const userId = workflowRecord.userId
|
||||||
const costMultiplier = getCostMultiplier()
|
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
|
// Check if user stats record exists
|
||||||
const userStatsRecords = await db.select().from(userStats).where(eq(userStats.userId, userId))
|
const userStatsRecords = await db.select().from(userStats).where(eq(userStats.userId, userId))
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { BASE_EXECUTION_CHARGE } from '@/lib/billing/constants'
|
||||||
import type { ExecutionEnvironment, ExecutionTrigger, WorkflowState } from '@/lib/logs/types'
|
import type { ExecutionEnvironment, ExecutionTrigger, WorkflowState } from '@/lib/logs/types'
|
||||||
import { loadWorkflowFromNormalizedTables } from '@/lib/workflows/db-helpers'
|
import { loadWorkflowFromNormalizedTables } from '@/lib/workflows/db-helpers'
|
||||||
|
|
||||||
@@ -53,6 +54,8 @@ export function calculateCostSummary(traceSpans: any[]): {
|
|||||||
totalTokens: number
|
totalTokens: number
|
||||||
totalPromptTokens: number
|
totalPromptTokens: number
|
||||||
totalCompletionTokens: number
|
totalCompletionTokens: number
|
||||||
|
baseExecutionCharge: number
|
||||||
|
modelCost: number
|
||||||
models: Record<
|
models: Record<
|
||||||
string,
|
string,
|
||||||
{
|
{
|
||||||
@@ -65,12 +68,14 @@ export function calculateCostSummary(traceSpans: any[]): {
|
|||||||
} {
|
} {
|
||||||
if (!traceSpans || traceSpans.length === 0) {
|
if (!traceSpans || traceSpans.length === 0) {
|
||||||
return {
|
return {
|
||||||
totalCost: 0,
|
totalCost: BASE_EXECUTION_CHARGE,
|
||||||
totalInputCost: 0,
|
totalInputCost: 0,
|
||||||
totalOutputCost: 0,
|
totalOutputCost: 0,
|
||||||
totalTokens: 0,
|
totalTokens: 0,
|
||||||
totalPromptTokens: 0,
|
totalPromptTokens: 0,
|
||||||
totalCompletionTokens: 0,
|
totalCompletionTokens: 0,
|
||||||
|
baseExecutionCharge: BASE_EXECUTION_CHARGE,
|
||||||
|
modelCost: 0,
|
||||||
models: {},
|
models: {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,6 +144,9 @@ export function calculateCostSummary(traceSpans: any[]): {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const modelCost = totalCost
|
||||||
|
totalCost += BASE_EXECUTION_CHARGE
|
||||||
|
|
||||||
return {
|
return {
|
||||||
totalCost,
|
totalCost,
|
||||||
totalInputCost,
|
totalInputCost,
|
||||||
@@ -146,6 +154,8 @@ export function calculateCostSummary(traceSpans: any[]): {
|
|||||||
totalTokens,
|
totalTokens,
|
||||||
totalPromptTokens,
|
totalPromptTokens,
|
||||||
totalCompletionTokens,
|
totalCompletionTokens,
|
||||||
|
baseExecutionCharge: BASE_EXECUTION_CHARGE,
|
||||||
|
modelCost,
|
||||||
models,
|
models,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { BASE_EXECUTION_CHARGE } from '@/lib/billing/constants'
|
||||||
import { createLogger } from '@/lib/logs/console/logger'
|
import { createLogger } from '@/lib/logs/console/logger'
|
||||||
import { executionLogger } from '@/lib/logs/execution/logger'
|
import { executionLogger } from '@/lib/logs/execution/logger'
|
||||||
import {
|
import {
|
||||||
@@ -117,12 +118,14 @@ export class LoggingSession {
|
|||||||
async completeWithError(error?: any): Promise<void> {
|
async completeWithError(error?: any): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const costSummary = {
|
const costSummary = {
|
||||||
totalCost: 0,
|
totalCost: BASE_EXECUTION_CHARGE,
|
||||||
totalInputCost: 0,
|
totalInputCost: 0,
|
||||||
totalOutputCost: 0,
|
totalOutputCost: 0,
|
||||||
totalTokens: 0,
|
totalTokens: 0,
|
||||||
totalPromptTokens: 0,
|
totalPromptTokens: 0,
|
||||||
totalCompletionTokens: 0,
|
totalCompletionTokens: 0,
|
||||||
|
baseExecutionCharge: BASE_EXECUTION_CHARGE,
|
||||||
|
modelCost: 0,
|
||||||
models: {},
|
models: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user