From 2a893b56cb4c57795085eb53220ee4d009c73281 Mon Sep 17 00:00:00 2001 From: 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 Date: Mon, 3 Aug 2026 15:01:40 +0300 Subject: [PATCH 1/3] feat: update Mistral pricing and add cost calculation tests --- src/tealtiger/cost/pricing.py | 405 +++++++++++++++++----------------- tests/cost/test_pricing.py | 42 ++++ 2 files changed, 241 insertions(+), 206 deletions(-) create mode 100644 tests/cost/test_pricing.py diff --git a/src/tealtiger/cost/pricing.py b/src/tealtiger/cost/pricing.py index 5eedf56..1858c03 100644 --- a/src/tealtiger/cost/pricing.py +++ b/src/tealtiger/cost/pricing.py @@ -1,385 +1,377 @@ """ -Model Pricing Data - -Up-to-date pricing information for major AI model providers -Last updated: January 2026 +Model Pricing Data: Up-to-date pricing information for major AI model providers +Last updated: August 2026 """ from typing import Dict, List, Optional + from .types import ModelPricing, ModelProvider # Pricing database for all supported models # Prices are in USD per 1K tokens MODEL_PRICING: Dict[str, ModelPricing] = { # OpenAI GPT-4 Models - 'gpt-4': ModelPricing( - model='gpt-4', - provider='openai', + "gpt-4": ModelPricing( + model="gpt-4", + provider="openai", input_cost_per_1k=0.03, output_cost_per_1k=0.06, - last_updated='2026-01-31' + last_updated="2026-01-31", ), - 'gpt-4-32k': ModelPricing( - model='gpt-4-32k', - provider='openai', + "gpt-4-32k": ModelPricing( + model="gpt-4-32k", + provider="openai", input_cost_per_1k=0.06, output_cost_per_1k=0.12, - last_updated='2026-01-31' + last_updated="2026-01-31", ), - 'gpt-4-turbo': ModelPricing( - model='gpt-4-turbo', - provider='openai', + "gpt-4-turbo": ModelPricing( + model="gpt-4-turbo", + provider="openai", input_cost_per_1k=0.01, output_cost_per_1k=0.03, - last_updated='2026-01-31' + last_updated="2026-01-31", ), - 'gpt-4-turbo-preview': ModelPricing( - model='gpt-4-turbo-preview', - provider='openai', + "gpt-4-turbo-preview": ModelPricing( + model="gpt-4-turbo-preview", + provider="openai", input_cost_per_1k=0.01, output_cost_per_1k=0.03, - last_updated='2026-01-31' + last_updated="2026-01-31", ), - 'gpt-4-vision-preview': ModelPricing( - model='gpt-4-vision-preview', - provider='openai', + "gpt-4-vision-preview": ModelPricing( + model="gpt-4-vision-preview", + provider="openai", input_cost_per_1k=0.01, output_cost_per_1k=0.03, image_cost=0.00765, # per image (1024x1024) - last_updated='2026-01-31' + last_updated="2026-01-31", ), - # OpenAI GPT-3.5 Models - 'gpt-3.5-turbo': ModelPricing( - model='gpt-3.5-turbo', - provider='openai', + "gpt-3.5-turbo": ModelPricing( + model="gpt-3.5-turbo", + provider="openai", input_cost_per_1k=0.0005, output_cost_per_1k=0.0015, - last_updated='2026-01-31' + last_updated="2026-01-31", ), - 'gpt-3.5-turbo-16k': ModelPricing( - model='gpt-3.5-turbo-16k', - provider='openai', + "gpt-3.5-turbo-16k": ModelPricing( + model="gpt-3.5-turbo-16k", + provider="openai", input_cost_per_1k=0.003, output_cost_per_1k=0.004, - last_updated='2026-01-31' + last_updated="2026-01-31", ), - # Anthropic Claude Models - 'claude-3-opus-20240229': ModelPricing( - model='claude-3-opus-20240229', - provider='anthropic', + "claude-3-opus-20240229": ModelPricing( + model="claude-3-opus-20240229", + provider="anthropic", input_cost_per_1k=0.015, output_cost_per_1k=0.075, - last_updated='2026-01-31' + last_updated="2026-01-31", ), - 'claude-3-sonnet-20240229': ModelPricing( - model='claude-3-sonnet-20240229', - provider='anthropic', + "claude-3-sonnet-20240229": ModelPricing( + model="claude-3-sonnet-20240229", + provider="anthropic", input_cost_per_1k=0.003, output_cost_per_1k=0.015, - last_updated='2026-01-31' + last_updated="2026-01-31", ), - 'claude-3-haiku-20240307': ModelPricing( - model='claude-3-haiku-20240307', - provider='anthropic', + "claude-3-haiku-20240307": ModelPricing( + model="claude-3-haiku-20240307", + provider="anthropic", input_cost_per_1k=0.00025, output_cost_per_1k=0.00125, - last_updated='2026-01-31' + last_updated="2026-01-31", ), - 'claude-2.1': ModelPricing( - model='claude-2.1', - provider='anthropic', + "claude-2.1": ModelPricing( + model="claude-2.1", + provider="anthropic", input_cost_per_1k=0.008, output_cost_per_1k=0.024, - last_updated='2026-01-31' + last_updated="2026-01-31", ), - 'claude-2.0': ModelPricing( - model='claude-2.0', - provider='anthropic', + "claude-2.0": ModelPricing( + model="claude-2.0", + provider="anthropic", input_cost_per_1k=0.008, output_cost_per_1k=0.024, - last_updated='2026-01-31' + last_updated="2026-01-31", ), - 'claude-instant-1.2': ModelPricing( - model='claude-instant-1.2', - provider='anthropic', + "claude-instant-1.2": ModelPricing( + model="claude-instant-1.2", + provider="anthropic", input_cost_per_1k=0.0008, output_cost_per_1k=0.0024, - last_updated='2026-01-31' + last_updated="2026-01-31", ), - # Google PaLM/Gemini Models - 'gemini-pro': ModelPricing( - model='gemini-pro', - provider='google', + "gemini-pro": ModelPricing( + model="gemini-pro", + provider="google", input_cost_per_1k=0.0005, # $0.50 per 1M tokens = $0.0005 per 1K tokens output_cost_per_1k=0.0015, # $1.50 per 1M tokens = $0.0015 per 1K tokens - last_updated='2026-03-06' + last_updated="2026-03-06", ), - 'gemini-pro-vision': ModelPricing( - model='gemini-pro-vision', - provider='google', + "gemini-pro-vision": ModelPricing( + model="gemini-pro-vision", + provider="google", input_cost_per_1k=0.0005, output_cost_per_1k=0.0015, image_cost=0.0025, - last_updated='2026-03-06' + last_updated="2026-03-06", ), - 'gemini-ultra': ModelPricing( - model='gemini-ultra', - provider='google', + "gemini-ultra": ModelPricing( + model="gemini-ultra", + provider="google", input_cost_per_1k=0.007, # $7.00 per 1M tokens = $0.007 per 1K tokens output_cost_per_1k=0.021, # $21.00 per 1M tokens = $0.021 per 1K tokens - last_updated='2026-03-06' + last_updated="2026-03-06", ), - 'gemini-1.5-pro': ModelPricing( - model='gemini-1.5-pro', - provider='google', + "gemini-1.5-pro": ModelPricing( + model="gemini-1.5-pro", + provider="google", input_cost_per_1k=0.0005, output_cost_per_1k=0.0015, - last_updated='2026-03-06' + last_updated="2026-03-06", ), - 'palm-2': ModelPricing( - model='palm-2', - provider='google', + "palm-2": ModelPricing( + model="palm-2", + provider="google", input_cost_per_1k=0.0005, output_cost_per_1k=0.0005, - last_updated='2026-01-31' + last_updated="2026-01-31", ), - # Cohere Models - 'command': ModelPricing( - model='command', - provider='cohere', + "command": ModelPricing( + model="command", + provider="cohere", input_cost_per_1k=0.001, output_cost_per_1k=0.002, - last_updated='2026-01-31' + last_updated="2026-01-31", ), - 'command-light': ModelPricing( - model='command-light', - provider='cohere', + "command-light": ModelPricing( + model="command-light", + provider="cohere", input_cost_per_1k=0.0003, output_cost_per_1k=0.0006, - last_updated='2026-01-31' + last_updated="2026-01-31", ), - 'command-nightly': ModelPricing( - model='command-nightly', - provider='cohere', + "command-nightly": ModelPricing( + model="command-nightly", + provider="cohere", input_cost_per_1k=0.001, output_cost_per_1k=0.002, - last_updated='2026-01-31' + last_updated="2026-01-31", ), - # AWS Bedrock - Anthropic Claude Models - 'anthropic.claude-v2': ModelPricing( - model='anthropic.claude-v2', - provider='bedrock', + "anthropic.claude-v2": ModelPricing( + model="anthropic.claude-v2", + provider="bedrock", input_cost_per_1k=0.008, output_cost_per_1k=0.024, - last_updated='2026-03-06' + last_updated="2026-03-06", ), - 'anthropic.claude-v2:1': ModelPricing( - model='anthropic.claude-v2:1', - provider='bedrock', + "anthropic.claude-v2:1": ModelPricing( + model="anthropic.claude-v2:1", + provider="bedrock", input_cost_per_1k=0.008, output_cost_per_1k=0.024, - last_updated='2026-03-06' + last_updated="2026-03-06", ), - 'anthropic.claude-instant-v1': ModelPricing( - model='anthropic.claude-instant-v1', - provider='bedrock', + "anthropic.claude-instant-v1": ModelPricing( + model="anthropic.claude-instant-v1", + provider="bedrock", input_cost_per_1k=0.0008, output_cost_per_1k=0.0024, - last_updated='2026-03-06' + last_updated="2026-03-06", ), - 'anthropic.claude-3-sonnet-20240229-v1:0': ModelPricing( - model='anthropic.claude-3-sonnet-20240229-v1:0', - provider='bedrock', + "anthropic.claude-3-sonnet-20240229-v1:0": ModelPricing( + model="anthropic.claude-3-sonnet-20240229-v1:0", + provider="bedrock", input_cost_per_1k=0.003, output_cost_per_1k=0.015, - last_updated='2026-03-06' + last_updated="2026-03-06", ), - 'anthropic.claude-3-haiku-20240307-v1:0': ModelPricing( - model='anthropic.claude-3-haiku-20240307-v1:0', - provider='bedrock', + "anthropic.claude-3-haiku-20240307-v1:0": ModelPricing( + model="anthropic.claude-3-haiku-20240307-v1:0", + provider="bedrock", input_cost_per_1k=0.00025, output_cost_per_1k=0.00125, - last_updated='2026-03-06' + last_updated="2026-03-06", ), - # AWS Bedrock - Amazon Titan Models - 'amazon.titan-text-lite-v1': ModelPricing( - model='amazon.titan-text-lite-v1', - provider='bedrock', + "amazon.titan-text-lite-v1": ModelPricing( + model="amazon.titan-text-lite-v1", + provider="bedrock", input_cost_per_1k=0.0003, output_cost_per_1k=0.0004, - last_updated='2026-03-06' + last_updated="2026-03-06", ), - 'amazon.titan-text-express-v1': ModelPricing( - model='amazon.titan-text-express-v1', - provider='bedrock', + "amazon.titan-text-express-v1": ModelPricing( + model="amazon.titan-text-express-v1", + provider="bedrock", input_cost_per_1k=0.0008, output_cost_per_1k=0.0016, - last_updated='2026-03-06' + last_updated="2026-03-06", ), - 'amazon.titan-text-premier-v1:0': ModelPricing( - model='amazon.titan-text-premier-v1:0', - provider='bedrock', + "amazon.titan-text-premier-v1:0": ModelPricing( + model="amazon.titan-text-premier-v1:0", + provider="bedrock", input_cost_per_1k=0.0005, output_cost_per_1k=0.0015, - last_updated='2026-03-06' + last_updated="2026-03-06", ), - # AWS Bedrock - AI21 Jurassic Models - 'ai21.j2-ultra-v1': ModelPricing( - model='ai21.j2-ultra-v1', - provider='bedrock', + "ai21.j2-ultra-v1": ModelPricing( + model="ai21.j2-ultra-v1", + provider="bedrock", input_cost_per_1k=0.0188, output_cost_per_1k=0.0188, - last_updated='2026-03-06' + last_updated="2026-03-06", ), - 'ai21.j2-mid-v1': ModelPricing( - model='ai21.j2-mid-v1', - provider='bedrock', + "ai21.j2-mid-v1": ModelPricing( + model="ai21.j2-mid-v1", + provider="bedrock", input_cost_per_1k=0.0125, output_cost_per_1k=0.0125, - last_updated='2026-03-06' + last_updated="2026-03-06", ), - # AWS Bedrock - Cohere Command Models - 'cohere.command-text-v14': ModelPricing( - model='cohere.command-text-v14', - provider='bedrock', + "cohere.command-text-v14": ModelPricing( + model="cohere.command-text-v14", + provider="bedrock", input_cost_per_1k=0.0015, output_cost_per_1k=0.002, - last_updated='2026-03-06' + last_updated="2026-03-06", ), - 'cohere.command-light-text-v14': ModelPricing( - model='cohere.command-light-text-v14', - provider='bedrock', + "cohere.command-light-text-v14": ModelPricing( + model="cohere.command-light-text-v14", + provider="bedrock", input_cost_per_1k=0.0003, output_cost_per_1k=0.0006, - last_updated='2026-03-06' + last_updated="2026-03-06", ), - # AWS Bedrock - Meta Llama Models - 'meta.llama2-13b-chat-v1': ModelPricing( - model='meta.llama2-13b-chat-v1', - provider='bedrock', + "meta.llama2-13b-chat-v1": ModelPricing( + model="meta.llama2-13b-chat-v1", + provider="bedrock", input_cost_per_1k=0.00075, output_cost_per_1k=0.001, - last_updated='2026-03-06' + last_updated="2026-03-06", ), - 'meta.llama2-70b-chat-v1': ModelPricing( - model='meta.llama2-70b-chat-v1', - provider='bedrock', + "meta.llama2-70b-chat-v1": ModelPricing( + model="meta.llama2-70b-chat-v1", + provider="bedrock", input_cost_per_1k=0.00195, output_cost_per_1k=0.00256, - last_updated='2026-03-06' + last_updated="2026-03-06", ), - # Mistral AI Models - 'mistral-small': ModelPricing( - model='mistral-small', - provider='mistral', + "mistral-small": ModelPricing( + model="mistral-small", + provider="mistral", input_cost_per_1k=0.001, output_cost_per_1k=0.003, - last_updated='2026-03-06' + last_updated="2026-03-06", ), - 'mistral-small-latest': ModelPricing( - model='mistral-small-latest', - provider='mistral', - input_cost_per_1k=0.001, - output_cost_per_1k=0.003, - last_updated='2026-03-06' + "mistral-small-latest": ModelPricing( + model="mistral-small-latest", + provider="mistral", + input_cost_per_1k=0.00015, + output_cost_per_1k=0.0006, + last_updated="2026-08-03", ), - 'mistral-medium': ModelPricing( - model='mistral-medium', - provider='mistral', + "mistral-medium": ModelPricing( + model="mistral-medium", + provider="mistral", input_cost_per_1k=0.0027, output_cost_per_1k=0.0081, - last_updated='2026-03-06' + last_updated="2026-03-06", ), - 'mistral-medium-latest': ModelPricing( - model='mistral-medium-latest', - provider='mistral', - input_cost_per_1k=0.0027, - output_cost_per_1k=0.0081, - last_updated='2026-03-06' + "mistral-medium-latest": ModelPricing( + model="mistral-medium-latest", + provider="mistral", + input_cost_per_1k=0.0015, + output_cost_per_1k=0.0075, + last_updated="2026-08-03", ), - 'mistral-large': ModelPricing( - model='mistral-large', - provider='mistral', + "mistral-large": ModelPricing( + model="mistral-large", + provider="mistral", input_cost_per_1k=0.004, output_cost_per_1k=0.012, - last_updated='2026-03-06' + last_updated="2026-03-06", ), - 'mistral-large-latest': ModelPricing( - model='mistral-large-latest', - provider='mistral', - input_cost_per_1k=0.004, - output_cost_per_1k=0.012, - last_updated='2026-03-06' + "mistral-large-latest": ModelPricing( + model="mistral-large-latest", + provider="mistral", + input_cost_per_1k=0.0005, + output_cost_per_1k=0.0015, + last_updated="2026-08-03", ), - 'mixtral-8x7b': ModelPricing( - model='mixtral-8x7b', - provider='mistral', + # addding mistral medium latest + "mixtral-8x7b": ModelPricing( + model="mixtral-8x7b", + provider="mistral", input_cost_per_1k=0.0007, output_cost_per_1k=0.0007, - last_updated='2026-03-06' + last_updated="2026-03-06", ), - 'mixtral-8x22b': ModelPricing( - model='mixtral-8x22b', - provider='mistral', + "mixtral-8x22b": ModelPricing( + model="mixtral-8x22b", + provider="mistral", input_cost_per_1k=0.002, output_cost_per_1k=0.006, - last_updated='2026-03-06' + last_updated="2026-03-06", ), } -def get_model_pricing(model: str, provider: Optional[ModelProvider] = None) -> Optional[ModelPricing]: +def get_model_pricing( + model: str, provider: Optional[ModelProvider] = None +) -> Optional[ModelPricing]: """ Get pricing for a specific model with fuzzy matching. - + Args: model: Model identifier provider: Optional provider override - + Returns: Model pricing or None if not found """ # Handle empty or whitespace-only strings if not model or not model.strip(): return None - + # Try exact match first if model in MODEL_PRICING: return MODEL_PRICING[model] - + # Try case-insensitive match normalized = model.lower().strip() for key, pricing in MODEL_PRICING.items(): if key.lower() == normalized: return pricing - + # Try partial match for versioned models (only if normalized is not empty) if normalized: for key, pricing in MODEL_PRICING.items(): if normalized.startswith(key.lower()) or key.lower().startswith(normalized): if not provider or pricing.provider == provider: return pricing - + return None def get_provider_models(provider: ModelProvider) -> List[ModelPricing]: """ Get all models for a specific provider. - + Args: provider: Provider name - + Returns: List of model pricing """ @@ -389,10 +381,10 @@ def get_provider_models(provider: ModelProvider) -> List[ModelPricing]: def is_model_supported(model: str) -> bool: """ Check if a model is supported. - + Args: model: Model identifier - + Returns: True if model pricing is available """ @@ -402,7 +394,7 @@ def is_model_supported(model: str) -> bool: def get_supported_models() -> List[str]: """ Get list of all supported models. - + Returns: List of model identifiers """ @@ -412,9 +404,10 @@ def get_supported_models() -> List[str]: def get_supported_providers() -> List[ModelProvider]: """ Get list of all supported providers. - + Returns: List of provider names """ - providers = set(p.provider for p in MODEL_PRICING.values()) + # providers = set(p.provider for p in MODEL_PRICING.values()) + providers = {p.provider for p in MODEL_PRICING.values()} return list(providers) diff --git a/tests/cost/test_pricing.py b/tests/cost/test_pricing.py new file mode 100644 index 0000000..5747d0f --- /dev/null +++ b/tests/cost/test_pricing.py @@ -0,0 +1,42 @@ +"""This file is for testing the pricing module, by verifying the pricing data is correct and complete.""" + +import pytest + +from tealtiger.cost import CostTracker +from tealtiger.cost.pricing import get_model_pricing +from tealtiger.cost.types import TokenUsage + +# Keep this table in sync with pricing.py +MISTRAL_PRICING = [ + ("mistral-small", 0.001, 0.003), + ("mistral-small-latest", 0.00015, 0.0006), + ("mistral-medium", 0.0027, 0.0081), + ("mistral-medium-latest", 0.0015, 0.0075), + ("mistral-large", 0.004, 0.012), + ("mistral-large-latest", 0.0005, 0.0015), + ("mixtral-8x7b", 0.0007, 0.0007), + ("mixtral-8x22b", 0.002, 0.006), + # add when present in pricing.py: + # ("open-mistral-nemo", 0.00015, 0.00015), + # ("codestral-latest", 0.0003, 0.0009), +] + + +@pytest.mark.parametrize("model,input_rate,output_rate", MISTRAL_PRICING) +def test_mistral_pricing_lookup(model, input_rate, output_rate): + pricing = get_model_pricing(model) + assert pricing is not None + assert pricing.provider == "mistral" + assert pricing.input_cost_per_1k == input_rate + assert pricing.output_cost_per_1k == output_rate + + +# this test is for verifying the cost calculation for mistral-large-latest +def test_mistral_large_latest_cost_calculation(): + """Cost calculation for mistral-large-latest.""" + tracker = CostTracker() + tokens = TokenUsage(input_tokens=1000, output_tokens=1000, total_tokens=2000) + estimate = tracker.estimate_cost("mistral-large-latest", tokens) + assert estimate.breakdown.input_cost == pytest.approx(0.0005) + assert estimate.breakdown.output_cost == pytest.approx(0.0015) + assert estimate.estimated_cost == pytest.approx(0.002) From 9f916d7e4e8f40c4fc8ee3f241dd13a667ad54bf Mon Sep 17 00:00:00 2001 From: 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 Date: Mon, 3 Aug 2026 15:24:54 +0300 Subject: [PATCH 2/3] feat: update Mistral pricing and add cost calculation tests --- src/tealtiger/cost/pricing.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/tealtiger/cost/pricing.py b/src/tealtiger/cost/pricing.py index 1858c03..40a86c2 100644 --- a/src/tealtiger/cost/pricing.py +++ b/src/tealtiger/cost/pricing.py @@ -325,6 +325,22 @@ output_cost_per_1k=0.006, last_updated="2026-03-06", ), + # Adding Codestral latest + "codestral-latest": ModelPricing( + model="codestral-latest", + provider="mistral", + input_cost_per_1k=0.0003, + output_cost_per_1k=0.0009, + last_updated="2026-08-03", + ), + # Adding Open Mistral Nemo + "open-mistral-nemo": ModelPricing( + model="open-mistral-nemo", + provider="mistral", + input_cost_per_1k=0.00015, + output_cost_per_1k=0.00015, + last_updated="2026-08-03", + ), } @@ -408,6 +424,5 @@ def get_supported_providers() -> List[ModelProvider]: Returns: List of provider names """ - # providers = set(p.provider for p in MODEL_PRICING.values()) - providers = {p.provider for p in MODEL_PRICING.values()} + providers = set(p.provider for p in MODEL_PRICING.values()) return list(providers) From 629b4bded2686d5c683b5ae83df85161aa9c3c9a Mon Sep 17 00:00:00 2001 From: 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 Date: Wed, 5 Aug 2026 09:07:44 +0300 Subject: [PATCH 3/3] Added the mistral pricing link --- src/tealtiger/cost/pricing.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tealtiger/cost/pricing.py b/src/tealtiger/cost/pricing.py index 40a86c2..dce4342 100644 --- a/src/tealtiger/cost/pricing.py +++ b/src/tealtiger/cost/pricing.py @@ -1,6 +1,7 @@ """ Model Pricing Data: Up-to-date pricing information for major AI model providers Last updated: August 2026 +URL for Mistral pricing: https://mistral.ai/pricing/api/ """ from typing import Dict, List, Optional