credits adn tokens final correct setup

This commit is contained in:
IGNY8 VPS (Salman)
2025-12-20 00:36:23 +00:00
parent e041cb8e65
commit c17b22e927
13 changed files with 1170 additions and 233 deletions

View File

@@ -0,0 +1,99 @@
# Generated by Django 5.2.9 on 2025-12-19 18:20
import django.core.validators
import django.db.models.deletion
from decimal import Decimal
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('billing', '0017_add_history_tracking'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.RemoveField(
model_name='creditcostconfig',
name='credits_cost',
),
migrations.RemoveField(
model_name='creditcostconfig',
name='previous_cost',
),
migrations.RemoveField(
model_name='creditcostconfig',
name='unit',
),
migrations.RemoveField(
model_name='historicalcreditcostconfig',
name='credits_cost',
),
migrations.RemoveField(
model_name='historicalcreditcostconfig',
name='previous_cost',
),
migrations.RemoveField(
model_name='historicalcreditcostconfig',
name='unit',
),
migrations.AddField(
model_name='creditcostconfig',
name='min_credits',
field=models.IntegerField(default=1, help_text='Minimum credits to charge regardless of token usage', validators=[django.core.validators.MinValueValidator(0)]),
),
migrations.AddField(
model_name='creditcostconfig',
name='previous_tokens_per_credit',
field=models.IntegerField(blank=True, help_text='Tokens per credit before last update (for audit trail)', null=True),
),
migrations.AddField(
model_name='creditcostconfig',
name='price_per_credit_usd',
field=models.DecimalField(decimal_places=4, default=Decimal('0.01'), help_text='USD price per credit (for revenue reporting)', max_digits=10, validators=[django.core.validators.MinValueValidator(Decimal('0.0001'))]),
),
migrations.AddField(
model_name='creditcostconfig',
name='tokens_per_credit',
field=models.IntegerField(default=100, help_text='Number of tokens that equal 1 credit (e.g., 100 tokens = 1 credit)', validators=[django.core.validators.MinValueValidator(1)]),
),
migrations.AddField(
model_name='historicalcreditcostconfig',
name='min_credits',
field=models.IntegerField(default=1, help_text='Minimum credits to charge regardless of token usage', validators=[django.core.validators.MinValueValidator(0)]),
),
migrations.AddField(
model_name='historicalcreditcostconfig',
name='previous_tokens_per_credit',
field=models.IntegerField(blank=True, help_text='Tokens per credit before last update (for audit trail)', null=True),
),
migrations.AddField(
model_name='historicalcreditcostconfig',
name='price_per_credit_usd',
field=models.DecimalField(decimal_places=4, default=Decimal('0.01'), help_text='USD price per credit (for revenue reporting)', max_digits=10, validators=[django.core.validators.MinValueValidator(Decimal('0.0001'))]),
),
migrations.AddField(
model_name='historicalcreditcostconfig',
name='tokens_per_credit',
field=models.IntegerField(default=100, help_text='Number of tokens that equal 1 credit (e.g., 100 tokens = 1 credit)', validators=[django.core.validators.MinValueValidator(1)]),
),
migrations.CreateModel(
name='BillingConfiguration',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('default_tokens_per_credit', models.IntegerField(default=100, help_text='Default: How many tokens equal 1 credit (e.g., 100)', validators=[django.core.validators.MinValueValidator(1)])),
('default_credit_price_usd', models.DecimalField(decimal_places=4, default=Decimal('0.01'), help_text='Default price per credit in USD', max_digits=10, validators=[django.core.validators.MinValueValidator(Decimal('0.0001'))])),
('enable_token_based_reporting', models.BooleanField(default=True, help_text='Show token metrics in all reports')),
('credit_rounding_mode', models.CharField(choices=[('up', 'Round Up'), ('down', 'Round Down'), ('nearest', 'Round to Nearest')], default='up', help_text='How to round fractional credits', max_length=10)),
('updated_at', models.DateTimeField(auto_now=True)),
('updated_by', models.ForeignKey(blank=True, help_text='Admin who last updated', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'Billing Configuration',
'verbose_name_plural': 'Billing Configuration',
'db_table': 'igny8_billing_configuration',
},
),
]

View File

@@ -0,0 +1,111 @@
# Generated by Django 5.2.9 on 2025-12-19 18:28
from django.db import migrations
from decimal import Decimal
def populate_token_config(apps, schema_editor):
"""
Populate BillingConfiguration singleton and update CreditCostConfig records.
Token-based pricing ratios (tokens per 1 credit):
- Default: 100 tokens = 1 credit
- AI operations vary by complexity
"""
BillingConfiguration = apps.get_model('billing', 'BillingConfiguration')
CreditCostConfig = apps.get_model('billing', 'CreditCostConfig')
# Create BillingConfiguration singleton (if not exists)
if not BillingConfiguration.objects.exists():
BillingConfiguration.objects.create(
default_tokens_per_credit=100,
default_credit_price_usd=Decimal('0.01'),
enable_token_based_reporting=True,
credit_rounding_mode='up'
)
# Token-to-credit ratios for each operation type
# Lower number = more expensive (fewer tokens per credit)
# Higher number = cheaper (more tokens per credit)
operation_configs = {
'clustering': {
'tokens_per_credit': 150, # Clustering is fairly complex
'min_credits': 2,
'price_per_credit_usd': Decimal('0.01'),
'display_name': 'Content Clustering',
'description': 'AI-powered keyword clustering'
},
'idea_generation': {
'tokens_per_credit': 200, # Idea generation is mid-complexity
'min_credits': 1,
'price_per_credit_usd': Decimal('0.01'),
'display_name': 'Idea Generation',
'description': 'AI content idea generation'
},
'content_generation': {
'tokens_per_credit': 100, # Content generation is expensive (outputs many tokens)
'min_credits': 3,
'price_per_credit_usd': Decimal('0.01'),
'display_name': 'Content Generation',
'description': 'AI content writing'
},
'image_generation': {
'tokens_per_credit': 50, # Image generation is most expensive
'min_credits': 5,
'price_per_credit_usd': Decimal('0.02'),
'display_name': 'Image Generation',
'description': 'AI image generation'
},
'optimization': {
'tokens_per_credit': 200, # Optimization is algorithm-based with minimal AI
'min_credits': 1,
'price_per_credit_usd': Decimal('0.005'),
'display_name': 'Content Optimization',
'description': 'SEO and content optimization'
},
'linking': {
'tokens_per_credit': 300, # Linking is mostly algorithmic
'min_credits': 1,
'price_per_credit_usd': Decimal('0.005'),
'display_name': 'Internal Linking',
'description': 'Automatic internal link injection'
},
'reparse': {
'tokens_per_credit': 150, # Reparse is mid-complexity
'min_credits': 1,
'price_per_credit_usd': Decimal('0.01'),
'display_name': 'Content Reparse',
'description': 'Content reparsing and analysis'
},
}
# Update or create CreditCostConfig records
for operation_type, config in operation_configs.items():
CreditCostConfig.objects.update_or_create(
operation_type=operation_type,
defaults={
'tokens_per_credit': config['tokens_per_credit'],
'min_credits': config['min_credits'],
'price_per_credit_usd': config['price_per_credit_usd'],
'display_name': config['display_name'],
'description': config['description'],
'is_active': True
}
)
def reverse_token_config(apps, schema_editor):
"""Reverse migration - clean up configuration"""
BillingConfiguration = apps.get_model('billing', 'BillingConfiguration')
BillingConfiguration.objects.all().delete()
# Note: We don't delete CreditCostConfig records as they may have historical data
class Migration(migrations.Migration):
dependencies = [
('billing', '0018_remove_creditcostconfig_credits_cost_and_more'),
]
operations = [
migrations.RunPython(populate_token_config, reverse_token_config),
]