11import Foundation
22
33enum UsageAnalytics {
4- /// 双用量池:API 按 apiPercentUsed × limit 计算;Auto+Composer 按 bonusSpend
4+ /// 双用量池:API 按 apiPercentUsed × limit 计算;Auto+Composer 按第一方模型花费汇总
55 static func resolveUsagePools(
66 summary: UsageSummary ? ,
7- period: PeriodUsageResponse ?
7+ period: PeriodUsageResponse ? ,
8+ aggregated: AggregatedUsageResponse ? = nil
89 ) -> [ UsagePoolMetrics ] {
910 let plan = summary? . billingPlan
1011 let periodPlan = period? . planUsage
12+ let autoBucketModels = period? . autoBucketModels
1113
1214 let apiPercent = plan? . apiPercentUsed ?? periodPlan? . apiPercentUsed
1315 let autoPercent = plan? . autoPercentUsed ?? periodPlan? . autoPercentUsed
@@ -17,14 +19,13 @@ enum UsageAnalytics {
1719 limitCents: apiLimit,
1820 explicitSpend: periodPlan? . apiSpend
1921 )
20- let autoSpend = periodPlan? . bonusSpend
21- ?? periodPlan? . autoSpend
22- ?? {
23- guard let total = periodPlan? . totalSpend, let included = periodPlan? . includedSpend else {
24- return nil
25- }
26- return max ( 0 , total - included)
27- } ( )
22+ let autoSpend = resolveAutoSpendCents (
23+ summary: summary,
24+ period: period,
25+ aggregated: aggregated,
26+ autoBucketModels: autoBucketModels,
27+ apiUsedCents: apiUsed
28+ )
2829
2930 let apiMessage = summary? . namedModelSelectedDisplayMessage
3031 ?? period? . namedModelSelectedDisplayMessage
@@ -76,6 +77,58 @@ enum UsageAnalytics {
7677 return explicitSpend
7778 }
7879
80+ /// Auto 池花费:接口常不再返回 bonusSpend;优先 aggregated 第一方汇总,其次 total − API 已用
81+ static func resolveAutoSpendCents(
82+ summary: UsageSummary ? ,
83+ period: PeriodUsageResponse ? ,
84+ aggregated: AggregatedUsageResponse ? ,
85+ autoBucketModels: [ String ] ? ,
86+ apiUsedCents: Double ?
87+ ) -> Double ? {
88+ let periodPlan = period? . planUsage
89+
90+ if let bonusSpend = periodPlan? . bonusSpend, bonusSpend > 0 {
91+ return bonusSpend
92+ }
93+ if let autoSpend = periodPlan? . autoSpend, autoSpend > 0 {
94+ return autoSpend
95+ }
96+
97+ if let fromAggregated = poolSpendCents (
98+ from: aggregated,
99+ autoBucketModels: autoBucketModels,
100+ pool: . firstParty
101+ ) {
102+ return fromAggregated
103+ }
104+
105+ let totalSpend = periodPlan? . totalSpend
106+ ?? aggregated? . totalCostCents
107+ ?? summary? . billingPlan? . used
108+ if let totalSpend, let apiUsedCents, totalSpend >= apiUsedCents {
109+ let derived = totalSpend - apiUsedCents
110+ if derived > 0 { return derived }
111+ }
112+
113+ return nil
114+ }
115+
116+ static func poolSpendCents(
117+ from aggregated: AggregatedUsageResponse ? ,
118+ autoBucketModels: [ String ] ? ,
119+ pool: ModelPricingCatalog . Pool
120+ ) -> Double ? {
121+ guard let aggregations = aggregated? . aggregations, !aggregations. isEmpty else { return nil }
122+
123+ let total = aggregations. reduce ( 0.0 ) { sum, item in
124+ guard let model = item. modelIntent else { return sum }
125+ let itemPool = resolveUsagePool ( for: model, autoBucketModels: autoBucketModels)
126+ guard itemPool == pool else { return sum }
127+ return sum + ( item. totalCents ?? 0 )
128+ }
129+ return total > 0 ? total : nil
130+ }
131+
79132 /// Billing 总口径:totalPercentUsed + 全周期花费
80133 static func resolveBillingBaseline(
81134 summary: UsageSummary ? ,
@@ -88,7 +141,7 @@ enum UsageAnalytics {
88141 ?? periodPlan? . totalPercentUsed
89142 ?? 0
90143 let canonicalCostCents = aggregated? . totalCostCents ?? periodPlan? . totalSpend
91- let pools = resolveUsagePools ( summary: summary, period: period)
144+ let pools = resolveUsagePools ( summary: summary, period: period, aggregated : aggregated )
92145
93146 guard percent > 0 || canonicalCostCents != nil || pools. contains ( where: { $0. percent != nil } ) else {
94147 return nil
@@ -685,9 +738,11 @@ enum UsageAnalytics {
685738
686739 static func spendingBreakdown(
687740 from period: PeriodUsageResponse ? ,
688- summary: UsageSummary ?
741+ summary: UsageSummary ? ,
742+ aggregated: AggregatedUsageResponse ? = nil
689743 ) -> [ SpendingBreakdownItem ] {
690744 var items : [ SpendingBreakdownItem ] = [ ]
745+ let periodPlan = period? . planUsage
691746
692747 if let plan = summary? . billingPlan {
693748 if let total = plan. totalPercentUsed {
@@ -696,18 +751,29 @@ enum UsageAnalytics {
696751 id: " total " ,
697752 label: " Billing 总计 " ,
698753 percent: total,
699- spendCents: period ? . planUsage ? . totalSpend ,
754+ spendCents: periodPlan ? . totalSpend ?? aggregated ? . totalCostCents ,
700755 colorName: " blue "
701756 )
702757 )
703758 }
704759 if let auto = plan. autoPercentUsed {
760+ let apiUsed = resolveAPIUsedCents (
761+ percent: plan. apiPercentUsed ?? periodPlan? . apiPercentUsed,
762+ limitCents: plan. limit ?? periodPlan? . limit,
763+ explicitSpend: periodPlan? . apiSpend
764+ )
705765 items. append (
706766 SpendingBreakdownItem (
707767 id: " auto " ,
708768 label: " Auto + Composer " ,
709769 percent: auto,
710- spendCents: period? . planUsage? . bonusSpend,
770+ spendCents: resolveAutoSpendCents (
771+ summary: summary,
772+ period: period,
773+ aggregated: aggregated,
774+ autoBucketModels: period? . autoBucketModels,
775+ apiUsedCents: apiUsed
776+ ) ,
711777 colorName: " purple "
712778 )
713779 )
@@ -716,7 +782,7 @@ enum UsageAnalytics {
716782 let apiUsed = resolveAPIUsedCents (
717783 percent: api,
718784 limitCents: plan. limit,
719- explicitSpend: period ? . planUsage ? . apiSpend
785+ explicitSpend: periodPlan ? . apiSpend
720786 )
721787 items. append (
722788 SpendingBreakdownItem (
0 commit comments