New Model & tokens/credits updates

This commit is contained in:
IGNY8 VPS (Salman)
2025-12-23 06:26:15 +00:00
parent 1d4825ad77
commit d768ed71d4
9 changed files with 945 additions and 35 deletions

View File

@@ -8,6 +8,7 @@ from unfold.admin import ModelAdmin
from simple_history.admin import SimpleHistoryAdmin
from igny8_core.admin.base import AccountAdminMixin, Igny8ModelAdmin
from igny8_core.business.billing.models import (
AIModelConfig,
CreditCostConfig,
Invoice,
Payment,
@@ -49,11 +50,43 @@ class CreditTransactionAdmin(ExportMixin, AccountAdminMixin, Igny8ModelAdmin):
get_account_display.short_description = 'Account'
@admin.register(AIModelConfig)
class AIModelConfigAdmin(Igny8ModelAdmin):
list_display = ['display_name', 'model_name', 'provider', 'model_type', 'tokens_per_credit', 'cost_per_1k_input_tokens', 'cost_per_1k_output_tokens', 'is_active', 'is_default']
list_filter = ['provider', 'model_type', 'is_active', 'is_default']
search_fields = ['model_name', 'display_name', 'description']
readonly_fields = ['created_at', 'updated_at']
fieldsets = (
('Model Information', {
'fields': ('model_name', 'display_name', 'description', 'provider', 'model_type')
}),
('Pricing', {
'fields': ('cost_per_1k_input_tokens', 'cost_per_1k_output_tokens', 'tokens_per_credit')
}),
('Status', {
'fields': ('is_active', 'is_default')
}),
('Timestamps', {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',)
}),
)
def save_model(self, request, obj, form, change):
# If setting as default, unset other defaults of same type
if obj.is_default:
AIModelConfig.objects.filter(
model_type=obj.model_type,
is_default=True
).exclude(pk=obj.pk).update(is_default=False)
super().save_model(request, obj, form, change)
@admin.register(CreditUsageLog)
class CreditUsageLogAdmin(AccountAdminMixin, Igny8ModelAdmin):
list_display = ['id', 'account', 'operation_type', 'credits_used', 'cost_usd', 'model_used', 'created_at']
list_filter = ['operation_type', 'created_at', 'account', 'model_used']
search_fields = ['account__name', 'model_used']
list_display = ['id', 'account', 'operation_type', 'credits_used', 'cost_usd', 'model_config', 'created_at']
list_filter = ['operation_type', 'created_at', 'account', 'model_config']
search_fields = ['account__name', 'model_name']
readonly_fields = ['created_at']
date_hierarchy = 'created_at'

View File

@@ -0,0 +1,90 @@
# Generated by Django 5.2.9 on 2025-12-23 05:31
import django.core.validators
import django.db.models.deletion
from decimal import Decimal
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('billing', '0018_update_operation_choices'),
]
operations = [
migrations.CreateModel(
name='AIModelConfig',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('model_name', models.CharField(help_text='Technical model name (e.g., gpt-4-turbo, gpt-3.5-turbo)', max_length=100, unique=True)),
('provider', models.CharField(choices=[('openai', 'OpenAI'), ('anthropic', 'Anthropic'), ('runware', 'Runware'), ('other', 'Other')], help_text='AI provider', max_length=50)),
('model_type', models.CharField(choices=[('text', 'Text Generation'), ('image', 'Image Generation'), ('embedding', 'Embeddings')], default='text', help_text='Type of AI model', max_length=20)),
('cost_per_1k_input_tokens', models.DecimalField(decimal_places=6, default=Decimal('0.001'), help_text='Cost in USD per 1,000 input tokens', max_digits=10, validators=[django.core.validators.MinValueValidator(Decimal('0'))])),
('cost_per_1k_output_tokens', models.DecimalField(decimal_places=6, default=Decimal('0.002'), help_text='Cost in USD per 1,000 output tokens', max_digits=10, validators=[django.core.validators.MinValueValidator(Decimal('0'))])),
('tokens_per_credit', models.IntegerField(default=100, help_text='How many tokens equal 1 credit (e.g., 100 tokens = 1 credit)', validators=[django.core.validators.MinValueValidator(1)])),
('display_name', models.CharField(help_text="Human-readable name (e.g., 'GPT-4 Turbo (Premium)')", max_length=150)),
('description', models.TextField(blank=True, help_text='Model description and use cases')),
('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')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
options={
'verbose_name': 'AI Model Configuration',
'verbose_name_plural': 'AI Model Configurations',
'db_table': 'igny8_ai_model_config',
'ordering': ['provider', 'model_name'],
},
),
migrations.AddField(
model_name='creditusagelog',
name='cost_usd_input',
field=models.DecimalField(blank=True, decimal_places=6, help_text='USD cost for input tokens', max_digits=10, null=True),
),
migrations.AddField(
model_name='creditusagelog',
name='cost_usd_output',
field=models.DecimalField(blank=True, decimal_places=6, help_text='USD cost for output tokens', max_digits=10, null=True),
),
migrations.AddField(
model_name='creditusagelog',
name='cost_usd_total',
field=models.DecimalField(blank=True, decimal_places=6, help_text='Total USD cost (input + output)', max_digits=10, null=True),
),
migrations.AddField(
model_name='creditusagelog',
name='model_name',
field=models.CharField(blank=True, help_text='Model name (deprecated, use model_used FK)', max_length=100),
),
migrations.AlterField(
model_name='creditcostconfig',
name='unit',
field=models.CharField(choices=[('per_request', 'Per Request'), ('per_100_words', 'Per 100 Words'), ('per_200_words', 'Per 200 Words'), ('per_item', 'Per Item'), ('per_image', 'Per Image'), ('per_100_tokens', 'Per 100 Tokens'), ('per_1000_tokens', 'Per 1000 Tokens')], default='per_request', help_text='What the cost applies to', max_length=50),
),
migrations.AlterField(
model_name='creditusagelog',
name='cost_usd',
field=models.DecimalField(blank=True, decimal_places=4, help_text='Deprecated, use cost_usd_total', max_digits=10, null=True),
),
migrations.AlterField(
model_name='historicalcreditcostconfig',
name='unit',
field=models.CharField(choices=[('per_request', 'Per Request'), ('per_100_words', 'Per 100 Words'), ('per_200_words', 'Per 200 Words'), ('per_item', 'Per Item'), ('per_image', 'Per Image'), ('per_100_tokens', 'Per 100 Tokens'), ('per_1000_tokens', 'Per 1000 Tokens')], default='per_request', help_text='What the cost applies to', max_length=50),
),
migrations.AddField(
model_name='creditcostconfig',
name='default_model',
field=models.ForeignKey(blank=True, help_text='Default AI model for this operation (optional)', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='operation_configs', to='billing.aimodelconfig'),
),
migrations.AddField(
model_name='historicalcreditcostconfig',
name='default_model',
field=models.ForeignKey(blank=True, db_constraint=False, help_text='Default AI model for this operation (optional)', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='billing.aimodelconfig'),
),
migrations.AlterField(
model_name='creditusagelog',
name='model_used',
field=models.ForeignKey(blank=True, help_text='AI model used for this operation', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='usage_logs', to='billing.aimodelconfig'),
),
]

View File

@@ -0,0 +1,25 @@
# Generated by Django 5.2.9 on 2025-12-23 05:32
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('billing', '0019_add_ai_model_config'),
('system', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='integrationsettings',
name='default_image_model',
field=models.ForeignKey(blank=True, help_text='Default AI model for image generation operations', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='image_integrations', to='billing.aimodelconfig'),
),
migrations.AddField(
model_name='integrationsettings',
name='default_text_model',
field=models.ForeignKey(blank=True, help_text='Default AI model for text generation operations', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='text_integrations', to='billing.aimodelconfig'),
),
]

View File

@@ -60,6 +60,25 @@ class IntegrationSettings(AccountBaseModel):
integration_type = models.CharField(max_length=50, choices=INTEGRATION_TYPE_CHOICES, db_index=True)
config = models.JSONField(default=dict, help_text="Integration configuration (API keys, settings, etc.)")
is_active = models.BooleanField(default=True)
# AI Model Selection (NEW)
default_text_model = models.ForeignKey(
'billing.AIModelConfig',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='text_integrations',
help_text="Default AI model for text generation operations"
)
default_image_model = models.ForeignKey(
'billing.AIModelConfig',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='image_integrations',
help_text="Default AI model for image generation operations"
)
updated_at = models.DateTimeField(auto_now=True)
created_at = models.DateTimeField(auto_now_add=True)