from django.db import models from apps.common.models import TeamOwnedModel, TimeStampedModel class ModelProvider(TimeStampedModel): class Status(models.TextChoices): ACTIVE = "active", "Active" DISABLED = "disabled", "Disabled" name = models.CharField(max_length=64, unique=True) display_name = models.CharField(max_length=128) status = models.CharField(max_length=24, choices=Status.choices, default=Status.ACTIVE) base_url = models.URLField(blank=True) metadata = models.JSONField(default=dict, blank=True) def __str__(self) -> str: return self.display_name class ModelConfig(TimeStampedModel): class Capability(models.TextChoices): TEXT = "text", "Text" IMAGE = "image", "Image" VIDEO = "video", "Video" VISION = "vision", "Vision" EXPORT = "export", "Export" class Status(models.TextChoices): ACTIVE = "active", "Active" DISABLED = "disabled", "Disabled" provider = models.ForeignKey(ModelProvider, on_delete=models.CASCADE, related_name="models") name = models.CharField(max_length=128) display_name = models.CharField(max_length=128) capability = models.CharField(max_length=32, choices=Capability.choices) endpoint = models.CharField(max_length=255, blank=True) unit_price = models.DecimalField(max_digits=12, decimal_places=4, default=0) status = models.CharField(max_length=24, choices=Status.choices, default=Status.ACTIVE) rate_limit_per_minute = models.PositiveIntegerField(default=60) metadata = models.JSONField(default=dict, blank=True) class Meta: unique_together = [("provider", "name", "capability")] def __str__(self) -> str: return f"{self.provider.name}:{self.name}:{self.capability}" class AITask(TeamOwnedModel): class Type(models.TextChoices): SCRIPT_GENERATION = "script_generation", "Script Generation" SCRIPT_OPTIMIZATION = "script_optimization", "Script Optimization" PRODUCT_IMAGE = "product_image", "Product Image" PERSON_IMAGE = "person_image", "Person Image" SCENE_IMAGE = "scene_image", "Scene Image" STORYBOARD = "storyboard", "Storyboard" VIDEO_SEGMENT = "video_segment", "Video Segment" EXPORT = "export", "Export" class Status(models.TextChoices): CREATED = "created", "Created" RESERVED = "reserved", "Reserved" SUBMITTED = "submitted", "Submitted" POLLING = "polling", "Polling" POSTPROCESSING = "postprocessing", "Postprocessing" SUCCEEDED = "succeeded", "Succeeded" FAILED = "failed", "Failed" COMPENSATING = "compensating", "Compensating" CANCELLED = "cancelled", "Cancelled" project = models.ForeignKey( "projects.Project", on_delete=models.CASCADE, null=True, blank=True, related_name="ai_tasks", ) task_type = models.CharField(max_length=48, choices=Type.choices) status = models.CharField(max_length=32, choices=Status.choices, default=Status.CREATED) model_config = models.ForeignKey(ModelConfig, on_delete=models.PROTECT, related_name="tasks") idempotency_key = models.CharField(max_length=128, unique=True) provider_task_id = models.CharField(max_length=255, blank=True) request_payload = models.JSONField(default=dict, blank=True) response_payload = models.JSONField(default=dict, blank=True) estimated_cost = models.DecimalField(max_digits=12, decimal_places=4, default=0) actual_cost = models.DecimalField(max_digits=12, decimal_places=4, default=0) error_code = models.CharField(max_length=64, blank=True) error_message = models.TextField(blank=True) submitted_at = models.DateTimeField(null=True, blank=True) completed_at = models.DateTimeField(null=True, blank=True) class Meta: indexes = [ models.Index(fields=["team", "status"]), models.Index(fields=["project", "task_type"]), models.Index(fields=["provider_task_id"]), ] def __str__(self) -> str: return f"{self.task_type}:{self.status}:{self.id}"