feat(execution): base execution charge of 0.001/execution (#817)

This commit is contained in:
Vikhyath Mondreti
2025-07-29 11:25:50 -07:00
committed by GitHub
parent 08720d926c
commit 13608a8bbc
5 changed files with 45 additions and 58 deletions

View File

@@ -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({
</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>
@@ -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>
@@ -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>
)}

View 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

View File

@@ -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<void> {
@@ -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))

View File

@@ -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,
}
}

View File

@@ -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<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: {},
}