mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-07 18:07:47 +08:00
feat(frontend): expose processing tier pricing
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import type { TieredPricingConfig } from '@/api/endpoints/types'
|
||||
import {
|
||||
tieredPricingHasCacheTtl,
|
||||
tieredPricingHasImageOutputPricing,
|
||||
} from '../tiered-pricing'
|
||||
|
||||
function pricingWithProcessingTier(
|
||||
processingTier: NonNullable<TieredPricingConfig['processing_tiers']>[string],
|
||||
): TieredPricingConfig {
|
||||
return {
|
||||
tiers: [{ up_to: null, input_price_per_1m: 1, output_price_per_1m: 2 }],
|
||||
processing_tiers: { priority: processingTier },
|
||||
}
|
||||
}
|
||||
|
||||
describe('tiered pricing capabilities', () => {
|
||||
it('detects image prices in the base and processing-tier catalogs', () => {
|
||||
expect(tieredPricingHasImageOutputPricing({
|
||||
tiers: [],
|
||||
image_output_price_default: 0,
|
||||
})).toBe(true)
|
||||
expect(tieredPricingHasImageOutputPricing(pricingWithProcessingTier({
|
||||
image_output_prices: { '1024x1024': { high: 0.08 } },
|
||||
}))).toBe(true)
|
||||
expect(tieredPricingHasImageOutputPricing(pricingWithProcessingTier({
|
||||
image_output_price_ranges: [{
|
||||
up_to_pixels: null,
|
||||
prices: { medium: 0.04 },
|
||||
}],
|
||||
}))).toBe(true)
|
||||
expect(tieredPricingHasImageOutputPricing(pricingWithProcessingTier({}))).toBe(false)
|
||||
})
|
||||
|
||||
it('detects cache TTL prices in the base and processing-tier catalogs', () => {
|
||||
expect(tieredPricingHasCacheTtl({
|
||||
tiers: [{
|
||||
up_to: null,
|
||||
input_price_per_1m: 1,
|
||||
output_price_per_1m: 2,
|
||||
cache_ttl_pricing: [{ ttl_minutes: 60, cache_creation_price_per_1m: 3 }],
|
||||
}],
|
||||
}, 60)).toBe(true)
|
||||
expect(tieredPricingHasCacheTtl(pricingWithProcessingTier({
|
||||
tiers: [{
|
||||
up_to: null,
|
||||
input_price_per_1m: 1,
|
||||
output_price_per_1m: 2,
|
||||
cache_ttl_pricing: [{ ttl_minutes: 60, cache_creation_price_per_1m: 3 }],
|
||||
}],
|
||||
}), 60)).toBe(true)
|
||||
expect(tieredPricingHasCacheTtl(pricingWithProcessingTier({}), 60)).toBe(false)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,67 @@
|
||||
import type {
|
||||
ProcessingTierPricingConfig,
|
||||
TieredPricingConfig,
|
||||
} from '@/api/endpoints/types'
|
||||
|
||||
type PricingCatalog = TieredPricingConfig | ProcessingTierPricingConfig
|
||||
|
||||
export function comparePricingUpperBounds(
|
||||
left: number | null,
|
||||
right: number | null,
|
||||
): number {
|
||||
if (left === null && right === null) return 0
|
||||
if (left === null) return 1
|
||||
if (right === null) return -1
|
||||
return left - right
|
||||
}
|
||||
|
||||
function pricingCatalogs(pricing: TieredPricingConfig | null | undefined): PricingCatalog[] {
|
||||
if (!pricing) return []
|
||||
const processingTiers = pricing.processing_tiers
|
||||
? Object.values(pricing.processing_tiers).filter(isRecord)
|
||||
: []
|
||||
return [pricing, ...processingTiers]
|
||||
}
|
||||
|
||||
export function tieredPricingHasImageOutputPricing(
|
||||
pricing: TieredPricingConfig | null | undefined,
|
||||
): boolean {
|
||||
return pricingCatalogs(pricing).some((catalog) => {
|
||||
if (toFinitePrice(catalog.image_output_price_default) !== null) return true
|
||||
if (Object.values(catalog.image_output_prices || {}).some(prices => (
|
||||
isRecord(prices)
|
||||
&& Object.values(prices).some(price => toFinitePrice(price) !== null)
|
||||
))) return true
|
||||
return (catalog.image_output_price_ranges || []).some(range => (
|
||||
isRecord(range)
|
||||
&& isRecord(range.prices)
|
||||
&& Object.values(range.prices).some(price => toFinitePrice(price) !== null)
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
export function tieredPricingHasCacheTtl(
|
||||
pricing: TieredPricingConfig | null | undefined,
|
||||
ttlMinutes: number,
|
||||
): boolean {
|
||||
return pricingCatalogs(pricing).some(catalog => (
|
||||
Array.isArray(catalog.tiers)
|
||||
&& catalog.tiers.some(tier => (
|
||||
Array.isArray(tier.cache_ttl_pricing)
|
||||
&& tier.cache_ttl_pricing.some(entry => entry.ttl_minutes === ttlMinutes)
|
||||
))
|
||||
))
|
||||
}
|
||||
|
||||
function toFinitePrice(value: unknown): number | null {
|
||||
if (typeof value === 'number' && Number.isFinite(value)) return value
|
||||
if (typeof value === 'string' && value.trim()) {
|
||||
const parsed = Number(value)
|
||||
return Number.isFinite(parsed) ? parsed : null
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return Boolean(value) && typeof value === 'object' && !Array.isArray(value)
|
||||
}
|
||||
Reference in New Issue
Block a user