{"version":3,"sources":["../src/models.ts","../src/calculator.ts"],"sourcesContent":["/**\n * Model configurations for different OpenAI Vision models\n */\n\nexport interface ModelConfig {\n  /**\n   * Model identifier\n   */\n  id: string;\n  \n  /**\n   * Human-readable model name\n   */\n  name: string;\n  \n  /**\n   * Base tokens for high detail mode\n   */\n  baseTokens: number;\n  \n  /**\n   * Tokens per tile for high detail mode\n   */\n  tokensPerTile: number;\n  \n  /**\n   * Size of each tile in pixels\n   */\n  tileSize: number;\n  \n  /**\n   * Maximum dimension before scaling\n   */\n  maxDimension: number;\n  \n  /**\n   * Target size for shortest side\n   */\n  shortSideTarget: number;\n  \n  /**\n   * Tokens for low detail mode\n   */\n  lowDetailTokens: number;\n  \n  /**\n   * Default cost per 1k input tokens in USD\n   */\n  defaultCostPer1kTokens: number;\n  \n  /**\n   * Whether this model supports vision\n   */\n  supportsVision: boolean;\n  \n  /**\n   * Additional notes about the model\n   */\n  notes?: string;\n}\n\n/**\n * Available models\n */\nexport const MODELS: Record<string, ModelConfig> = {\n  'gpt-4o': {\n    id: 'gpt-4o',\n    name: 'GPT-4o',\n    baseTokens: 85,\n    tokensPerTile: 170,\n    tileSize: 512,\n    maxDimension: 2048,\n    shortSideTarget: 768,\n    lowDetailTokens: 85,\n    defaultCostPer1kTokens: 5.00,\n    supportsVision: true,\n    notes: 'Latest multimodal model with vision capabilities'\n  },\n  \n  'gpt-4o-2024-05-13': {\n    id: 'gpt-4o-2024-05-13',\n    name: 'GPT-4o (2024-05-13)',\n    baseTokens: 85,\n    tokensPerTile: 170,\n    tileSize: 512,\n    maxDimension: 2048,\n    shortSideTarget: 768,\n    lowDetailTokens: 85,\n    defaultCostPer1kTokens: 5.00,\n    supportsVision: true,\n    notes: 'Specific version of GPT-4o'\n  },\n  \n  'gpt-4o-mini': {\n    id: 'gpt-4o-mini',\n    name: 'GPT-4o Mini',\n    baseTokens: 85,\n    tokensPerTile: 170,\n    tileSize: 512,\n    maxDimension: 2048,\n    shortSideTarget: 768,\n    lowDetailTokens: 85,\n    defaultCostPer1kTokens: 0.15,\n    supportsVision: true,\n    notes: 'Smaller, more affordable version of GPT-4o'\n  },\n  \n  'gpt-4-vision-preview': {\n    id: 'gpt-4-vision-preview',\n    name: 'GPT-4 Vision Preview',\n    baseTokens: 85,\n    tokensPerTile: 170,\n    tileSize: 512,\n    maxDimension: 2048,\n    shortSideTarget: 768,\n    lowDetailTokens: 85,\n    defaultCostPer1kTokens: 10.00,\n    supportsVision: true,\n    notes: 'Original GPT-4 with vision capabilities'\n  },\n  \n  'gpt-4-turbo': {\n    id: 'gpt-4-turbo',\n    name: 'GPT-4 Turbo',\n    baseTokens: 85,\n    tokensPerTile: 170,\n    tileSize: 512,\n    maxDimension: 2048,\n    shortSideTarget: 768,\n    lowDetailTokens: 85,\n    defaultCostPer1kTokens: 10.00,\n    supportsVision: true,\n    notes: 'GPT-4 Turbo with vision support'\n  },\n  \n  'gpt-4-turbo-2024-04-09': {\n    id: 'gpt-4-turbo-2024-04-09',\n    name: 'GPT-4 Turbo (2024-04-09)',\n    baseTokens: 85,\n    tokensPerTile: 170,\n    tileSize: 512,\n    maxDimension: 2048,\n    shortSideTarget: 768,\n    lowDetailTokens: 85,\n    defaultCostPer1kTokens: 10.00,\n    supportsVision: true,\n    notes: 'Specific version of GPT-4 Turbo with vision'\n  },\n  \n  'gpt-4-1106-vision-preview': {\n    id: 'gpt-4-1106-vision-preview',\n    name: 'GPT-4 Vision (1106)',\n    baseTokens: 85,\n    tokensPerTile: 170,\n    tileSize: 512,\n    maxDimension: 2048,\n    shortSideTarget: 768,\n    lowDetailTokens: 85,\n    defaultCostPer1kTokens: 10.00,\n    supportsVision: true,\n    notes: 'November 2023 version of GPT-4 Vision'\n  }\n};\n\n/**\n * Get a model configuration by ID\n */\nexport function getModel(modelId: string): ModelConfig | undefined {\n  return MODELS[modelId];\n}\n\n/**\n * Get all available model IDs\n */\nexport function getAvailableModels(): string[] {\n  return Object.keys(MODELS);\n}\n\n/**\n * Get all vision-enabled models\n */\nexport function getVisionModels(): ModelConfig[] {\n  return Object.values(MODELS).filter(model => model.supportsVision);\n}\n\n/**\n * Default model ID\n */\nexport const DEFAULT_MODEL = 'gpt-4o';","import { ImageTokenInput, ImageTokenResult, PricingConfig, BatchCalculationResult } from './types';\nimport { getModel, DEFAULT_MODEL, ModelConfig } from './models';\n\n/**\n * Calculate the number of tokens consumed by an image in OpenAI Vision models\n * Based on OpenAI's documentation: https://platform.openai.com/docs/guides/images-vision\n * \n * @param input - Image dimensions, detail level, and model\n * @param pricing - Optional pricing configuration (overrides model default)\n * @returns Token count and cost calculation\n */\nexport function calculateImageTokens(\n  input: ImageTokenInput,\n  pricing?: PricingConfig\n): ImageTokenResult {\n  const { width, height, detail = 'high', model = DEFAULT_MODEL } = input;\n  \n  // Validate input\n  if (width <= 0 || height <= 0) {\n    throw new Error('Image dimensions must be positive numbers');\n  }\n  \n  // Get model configuration\n  const modelConfig = getModel(model);\n  if (!modelConfig) {\n    throw new Error(`Unknown model: ${model}. Available models: ${Object.keys(getModel).join(', ')}`);\n  }\n  \n  if (!modelConfig.supportsVision) {\n    throw new Error(`Model ${model} does not support vision`);\n  }\n  \n  // Use provided pricing or model default\n  const costPer1kTokens = pricing?.costPer1kTokens ?? modelConfig.defaultCostPer1kTokens;\n  \n  // For low detail, always return configured low detail tokens\n  if (detail === 'low') {\n    const tokens = modelConfig.lowDetailTokens;\n    const cost = (tokens / 1000) * costPer1kTokens;\n    \n    return {\n      tokens,\n      cost,\n      model: modelConfig.id,\n      modelName: modelConfig.name,\n      details: {\n        originalSize: { width, height },\n        resizedSize: { width: 512, height: 512 },\n        tiles: { width: 1, height: 1, total: 1 },\n        baseTokens: tokens,\n        tokensPerTile: 0,\n        detailLevel: 'low'\n      }\n    };\n  }\n  \n  // For high detail or auto (which defaults to high)\n  const { resizedWidth, resizedHeight } = resizeImage(width, height, modelConfig);\n  \n  // Calculate number of tiles\n  const tilesWidth = Math.ceil(resizedWidth / modelConfig.tileSize);\n  const tilesHeight = Math.ceil(resizedHeight / modelConfig.tileSize);\n  const totalTiles = tilesWidth * tilesHeight;\n  \n  // Calculate total tokens\n  const tokens = modelConfig.baseTokens + (modelConfig.tokensPerTile * totalTiles);\n  \n  // Calculate cost\n  const cost = (tokens / 1000) * costPer1kTokens;\n  \n  return {\n    tokens,\n    cost,\n    model: modelConfig.id,\n    modelName: modelConfig.name,\n    details: {\n      originalSize: { width, height },\n      resizedSize: { width: resizedWidth, height: resizedHeight },\n      tiles: {\n        width: tilesWidth,\n        height: tilesHeight,\n        total: totalTiles\n      },\n      baseTokens: modelConfig.baseTokens,\n      tokensPerTile: modelConfig.tokensPerTile,\n      detailLevel: detail === 'auto' ? 'high' : detail\n    }\n  };\n}\n\n/**\n * Resize image dimensions according to model rules\n * \n * @param width - Original width\n * @param height - Original height\n * @param modelConfig - Model configuration\n * @returns Resized dimensions\n */\nfunction resizeImage(\n  width: number, \n  height: number, \n  modelConfig: ModelConfig\n): { resizedWidth: number; resizedHeight: number } {\n  let resizedWidth = width;\n  let resizedHeight = height;\n  \n  // Step 1: Scale down to fit within max dimension square if necessary\n  if (resizedWidth > modelConfig.maxDimension || resizedHeight > modelConfig.maxDimension) {\n    const aspectRatio = resizedWidth / resizedHeight;\n    \n    if (aspectRatio > 1) {\n      // Width is larger\n      resizedWidth = modelConfig.maxDimension;\n      resizedHeight = Math.floor(modelConfig.maxDimension / aspectRatio);\n    } else {\n      // Height is larger or square\n      resizedHeight = modelConfig.maxDimension;\n      resizedWidth = Math.floor(modelConfig.maxDimension * aspectRatio);\n    }\n  }\n  \n  // Step 2: Scale so shortest side is target size if both dimensions exceed target\n  if (resizedWidth > modelConfig.shortSideTarget && resizedHeight > modelConfig.shortSideTarget) {\n    const aspectRatio = resizedWidth / resizedHeight;\n    \n    if (aspectRatio > 1) {\n      // Width is larger, scale height to target\n      resizedHeight = modelConfig.shortSideTarget;\n      resizedWidth = Math.floor(modelConfig.shortSideTarget * aspectRatio);\n    } else {\n      // Height is larger or square, scale width to target\n      resizedWidth = modelConfig.shortSideTarget;\n      resizedHeight = Math.floor(modelConfig.shortSideTarget / aspectRatio);\n    }\n  }\n  \n  return { resizedWidth, resizedHeight };\n}\n\n/**\n * Calculate tokens for multiple images\n * \n * @param images - Array of image inputs\n * @param pricing - Optional pricing configuration (overrides model defaults)\n * @returns Array of results and total summary\n */\nexport function calculateBatchImageTokens(\n  images: ImageTokenInput[],\n  pricing?: PricingConfig\n): BatchCalculationResult {\n  const results = images.map(image => calculateImageTokens(image, pricing));\n  \n  const totalTokens = results.reduce((sum, result) => sum + result.tokens, 0);\n  const totalCost = results.reduce((sum, result) => sum + result.cost, 0);\n  \n  // Calculate breakdown by model\n  const byModel: Record<string, { count: number; tokens: number; cost: number }> = {};\n  \n  results.forEach(result => {\n    if (!byModel[result.model]) {\n      byModel[result.model] = { count: 0, tokens: 0, cost: 0 };\n    }\n    byModel[result.model].count++;\n    byModel[result.model].tokens += result.tokens;\n    byModel[result.model].cost += result.cost;\n  });\n  \n  return {\n    results,\n    summary: {\n      totalImages: images.length,\n      totalTokens,\n      totalCost,\n      byModel\n    }\n  };\n}\n\n/**\n * Get pricing for a specific model\n */\nexport function getPricing(model: string = DEFAULT_MODEL): PricingConfig {\n  const modelConfig = getModel(model);\n  if (!modelConfig) {\n    throw new Error(`Unknown model: ${model}`);\n  }\n  return { costPer1kTokens: modelConfig.defaultCostPer1kTokens };\n}\n\n/**\n * Create a custom pricing configuration\n */\nexport function createPricing(costPer1kTokens: number): PricingConfig {\n  if (costPer1kTokens <= 0) {\n    throw new Error('Cost per 1k tokens must be positive');\n  }\n  \n  return { costPer1kTokens };\n}"],"mappings":"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","names":["MODELS","getModel","modelId","getAvailableModels","getVisionModels","model","DEFAULT_MODEL","calculateImageTokens","input","pricing","width","height","detail","model","DEFAULT_MODEL","modelConfig","getModel","costPer1kTokens","tokens","cost","resizedWidth","resizedHeight","resizeImage","tilesWidth","tilesHeight","totalTiles","aspectRatio","calculateBatchImageTokens","images","results","image","totalTokens","sum","result","totalCost","byModel","getPricing","createPricing"]}