New Model & tokens/credits updates
This commit is contained in:
@@ -19,6 +19,102 @@ PAYMENT_METHOD_CHOICES = [
|
||||
]
|
||||
|
||||
|
||||
class AIModelConfig(models.Model):
|
||||
"""
|
||||
AI Model Configuration - Centralized pricing and token ratios
|
||||
Single source of truth for all AI model costs
|
||||
"""
|
||||
# Model identification
|
||||
model_name = models.CharField(
|
||||
max_length=100,
|
||||
unique=True,
|
||||
help_text="Technical model name (e.g., gpt-4-turbo, gpt-3.5-turbo)"
|
||||
)
|
||||
provider = models.CharField(
|
||||
max_length=50,
|
||||
choices=[
|
||||
('openai', 'OpenAI'),
|
||||
('anthropic', 'Anthropic'),
|
||||
('runware', 'Runware'),
|
||||
('other', 'Other'),
|
||||
],
|
||||
help_text="AI provider"
|
||||
)
|
||||
model_type = models.CharField(
|
||||
max_length=20,
|
||||
choices=[
|
||||
('text', 'Text Generation'),
|
||||
('image', 'Image Generation'),
|
||||
('embedding', 'Embeddings'),
|
||||
],
|
||||
default='text',
|
||||
help_text="Type of AI model"
|
||||
)
|
||||
|
||||
# Pricing (per 1K tokens for text models)
|
||||
cost_per_1k_input_tokens = models.DecimalField(
|
||||
max_digits=10,
|
||||
decimal_places=6,
|
||||
default=Decimal('0.001'),
|
||||
validators=[MinValueValidator(Decimal('0'))],
|
||||
help_text="Cost in USD per 1,000 input tokens"
|
||||
)
|
||||
cost_per_1k_output_tokens = models.DecimalField(
|
||||
max_digits=10,
|
||||
decimal_places=6,
|
||||
default=Decimal('0.002'),
|
||||
validators=[MinValueValidator(Decimal('0'))],
|
||||
help_text="Cost in USD per 1,000 output tokens"
|
||||
)
|
||||
|
||||
# Token-to-credit ratio
|
||||
tokens_per_credit = models.IntegerField(
|
||||
default=100,
|
||||
validators=[MinValueValidator(1)],
|
||||
help_text="How many tokens equal 1 credit (e.g., 100 tokens = 1 credit)"
|
||||
)
|
||||
|
||||
# Display
|
||||
display_name = models.CharField(
|
||||
max_length=150,
|
||||
help_text="Human-readable name (e.g., 'GPT-4 Turbo (Premium)')"
|
||||
)
|
||||
description = models.TextField(
|
||||
blank=True,
|
||||
help_text="Model description and use cases"
|
||||
)
|
||||
|
||||
# Status
|
||||
is_active = models.BooleanField(
|
||||
default=True,
|
||||
help_text="Enable/disable this model"
|
||||
)
|
||||
is_default = models.BooleanField(
|
||||
default=False,
|
||||
help_text="Use as system-wide default model"
|
||||
)
|
||||
|
||||
# Metadata
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
app_label = 'billing'
|
||||
db_table = 'igny8_ai_model_config'
|
||||
verbose_name = 'AI Model Configuration'
|
||||
verbose_name_plural = 'AI Model Configurations'
|
||||
ordering = ['provider', 'model_name']
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.display_name} ({self.model_name})"
|
||||
|
||||
def calculate_cost(self, tokens_input, tokens_output):
|
||||
"""Calculate USD cost for given token usage"""
|
||||
cost_input = (tokens_input / 1000) * self.cost_per_1k_input_tokens
|
||||
cost_output = (tokens_output / 1000) * self.cost_per_1k_output_tokens
|
||||
return float(cost_input + cost_output)
|
||||
|
||||
|
||||
class CreditTransaction(AccountBaseModel):
|
||||
"""Track all credit transactions (additions, deductions)"""
|
||||
TRANSACTION_TYPE_CHOICES = [
|
||||
@@ -89,10 +185,59 @@ class CreditUsageLog(AccountBaseModel):
|
||||
|
||||
operation_type = models.CharField(max_length=50, choices=OPERATION_TYPE_CHOICES, db_index=True)
|
||||
credits_used = models.IntegerField(validators=[MinValueValidator(0)])
|
||||
cost_usd = models.DecimalField(max_digits=10, decimal_places=4, null=True, blank=True)
|
||||
model_used = models.CharField(max_length=100, blank=True)
|
||||
|
||||
# Model tracking
|
||||
model_config = models.ForeignKey(
|
||||
'billing.AIModelConfig',
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name='usage_logs',
|
||||
help_text="AI model configuration used",
|
||||
db_column='model_config_id'
|
||||
)
|
||||
model_name = models.CharField(
|
||||
max_length=100,
|
||||
blank=True,
|
||||
help_text="Model name (deprecated, use model_config FK)"
|
||||
)
|
||||
|
||||
# Token tracking
|
||||
tokens_input = models.IntegerField(null=True, blank=True, validators=[MinValueValidator(0)])
|
||||
tokens_output = models.IntegerField(null=True, blank=True, validators=[MinValueValidator(0)])
|
||||
|
||||
# Cost tracking (USD)
|
||||
cost_usd_input = models.DecimalField(
|
||||
max_digits=10,
|
||||
decimal_places=6,
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="USD cost for input tokens"
|
||||
)
|
||||
cost_usd_output = models.DecimalField(
|
||||
max_digits=10,
|
||||
decimal_places=6,
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="USD cost for output tokens"
|
||||
)
|
||||
cost_usd_total = models.DecimalField(
|
||||
max_digits=10,
|
||||
decimal_places=6,
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Total USD cost (input + output)"
|
||||
)
|
||||
|
||||
# Legacy cost field (deprecated)
|
||||
cost_usd = models.DecimalField(
|
||||
max_digits=10,
|
||||
decimal_places=4,
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Deprecated, use cost_usd_total"
|
||||
)
|
||||
|
||||
related_object_type = models.CharField(max_length=50, blank=True) # 'keyword', 'cluster', 'task'
|
||||
related_object_id = models.IntegerField(null=True, blank=True)
|
||||
metadata = models.JSONField(default=dict)
|
||||
@@ -154,6 +299,8 @@ class CreditCostConfig(models.Model):
|
||||
('per_200_words', 'Per 200 Words'),
|
||||
('per_item', 'Per Item'),
|
||||
('per_image', 'Per Image'),
|
||||
('per_100_tokens', 'Per 100 Tokens'), # NEW: Token-based
|
||||
('per_1000_tokens', 'Per 1000 Tokens'), # NEW: Token-based
|
||||
]
|
||||
|
||||
unit = models.CharField(
|
||||
@@ -163,6 +310,16 @@ class CreditCostConfig(models.Model):
|
||||
help_text="What the cost applies to"
|
||||
)
|
||||
|
||||
# Model configuration
|
||||
default_model = models.ForeignKey(
|
||||
'billing.AIModelConfig',
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name='operation_configs',
|
||||
help_text="Default AI model for this operation (optional)"
|
||||
)
|
||||
|
||||
# Metadata
|
||||
display_name = models.CharField(max_length=100, help_text="Human-readable name")
|
||||
description = models.TextField(blank=True, help_text="What this operation does")
|
||||
|
||||
Reference in New Issue
Block a user