From eaad07e002d5cf5f36458ce88a1b44d0dee3a726 Mon Sep 17 00:00:00 2001 From: Shruc <42489293+P3il4@users.noreply.github.com> Date: Sat, 18 Apr 2026 00:28:26 +0300 Subject: [PATCH] adjust and refactor together models costs (#2811) * adjust, refactor together costs * aliases follow rule --- .../costMaps/togetherCostMap.ts | 19 +- .../TogetherImageGenerationProvider.ts | 46 ++-- .../TogetherImageGenerationProvider/models.ts | 260 +++++++++++------- .../src/services/ai/image/providers/types.ts | 16 ++ .../TogetherVideoGenerationProvider/models.ts | 20 +- 5 files changed, 241 insertions(+), 120 deletions(-) diff --git a/src/backend/src/services/MeteringService/costMaps/togetherCostMap.ts b/src/backend/src/services/MeteringService/costMaps/togetherCostMap.ts index e6f8445dc..c6884c57b 100644 --- a/src/backend/src/services/MeteringService/costMaps/togetherCostMap.ts +++ b/src/backend/src/services/MeteringService/costMaps/togetherCostMap.ts @@ -16,28 +16,28 @@ export const TOGETHER_COST_MAP = { 'together-image:Qwen/Qwen-Image': 0.0058 * 100_000_000, 'together-image:RunDiffusion/Juggernaut-pro-flux': 0.0049 * 100_000_000, 'together-image:Rundiffusion/Juggernaut-Lightning-Flux': 0.0017 * 100_000_000, - 'together-image:black-forest-labs/FLUX.1-Canny-pro': 0.05 * 100_000_000, - 'together-image:black-forest-labs/FLUX.1-dev': 0.025 * 100_000_000, - 'together-image:black-forest-labs/FLUX.1-dev-lora': 0.025 * 100_000_000, - 'together-image:black-forest-labs/FLUX.1-kontext-dev': 0.025 * 100_000_000, 'together-image:black-forest-labs/FLUX.1-kontext-max': 0.08 * 100_000_000, 'together-image:black-forest-labs/FLUX.1-kontext-pro': 0.04 * 100_000_000, 'together-image:black-forest-labs/FLUX.1-krea-dev': 0.025 * 100_000_000, - 'together-image:black-forest-labs/FLUX.1-pro': 0.05 * 100_000_000, 'together-image:black-forest-labs/FLUX.1-schnell': 0.0027 * 100_000_000, - 'together-image:black-forest-labs/FLUX.1.1-pro': 0.05 * 100_000_000, + 'together-image:black-forest-labs/FLUX.1.1-pro': 0.04 * 100_000_000, 'together-image:black-forest-labs/FLUX.2-pro': 0.03 * 100_000_000, 'together-image:black-forest-labs/FLUX.2-flex': 0.03 * 100_000_000, 'together-image:black-forest-labs/FLUX.2-dev': 0.0154 * 100_000_000, 'together-image:black-forest-labs/FLUX.2-max': 0.07 * 100_000_000, 'together-image:google/flash-image-2.5': 0.039 * 100_000_000, + 'together-image:google/flash-image-3.1': 0.067 * 100_000_000, 'together-image:google/gemini-3-pro-image': 0.134 * 100_000_000, 'together-image:google/imagen-4.0-fast': 0.02 * 100_000_000, 'together-image:google/imagen-4.0-preview': 0.04 * 100_000_000, 'together-image:google/imagen-4.0-ultra': 0.06 * 100_000_000, 'together-image:ideogram/ideogram-3.0': 0.06 * 100_000_000, + 'together-image:openai/gpt-image-1.5': 0.034 * 100_000_000, + 'together-image:Qwen/Qwen-Image-2.0': 0.04 * 100_000_000, + 'together-image:Qwen/Qwen-Image-2.0-Pro': 0.08 * 100_000_000, + 'together-image:Wan-AI/Wan2.6-image': 0.03 * 100_000_000, 'together-image:stabilityai/stable-diffusion-3-medium': 0.0019 * 100_000_000, - 'together-image:stabilityai/stable-diffusion-xl-base-1.0': 0.0045 * 100_000_000, + 'together-image:stabilityai/stable-diffusion-xl-base-1.0': 0.0019 * 100_000_000, // Video generation placeholder (per-video pricing). Update with real pricing when available. 'together-video:default': 0, @@ -45,6 +45,7 @@ export const TOGETHER_COST_MAP = { 'together-video:ByteDance/Seedance-1.0-pro': 0.57 * 100_000_000, 'together-video:Wan-AI/Wan2.2-I2V-A14B': 0.31 * 100_000_000, 'together-video:Wan-AI/Wan2.2-T2V-A14B': 0.66 * 100_000_000, + 'together-video:Wan-AI/wan2.7-t2v': 0.10 * 100_000_000, 'together-video:google/veo-2.0': 2.50 * 100_000_000, 'together-video:google/veo-3.0': 1.60 * 100_000_000, 'together-video:google/veo-3.0-audio': 3.20 * 100_000_000, @@ -56,10 +57,10 @@ export const TOGETHER_COST_MAP = { 'together-video:kwaivgI/kling-2.1-master': 0.92 * 100_000_000, 'together-video:kwaivgI/kling-2.1-pro': 0.32 * 100_000_000, 'together-video:kwaivgI/kling-2.1-standard': 0.18 * 100_000_000, - 'together-video:minimax/hailuo-02': 0.56 * 100_000_000, + 'together-video:minimax/hailuo-02': 0.49 * 100_000_000, 'together-video:minimax/video-01-director': 0.28 * 100_000_000, 'together-video:openai/sora-2': 0.80 * 100_000_000, - 'together-video:openai/sora-2-pro': 4.00 * 100_000_000, + 'together-video:openai/sora-2-pro': 3.00 * 100_000_000, 'together-video:pixverse/pixverse-v5': 0.30 * 100_000_000, 'together-video:vidu/vidu-2.0': 0.28 * 100_000_000, 'together-video:vidu/vidu-q1': 0.22 * 100_000_000, diff --git a/src/backend/src/services/ai/image/providers/TogetherImageGenerationProvider/TogetherImageGenerationProvider.ts b/src/backend/src/services/ai/image/providers/TogetherImageGenerationProvider/TogetherImageGenerationProvider.ts index 8cb9387ae..9c35e9e44 100644 --- a/src/backend/src/services/ai/image/providers/TogetherImageGenerationProvider/TogetherImageGenerationProvider.ts +++ b/src/backend/src/services/ai/image/providers/TogetherImageGenerationProvider/TogetherImageGenerationProvider.ts @@ -24,14 +24,13 @@ import { Context } from '../../../../../util/context.js'; import { EventService } from '../../../../EventService.js'; import { MeteringService } from '../../../../MeteringService/MeteringService.js'; import { IGenerateParams, IImageModel, IImageProvider } from '../types.js'; -import { TOGETHER_IMAGE_GENERATION_MODELS, GEMINI_3_IMAGE_RESOLUTION_MAP } from './models.js'; +import { TOGETHER_IMAGE_GENERATION_MODELS } from './models.js'; const TOGETHER_DEFAULT_RATIO = { w: 1024, h: 1024 }; type TogetherGenerateParams = IGenerateParams & { steps?: number; seed?: number; negative_prompt?: string; - n?: number; image_url?: string; image_base64?: string; mask_image_url?: string; @@ -101,28 +100,43 @@ export class TogetherImageGenerationProvider implements IImageProvider { throw new Error('actor not found in context'); } - const isGemini3 = selectedModel.id === 'togetherai:google/gemini-3-pro-image'; + const pricingUnit = selectedModel.pricing_unit ?? 'per-MP'; let costInMicroCents: number; let usageAmount: number; - const qualityCostKey = isGemini3 && quality && selectedModel.costs[quality] !== undefined ? quality : undefined; + let usageKey: string; - if ( qualityCostKey ) { - const centsPerImage = selectedModel.costs[qualityCostKey]; + if ( pricingUnit === 'per-image' ) { + const centsPerImage = selectedModel.costs['per-image']; + if ( centsPerImage === undefined ) { + throw new Error(`Model ${selectedModel.id} missing 'per-image' cost`); + } costInMicroCents = centsPerImage * 1_000_000; usageAmount = 1; + usageKey = 'per-image'; + } else if ( pricingUnit === 'per-tier' ) { + const tierKey = quality && selectedModel.costs[quality] !== undefined + ? quality + : Object.keys(selectedModel.costs)[0]; + const centsPerImage = selectedModel.costs[tierKey]; + if ( centsPerImage === undefined ) { + throw new Error(`Model ${selectedModel.id} missing tier cost`); + } + costInMicroCents = centsPerImage * 1_000_000; + usageAmount = 1; + usageKey = tierKey; } else { - const priceKey = '1MP'; - const centsPerMP = selectedModel.costs[priceKey]; + const centsPerMP = selectedModel.costs['1MP']; if ( centsPerMP === undefined ) { - throw new Error(`No pricing configured for model ${selectedModel.id}`); + throw new Error(`Model ${selectedModel.id} missing '1MP' cost`); } const MP = (ratio.h * ratio.w) / 1_000_000; costInMicroCents = centsPerMP * MP * 1_000_000; usageAmount = MP; + usageKey = '1MP'; } - const usageType = `${selectedModel.id}:${quality || '1MP'}`; + const usageType = `${selectedModel.id}:${usageKey}`; const usageAllowed = await this.#meteringService.hasEnoughCredits(actor, costInMicroCents); @@ -130,11 +144,12 @@ export class TogetherImageGenerationProvider implements IImageProvider { throw APIError.create('insufficient_funds'); } - // Resolve abstract aspect ratios to actual pixel dimensions for Gemini 3 Pro + // Resolve abstract aspect ratios (e.g. 1:1, 16:9) to concrete pixel + // dimensions via the model's own resolution_map. let resolvedRatio = ratio; - if ( isGemini3 && quality ) { + if ( pricingUnit === 'per-tier' && quality && selectedModel.resolution_map ) { const ratioKey = `${ratio.w}:${ratio.h}`; - const resolutionEntry = GEMINI_3_IMAGE_RESOLUTION_MAP[ratioKey]?.[quality]; + const resolutionEntry = selectedModel.resolution_map[ratioKey]?.[quality]; if ( resolutionEntry ) { resolvedRatio = resolutionEntry; } @@ -174,7 +189,6 @@ export class TogetherImageGenerationProvider implements IImageProvider { steps, seed, negative_prompt, - n, image_url, image_base64, mask_image_url, @@ -188,6 +202,7 @@ export class TogetherImageGenerationProvider implements IImageProvider { const request: Record = { prompt, model: model ?? DEFAULT_MODEL, + n: 1, }; const requiresConditionImage = this.#modelRequiresConditionImage(request.model as string); @@ -206,9 +221,6 @@ export class TogetherImageGenerationProvider implements IImageProvider { } if ( typeof seed === 'number' && Number.isFinite(seed) ) request.seed = Math.round(seed); if ( typeof negative_prompt === 'string' ) request.negative_prompt = negative_prompt; - if ( typeof n === 'number' && Number.isFinite(n) ) { - request.n = Math.max(1, Math.min(4, Math.round(n))); - } if ( disable_safety_checker ) { request.disable_safety_checker = true; } diff --git a/src/backend/src/services/ai/image/providers/TogetherImageGenerationProvider/models.ts b/src/backend/src/services/ai/image/providers/TogetherImageGenerationProvider/models.ts index 5eb43ace3..1b3e2abd1 100644 --- a/src/backend/src/services/ai/image/providers/TogetherImageGenerationProvider/models.ts +++ b/src/backend/src/services/ai/image/providers/TogetherImageGenerationProvider/models.ts @@ -19,201 +19,254 @@ import { IImageModel } from '../types'; +type ResolutionMap = Record>; + +export const GEMINI_3_IMAGE_RESOLUTION_MAP: ResolutionMap = { + '1:1': { '1K': { w: 1024, h: 1024 }, '2K': { w: 2048, h: 2048 }, '4K': { w: 4096, h: 4096 } }, + '2:3': { '1K': { w: 848, h: 1264 }, '2K': { w: 1696, h: 2528 }, '4K': { w: 3392, h: 5096 } }, + '3:2': { '1K': { w: 1264, h: 848 }, '2K': { w: 2528, h: 1696 }, '4K': { w: 5096, h: 3392 } }, + '3:4': { '1K': { w: 896, h: 1200 }, '2K': { w: 1792, h: 2400 }, '4K': { w: 3584, h: 4800 } }, + '4:3': { '1K': { w: 1200, h: 896 }, '2K': { w: 2400, h: 1792 }, '4K': { w: 4800, h: 3584 } }, + '4:5': { '1K': { w: 928, h: 1152 }, '2K': { w: 1856, h: 2304 }, '4K': { w: 3712, h: 4608 } }, + '5:4': { '1K': { w: 1152, h: 928 }, '2K': { w: 2304, h: 1856 }, '4K': { w: 4608, h: 3712 } }, + '9:16': { '1K': { w: 768, h: 1376 }, '2K': { w: 1536, h: 2752 }, '4K': { w: 3072, h: 5504 } }, + '16:9': { '1K': { w: 1376, h: 768 }, '2K': { w: 2752, h: 1536 }, '4K': { w: 5504, h: 3072 } }, + '21:9': { '1K': { w: 1584, h: 672 }, '2K': { w: 3168, h: 1344 }, '4K': { w: 6336, h: 2688 } }, +}; + +export const FLASH_IMAGE_3_1_RESOLUTION_MAP: ResolutionMap = { + '1:1': { '0.5K': { w: 512, h: 512 }, '1K': { w: 1024, h: 1024 }, '2K': { w: 2048, h: 2048 }, '4K': { w: 4096, h: 4096 } }, + '1:4': { '0.5K': { w: 256, h: 1024 }, '1K': { w: 512, h: 2048 }, '2K': { w: 1024, h: 4096 }, '4K': { w: 2048, h: 8192 } }, + '1:8': { '0.5K': { w: 192, h: 1536 }, '1K': { w: 384, h: 3072 }, '2K': { w: 768, h: 6144 }, '4K': { w: 1536, h: 12288 } }, + '2:3': { '0.5K': { w: 424, h: 632 }, '1K': { w: 848, h: 1264 }, '2K': { w: 1696, h: 2528 }, '4K': { w: 3392, h: 5056 } }, + '3:2': { '0.5K': { w: 632, h: 424 }, '1K': { w: 1264, h: 848 }, '2K': { w: 2528, h: 1696 }, '4K': { w: 5056, h: 3392 } }, + '3:4': { '0.5K': { w: 448, h: 600 }, '1K': { w: 896, h: 1200 }, '2K': { w: 1792, h: 2400 }, '4K': { w: 3584, h: 4800 } }, + '4:1': { '0.5K': { w: 1024, h: 256 }, '1K': { w: 2048, h: 512 }, '2K': { w: 4096, h: 1024 }, '4K': { w: 8192, h: 2048 } }, + '4:3': { '0.5K': { w: 600, h: 448 }, '1K': { w: 1200, h: 896 }, '2K': { w: 2400, h: 1792 }, '4K': { w: 4800, h: 3584 } }, + '4:5': { '0.5K': { w: 464, h: 576 }, '1K': { w: 928, h: 1152 }, '2K': { w: 1856, h: 2304 }, '4K': { w: 3712, h: 4608 } }, + '5:4': { '0.5K': { w: 576, h: 464 }, '1K': { w: 1152, h: 928 }, '2K': { w: 2304, h: 1856 }, '4K': { w: 4608, h: 3712 } }, + '8:1': { '0.5K': { w: 1536, h: 192 }, '1K': { w: 3072, h: 384 }, '2K': { w: 6144, h: 768 }, '4K': { w: 12288, h: 1536 } }, + '9:16': { '0.5K': { w: 384, h: 688 }, '1K': { w: 768, h: 1376 }, '2K': { w: 1536, h: 2752 }, '4K': { w: 3072, h: 5504 } }, + '16:9': { '0.5K': { w: 688, h: 384 }, '1K': { w: 1376, h: 768 }, '2K': { w: 2752, h: 1536 }, '4K': { w: 5504, h: 3072 } }, + '21:9': { '0.5K': { w: 792, h: 168 }, '1K': { w: 1584, h: 672 }, '2K': { w: 3168, h: 1344 }, '4K': { w: 6336, h: 2688 } }, +}; + export const TOGETHER_IMAGE_GENERATION_MODELS: IImageModel[] = [ { id: 'togetherai:ByteDance-Seed/Seedream-3.0', - aliases: ['ByteDance-Seed/Seedream-3.0'], + aliases: ['ByteDance-Seed/Seedream-3.0', 'Seedream-3.0'], costs_currency: 'usd-cents', index_cost_key: '1MP', name: 'ByteDance-Seed/Seedream-3.0', allowedQualityLevels: [''], + pricing_unit: 'per-MP', costs: { '1MP': 1.8 }, }, { id: 'togetherai:ByteDance-Seed/Seedream-4.0', - aliases: ['ByteDance-Seed/Seedream-4.0'], + aliases: ['ByteDance-Seed/Seedream-4.0', 'Seedream-4.0'], costs_currency: 'usd-cents', index_cost_key: '1MP', name: 'ByteDance-Seed/Seedream-4.0', allowedQualityLevels: [''], + pricing_unit: 'per-MP', costs: { '1MP': 3 }, }, { id: 'togetherai:HiDream-ai/HiDream-I1-Dev', - aliases: ['HiDream-ai/HiDream-I1-Dev'], + aliases: ['HiDream-ai/HiDream-I1-Dev', 'HiDream-I1-Dev'], costs_currency: 'usd-cents', index_cost_key: '1MP', name: 'HiDream-ai/HiDream-I1-Dev', allowedQualityLevels: [''], + pricing_unit: 'per-MP', costs: { '1MP': 0.45 }, }, { id: 'togetherai:HiDream-ai/HiDream-I1-Fast', - aliases: ['HiDream-ai/HiDream-I1-Fast'], + aliases: ['HiDream-ai/HiDream-I1-Fast', 'HiDream-I1-Fast'], costs_currency: 'usd-cents', index_cost_key: '1MP', name: 'HiDream-ai/HiDream-I1-Fast', allowedQualityLevels: [''], + pricing_unit: 'per-MP', costs: { '1MP': 0.32 }, }, { id: 'togetherai:HiDream-ai/HiDream-I1-Full', - aliases: ['HiDream-ai/HiDream-I1-Full'], + aliases: ['HiDream-ai/HiDream-I1-Full', 'HiDream-I1-Full'], costs_currency: 'usd-cents', index_cost_key: '1MP', name: 'HiDream-ai/HiDream-I1-Full', allowedQualityLevels: [''], + pricing_unit: 'per-MP', costs: { '1MP': 0.9 }, }, { id: 'togetherai:Lykon/DreamShaper', - aliases: ['Lykon/DreamShaper'], + aliases: ['Lykon/DreamShaper', 'DreamShaper'], costs_currency: 'usd-cents', index_cost_key: '1MP', name: 'Lykon/DreamShaper', allowedQualityLevels: [''], + pricing_unit: 'per-MP', costs: { '1MP': 0.06 }, }, { id: 'togetherai:Qwen/Qwen-Image', - aliases: ['Qwen/Qwen-Image'], + aliases: ['Qwen/Qwen-Image', 'Qwen-Image'], costs_currency: 'usd-cents', index_cost_key: '1MP', name: 'Qwen/Qwen-Image', allowedQualityLevels: [''], + pricing_unit: 'per-MP', costs: { '1MP': 0.58 }, }, + { + id: 'togetherai:Qwen/Qwen-Image-2.0', + aliases: ['Qwen/Qwen-Image-2.0', 'Qwen-Image-2.0'], + costs_currency: 'usd-cents', + index_cost_key: 'per-image', + name: 'Qwen/Qwen-Image-2.0', + allowedQualityLevels: [''], + pricing_unit: 'per-image', + costs: { 'per-image': 4 }, + }, + { + id: 'togetherai:Qwen/Qwen-Image-2.0-Pro', + aliases: ['Qwen/Qwen-Image-2.0-Pro', 'Qwen-Image-2.0-Pro'], + costs_currency: 'usd-cents', + index_cost_key: 'per-image', + name: 'Qwen/Qwen-Image-2.0-Pro', + allowedQualityLevels: [''], + pricing_unit: 'per-image', + costs: { 'per-image': 8 }, + }, { id: 'togetherai:RunDiffusion/Juggernaut-pro-flux', - aliases: ['RunDiffusion/Juggernaut-pro-flux'], + aliases: ['RunDiffusion/Juggernaut-pro-flux', 'Juggernaut-pro-flux'], costs_currency: 'usd-cents', index_cost_key: '1MP', name: 'RunDiffusion/Juggernaut-pro-flux', allowedQualityLevels: [''], + pricing_unit: 'per-MP', costs: { '1MP': 0.49 }, }, { id: 'togetherai:Rundiffusion/Juggernaut-Lightning-Flux', - aliases: ['Rundiffusion/Juggernaut-Lightning-Flux'], + aliases: ['Rundiffusion/Juggernaut-Lightning-Flux', 'Juggernaut-Lightning-Flux'], costs_currency: 'usd-cents', index_cost_key: '1MP', name: 'Rundiffusion/Juggernaut-Lightning-Flux', allowedQualityLevels: [''], + pricing_unit: 'per-MP', costs: { '1MP': 0.17 }, }, { - id: 'togetherai:black-forest-labs/FLUX.1-dev', - aliases: ['black-forest-labs/FLUX.1-dev'], + id: 'togetherai:Wan-AI/Wan2.6-image', + aliases: ['Wan-AI/Wan2.6-image', 'Wan2.6-image'], costs_currency: 'usd-cents', - index_cost_key: '1MP', - name: 'black-forest-labs/FLUX.1-dev', + index_cost_key: 'per-image', + name: 'Wan-AI/Wan2.6-image', allowedQualityLevels: [''], - costs: { '1MP': 2.5 }, - }, - { - id: 'togetherai:black-forest-labs/FLUX.1-dev-lora', - aliases: ['black-forest-labs/FLUX.1-dev-lora'], - costs_currency: 'usd-cents', - index_cost_key: '1MP', - name: 'black-forest-labs/FLUX.1-dev-lora', - allowedQualityLevels: [''], - costs: { '1MP': 2.5 }, - }, - { - id: 'togetherai:black-forest-labs/FLUX.1-kontext-dev', - aliases: ['black-forest-labs/FLUX.1-kontext-dev'], - costs_currency: 'usd-cents', - index_cost_key: '1MP', - name: 'black-forest-labs/FLUX.1-kontext-dev', - allowedQualityLevels: [''], - costs: { '1MP': 2.5 }, + pricing_unit: 'per-image', + costs: { 'per-image': 3 }, }, { id: 'togetherai:black-forest-labs/FLUX.1-kontext-max', - aliases: ['black-forest-labs/FLUX.1-kontext-max'], + aliases: ['black-forest-labs/FLUX.1-kontext-max', 'FLUX.1-kontext-max'], costs_currency: 'usd-cents', index_cost_key: '1MP', name: 'black-forest-labs/FLUX.1-kontext-max', allowedQualityLevels: [''], + pricing_unit: 'per-MP', costs: { '1MP': 8 }, }, { id: 'togetherai:black-forest-labs/FLUX.1-kontext-pro', - aliases: ['black-forest-labs/FLUX.1-kontext-pro'], + aliases: ['black-forest-labs/FLUX.1-kontext-pro', 'FLUX.1-kontext-pro'], costs_currency: 'usd-cents', index_cost_key: '1MP', name: 'black-forest-labs/FLUX.1-kontext-pro', allowedQualityLevels: [''], + pricing_unit: 'per-MP', costs: { '1MP': 4 }, }, { id: 'togetherai:black-forest-labs/FLUX.1-krea-dev', - aliases: ['black-forest-labs/FLUX.1-krea-dev'], + aliases: ['black-forest-labs/FLUX.1-krea-dev', 'FLUX.1-krea-dev'], costs_currency: 'usd-cents', index_cost_key: '1MP', name: 'black-forest-labs/FLUX.1-krea-dev', allowedQualityLevels: [''], + pricing_unit: 'per-MP', costs: { '1MP': 2.5 }, }, - { - id: 'togetherai:black-forest-labs/FLUX.1-pro', - aliases: ['black-forest-labs/FLUX.1-pro'], - costs_currency: 'usd-cents', - index_cost_key: '1MP', - name: 'black-forest-labs/FLUX.1-pro', - allowedQualityLevels: [''], - costs: { '1MP': 5 }, - }, { id: 'togetherai:black-forest-labs/FLUX.1-schnell', - aliases: ['black-forest-labs/FLUX.1-schnell'], + aliases: ['black-forest-labs/FLUX.1-schnell', 'FLUX.1-schnell'], costs_currency: 'usd-cents', index_cost_key: '1MP', name: 'black-forest-labs/FLUX.1-schnell', allowedQualityLevels: [''], + pricing_unit: 'per-MP', costs: { '1MP': 0.27 }, }, { id: 'togetherai:black-forest-labs/FLUX.1.1-pro', - aliases: ['black-forest-labs/FLUX.1.1-pro'], + aliases: ['black-forest-labs/FLUX.1.1-pro', 'FLUX.1.1-pro'], costs_currency: 'usd-cents', index_cost_key: '1MP', name: 'black-forest-labs/FLUX.1.1-pro', allowedQualityLevels: [''], + pricing_unit: 'per-MP', costs: { '1MP': 4 }, }, { - id: 'togetherai:black-forest-labs/FLUX.2-pro', - aliases: ['black-forest-labs/FLUX.2-pro'], + id: 'togetherai:black-forest-labs/FLUX.2-dev', + aliases: ['black-forest-labs/FLUX.2-dev', 'FLUX.2-dev'], costs_currency: 'usd-cents', - index_cost_key: '1MP', - name: 'black-forest-labs/FLUX.2-pro', + index_cost_key: 'per-image', + name: 'black-forest-labs/FLUX.2-dev', allowedQualityLevels: [''], - costs: { '1MP': 3 }, + pricing_unit: 'per-image', + costs: { 'per-image': 1.54 }, }, { id: 'togetherai:black-forest-labs/FLUX.2-flex', - aliases: ['black-forest-labs/FLUX.2-flex'], + aliases: ['black-forest-labs/FLUX.2-flex', 'FLUX.2-flex'], costs_currency: 'usd-cents', - index_cost_key: '1MP', + index_cost_key: 'per-image', name: 'black-forest-labs/FLUX.2-flex', allowedQualityLevels: [''], - costs: { '1MP': 3 }, + pricing_unit: 'per-image', + costs: { 'per-image': 3 }, }, { - id: 'togetherai:black-forest-labs/FLUX.2-dev', - aliases: ['black-forest-labs/FLUX.2-dev'], + id: 'togetherai:black-forest-labs/FLUX.2-max', + aliases: ['black-forest-labs/FLUX.2-max', 'FLUX.2-max'], costs_currency: 'usd-cents', index_cost_key: '1MP', - name: 'black-forest-labs/FLUX.2-dev', + name: 'black-forest-labs/FLUX.2-max', allowedQualityLevels: [''], - costs: { '1MP': 3 }, + pricing_unit: 'per-MP', + costs: { '1MP': 7 }, + }, + { + id: 'togetherai:black-forest-labs/FLUX.2-pro', + aliases: ['black-forest-labs/FLUX.2-pro', 'FLUX.2-pro'], + costs_currency: 'usd-cents', + index_cost_key: 'per-image', + name: 'black-forest-labs/FLUX.2-pro', + allowedQualityLevels: [''], + pricing_unit: 'per-image', + costs: { 'per-image': 3 }, }, { id: 'togetherai:google/flash-image-2.5', - aliases: ['google/flash-image-2.5'], + aliases: ['google/flash-image-2.5', 'flash-image-2.5'], costs_currency: 'usd-cents', - index_cost_key: '1MP', + index_cost_key: 'per-image', name: 'google/flash-image-2.5', allowedQualityLevels: ['1K'], allowedRatios: [ @@ -228,9 +281,36 @@ export const TOGETHER_IMAGE_GENERATION_MODELS: IImageModel[] = [ { w: 1344, h: 768 }, { w: 1536, h: 672 }, { w: 672, h: 1536 }, - ], - costs: { '1MP': 3.91 }, + pricing_unit: 'per-image', + costs: { 'per-image': 3.9 }, + }, + { + id: 'togetherai:google/flash-image-3.1', + aliases: ['google/flash-image-3.1', 'flash-image-3.1', 'nano-banana-2'], + name: 'google/flash-image-3.1', + costs_currency: 'usd-cents', + index_cost_key: '1K', + allowedQualityLevels: ['0.5K', '1K', '2K', '4K'], + allowedRatios: [ + { w: 1, h: 1 }, + { w: 2, h: 3 }, + { w: 3, h: 2 }, + { w: 3, h: 4 }, + { w: 4, h: 3 }, + { w: 4, h: 5 }, + { w: 5, h: 4 }, + { w: 9, h: 16 }, + { w: 16, h: 9 }, + { w: 21, h: 9 }, + { w: 1, h: 4 }, + { w: 4, h: 1 }, + { w: 1, h: 8 }, + { w: 8, h: 1 }, + ], + pricing_unit: 'per-tier', + costs: { '0.5K': 4.5, '1K': 6.7, '2K': 10.1, '4K': 15.1 }, + resolution_map: FLASH_IMAGE_3_1_RESOLUTION_MAP, }, { id: 'togetherai:google/gemini-3-pro-image', @@ -251,82 +331,78 @@ export const TOGETHER_IMAGE_GENERATION_MODELS: IImageModel[] = [ { w: 16, h: 9 }, { w: 21, h: 9 }, ], + pricing_unit: 'per-tier', costs: { '1K': 13.4, '2K': 13.4, '4K': 24 }, + resolution_map: GEMINI_3_IMAGE_RESOLUTION_MAP, }, { id: 'togetherai:google/imagen-4.0-fast', - aliases: ['google/imagen-4.0-fast'], + aliases: ['google/imagen-4.0-fast', 'imagen-4.0-fast'], costs_currency: 'usd-cents', index_cost_key: '1MP', name: 'google/imagen-4.0-fast', allowedQualityLevels: [''], + pricing_unit: 'per-MP', costs: { '1MP': 2 }, }, { id: 'togetherai:google/imagen-4.0-preview', - aliases: ['google/imagen-4.0-preview'], + aliases: ['google/imagen-4.0-preview', 'imagen-4.0-preview'], costs_currency: 'usd-cents', index_cost_key: '1MP', name: 'google/imagen-4.0-preview', allowedQualityLevels: [''], + pricing_unit: 'per-MP', costs: { '1MP': 4 }, }, { id: 'togetherai:google/imagen-4.0-ultra', - aliases: ['google/imagen-4.0-ultra'], + aliases: ['google/imagen-4.0-ultra', 'imagen-4.0-ultra'], costs_currency: 'usd-cents', index_cost_key: '1MP', name: 'google/imagen-4.0-ultra', allowedQualityLevels: [''], - costs: { '1MP': 6.02 }, + pricing_unit: 'per-MP', + costs: { '1MP': 6 }, }, { id: 'togetherai:ideogram/ideogram-3.0', - aliases: ['ideogram/ideogram-3.0'], + aliases: ['ideogram/ideogram-3.0', 'ideogram-3.0'], costs_currency: 'usd-cents', index_cost_key: '1MP', name: 'ideogram/ideogram-3.0', allowedQualityLevels: [''], - costs: { '1MP': 6.02 }, + pricing_unit: 'per-MP', + costs: { '1MP': 6 }, + }, + { + id: 'togetherai:openai/gpt-image-1.5', + aliases: ['openai/gpt-image-1.5', 'gpt-image-1.5'], + costs_currency: 'usd-cents', + index_cost_key: 'per-image', + name: 'openai/gpt-image-1.5', + allowedQualityLevels: [''], + pricing_unit: 'per-image', + costs: { 'per-image': 3.4 }, }, { id: 'togetherai:stabilityai/stable-diffusion-3-medium', - aliases: ['stabilityai/stable-diffusion-3-medium'], + aliases: ['stabilityai/stable-diffusion-3-medium', 'stable-diffusion-3-medium'], costs_currency: 'usd-cents', index_cost_key: '1MP', name: 'stabilityai/stable-diffusion-3-medium', allowedQualityLevels: [''], + pricing_unit: 'per-MP', costs: { '1MP': 0.19 }, }, { id: 'togetherai:stabilityai/stable-diffusion-xl-base-1.0', - aliases: ['stabilityai/stable-diffusion-xl-base-1.0'], + aliases: ['stabilityai/stable-diffusion-xl-base-1.0', 'stable-diffusion-xl-base-1.0'], costs_currency: 'usd-cents', index_cost_key: '1MP', name: 'stabilityai/stable-diffusion-xl-base-1.0', allowedQualityLevels: [''], + pricing_unit: 'per-MP', costs: { '1MP': 0.19 }, }, - { - id: 'togetherai:black-forest-labs/FLUX.2-max', - aliases: ['black-forest-labs/FLUX.2-max', 'FLUX.2-max'], - costs_currency: 'usd-cents', - index_cost_key: '1MP', - name: 'black-forest-labs/FLUX.2-max', - allowedQualityLevels: [''], - costs: { '1MP': 7 }, - }, ]; - -export const GEMINI_3_IMAGE_RESOLUTION_MAP: Record> = { - '1:1': { '1K': { w: 1024, h: 1024 }, '2K': { w: 2048, h: 2048 }, '4K': { w: 4096, h: 4096 } }, - '2:3': { '1K': { w: 848, h: 1264 }, '2K': { w: 1696, h: 2528 }, '4K': { w: 3392, h: 5096 } }, - '3:2': { '1K': { w: 1264, h: 848 }, '2K': { w: 2528, h: 1696 }, '4K': { w: 5096, h: 3392 } }, - '3:4': { '1K': { w: 896, h: 1200 }, '2K': { w: 1792, h: 2400 }, '4K': { w: 3584, h: 4800 } }, - '4:3': { '1K': { w: 1200, h: 896 }, '2K': { w: 2400, h: 1792 }, '4K': { w: 4800, h: 3584 } }, - '4:5': { '1K': { w: 928, h: 1152 }, '2K': { w: 1856, h: 2304 }, '4K': { w: 3712, h: 4608 } }, - '5:4': { '1K': { w: 1152, h: 928 }, '2K': { w: 2304, h: 1856 }, '4K': { w: 4608, h: 3712 } }, - '9:16': { '1K': { w: 768, h: 1376 }, '2K': { w: 1536, h: 2752 }, '4K': { w: 3072, h: 5504 } }, - '16:9': { '1K': { w: 1376, h: 768 }, '2K': { w: 2752, h: 1536 }, '4K': { w: 5504, h: 3072 } }, - '21:9': { '1K': { w: 1584, h: 672 }, '2K': { w: 3168, h: 1344 }, '4K': { w: 6336, h: 2688 } }, -}; diff --git a/src/backend/src/services/ai/image/providers/types.ts b/src/backend/src/services/ai/image/providers/types.ts index eac489101..ffe3ca67d 100644 --- a/src/backend/src/services/ai/image/providers/types.ts +++ b/src/backend/src/services/ai/image/providers/types.ts @@ -1,3 +1,5 @@ +export type ImagePricingUnit = 'per-image' | 'per-MP' | 'per-tier'; + export interface IImageModel { id: string; name: string; @@ -10,6 +12,20 @@ export interface IImageModel { index_cost_key?: string; index_input_cost_key?: string; costs: Record; + /** + * How `costs` should be interpreted: + * - 'per-image': flat cost per generated image (key: 'per-image') + * - 'per-MP': cost scales with width*height/1e6 (key: '1MP') + * - 'per-tier': cost is picked by `quality` (keys: e.g. '1K','2K','4K') + * Defaults to 'per-MP' when unset (legacy behavior). + */ + pricing_unit?: ImagePricingUnit; + /** + * For per-tier models: resolves an abstract aspect ratio (keyed `{w}:{h}`) + * + quality tier (e.g. '1K'/'2K'/'4K') to concrete pixel dimensions sent + * to the provider. Only consulted when `pricing_unit === 'per-tier'`. + */ + resolution_map?: Record>; allowedQualityLevels?: string[]; allowedRatios?: { w: number, h: number }[]; } diff --git a/src/backend/src/services/ai/video/providers/TogetherVideoGenerationProvider/models.ts b/src/backend/src/services/ai/video/providers/TogetherVideoGenerationProvider/models.ts index 3563621c7..913bd1791 100644 --- a/src/backend/src/services/ai/video/providers/TogetherVideoGenerationProvider/models.ts +++ b/src/backend/src/services/ai/video/providers/TogetherVideoGenerationProvider/models.ts @@ -54,7 +54,7 @@ export const TOGETHER_VIDEO_GENERATION_MODELS: ITogetherVideoModel[] = [ name: 'MiniMax Hailuo 02', model: 'minimax/hailuo-02', costs_currency: 'usd-cents', - costs: { 'per-video': 56 }, + costs: { 'per-video': 49 }, output_cost_key: 'per-video', durationSeconds: [10], dimensions: ['1366x768', '1920x1080'], @@ -362,6 +362,22 @@ export const TOGETHER_VIDEO_GENERATION_MODELS: ITogetherVideoModel[] = [ promptLength: null, promptSupported: null, }, + { + id: 'togetherai:Wan-AI/wan2.7-t2v', + puterId: 'togetherai:wan-ai/wan2.7-t2v', + organization: 'Wan-AI', + name: 'Wan 2.7 T2V', + model: 'Wan-AI/wan2.7-t2v', + costs_currency: 'usd-cents', + costs: { 'per-video': 10 }, + output_cost_key: 'per-video', + durationSeconds: null, + dimensions: null, + fps: null, + keyframes: null, + promptLength: null, + promptSupported: null, + }, { id: 'togetherai:vidu/vidu-2.0', puterId: 'togetherai:vidu/vidu-2.0', @@ -427,7 +443,7 @@ export const TOGETHER_VIDEO_GENERATION_MODELS: ITogetherVideoModel[] = [ name: 'Sora 2 Pro', model: 'openai/sora-2-pro', costs_currency: 'usd-cents', - costs: { 'per-video': 400 }, + costs: { 'per-video': 300 }, output_cost_key: 'per-video', durationSeconds: [8], dimensions: ['1280x720', '720x1280'],