11import { createLogger } from '@sim/logger'
22import { getErrorMessage } from '@sim/utils/errors'
33import { tasks } from '@trigger.dev/sdk'
4- import type { BillingAttributionSnapshot } from '@/lib/billing/core/attribution'
5- import { type BillingEntity , recordUsage , stableEventKey } from '@/lib/billing/core/usage-log'
4+ import {
5+ type BillingAttributionSnapshot ,
6+ refreshAttributionPeriod ,
7+ } from '@/lib/billing/core/attribution'
8+ import { recordUsage , stableEventKey } from '@/lib/billing/core/usage-log'
69import { resolveTriggerRegion } from '@/lib/core/async-jobs/region'
710import { env } from '@/lib/core/config/env'
811import { isBillingEnabled , isHosted , isTriggerDevEnabled } from '@/lib/core/config/env-flags'
@@ -34,12 +37,10 @@ export interface FalAICostSettlementPayload {
3437 markupMultiplier : number
3538 ledger : {
3639 source : FalAISettlementSource
37- userId : string
38- workspaceId : string | null
3940 workflowId ?: string
4041 executionId ?: string
41- billingEntity : BillingEntity
42- billingPeriod : { start : string ; end : string }
42+ /** The run's payer as admitted; its period is refreshed when settlement outlives it. */
43+ billingAttribution : BillingAttributionSnapshot
4344 }
4445}
4546
@@ -85,15 +86,9 @@ export async function scheduleFalAICostSettlement(
8586 markupMultiplier : params . markupMultiplier ,
8687 ledger : {
8788 source : params . source ,
88- userId : billingAttribution . actorUserId ,
89- workspaceId : billingAttribution . workspaceId ,
9089 workflowId : params . workflowId ,
9190 executionId : params . executionId ,
92- billingEntity : { ...billingAttribution . billingEntity } ,
93- billingPeriod : {
94- start : billingAttribution . billingPeriod . start ,
95- end : billingAttribution . billingPeriod . end ,
96- } ,
91+ billingAttribution,
9792 } ,
9893 }
9994
@@ -116,6 +111,8 @@ export async function scheduleFalAICostSettlement(
116111 * Bills the difference between fal's exact charge for a request and the estimate billed at
117112 * completion. The ledger row's event key is the request, so a retried settlement never bills
118113 * twice. An estimate above the exact charge is left as billed: the ledger records charges only.
114+ * A settlement that outlives the run's billing period bills the payer's current period, since
115+ * the ended one may already be closed and invoiced.
119116 */
120117export async function settleFalAICost (
121118 payload : FalAICostSettlementPayload
@@ -139,15 +136,19 @@ export async function settleFalAICost(
139136 }
140137 if ( shortfallDollars === 0 ) return 'exact'
141138
139+ const attribution =
140+ Date . parse ( ledger . billingAttribution . billingPeriod . end ) <= Date . now ( )
141+ ? await refreshAttributionPeriod ( ledger . billingAttribution )
142+ : ledger . billingAttribution
142143 await recordUsage ( {
143- userId : ledger . userId ,
144- workspaceId : ledger . workspaceId ?? undefined ,
144+ userId : attribution . actorUserId ,
145+ workspaceId : attribution . workspaceId ?? undefined ,
145146 workflowId : ledger . workflowId ,
146147 executionId : ledger . executionId ,
147- billingEntity : ledger . billingEntity ,
148+ billingEntity : { ... attribution . billingEntity } ,
148149 billingPeriod : {
149- start : new Date ( ledger . billingPeriod . start ) ,
150- end : new Date ( ledger . billingPeriod . end ) ,
150+ start : new Date ( attribution . billingPeriod . start ) ,
151+ end : new Date ( attribution . billingPeriod . end ) ,
151152 } ,
152153 entries : [
153154 {
0 commit comments