У меня есть задача на транскрибацию. Задача выполняется с помощью Celery. Также я использую Assemblyai для транскрибации файла в текст. Мои модели описаны ниже. Я хочу переделать логику таким образом, чтобы были utterances вместо sentences, хочу, чтобы TranscriptionChunk представлял с собой объект следующего формата: 
Transcription: {
id,
speaker_id
text: [
   [time_start, time_end,
    sentence_id, content,
    styles: {}],
   ...
]
}
speaker_id -- это id модели спикера, sentence_id -- это id модели sentence, ее нужно тоже создать, content -- это текст слова, styles -- это стилевые свойства файла (e.g. {

   "bold": boolean,

   "color": string | null,

   "highlight": string | null,

   "italic": boolean,

    "underline": boolean

})

Переделай логику и добавь модели.

**код транскрибации**:
import re
import subprocess
from time import sleep

import sentry_sdk
from celery import shared_task
import assemblyai as aai
from django.conf import settings
from django.utils import timezone
from rest_framework.generics import get_object_or_404

from config.utils import count_tokens, split_text_into_chunks
from config.groq import client
from config.settings import APP_URL
from transcription.models import (
    File,
    TranscriptionChunk,
    TranscriptionAction,
    TranscriptionActionType, FailedTranscriptionTask,
)
from config.s3 import s3_client
import os
import magic
import openai

from transcription.services.transcription import create_or_get_speaker
from users.models import User
from users.tasks import send_transcription_email

aai.settings.api_key = settings.ASSEMBLYAI_API_KEY
openai.api_key = settings.OPENAI_API_KEY
S3_ACCESS_KEY = settings.S3_ACCESS_KEY
S3_SECRET_KEY = settings.S3_SECRET_KEY
S3_ENDPOINT_URL = settings.S3_ENDPOINT_URL
S3_BUCKET_NAME = settings.S3_BUCKET_NAME
S3_REGION = settings.S3_REGION


@shared_task
def process_file(file_id, user_id, language_code):
    file_instance = get_object_or_404(File, id=file_id)
    user = User.objects.get(id=user_id)
    audio_file_path = convert_to_audio(file_instance.file.path)

    sentry_sdk.set_user({ # setting user for the sentry context
        "id": user.id,
        "email": user.email
    })

    if audio_file_path is None:
        return

    config = aai.TranscriptionConfig(
        speaker_labels=True,
        language_code=language_code,
    )
    transcriber = aai.Transcriber()

    try:
        audio_url = transcriber.upload_file(audio_file_path)
        transcript = transcriber.transcribe(audio_url, config=config)

        speakers = {}

        for utterance in transcript.utterances:
            if utterance.speaker not in speakers:
                speakers[utterance.speaker] = create_or_get_speaker(
                    utterance.speaker, file_instance
                )

        transcription_text = transcript.text

        process_transcription(transcript, file_instance, speakers)

        action_type_id = TranscriptionActionType.objects.get_or_create(
            name_en="Summary",
            defaults={
                "system_content": "You are a text summarization assistant that provides concise summaries. "
                                  "Ensure the summary is under 180 characters and is written in the requested language.",
                "user_content": "Process the following text and provide an incremental summary of the text '${text}' in ${language}. Ensure the summary is concise and contains no additional comments or explanations.",
            },
        )[0].id

        transcription_action_id = prompt.delay(
            transcription_text, action_type_id, file_id, language_code
        ).get(disable_sync_subtasks=False)

        s3_client.upload_file(audio_file_path, file_instance.s3_key)

        file_instance.is_transcribed = True
        file_instance.summary = TranscriptionAction.objects.get(
            id=transcription_action_id
        ).content
        file_instance.save()

        FailedTranscriptionTask.objects.filter(file_id=file_id).delete()

        subject = "Транскрибация файла готова!"
        to_email = user.email
        link_to_transcription = f"{APP_URL}/transcription/{file_id}/"
        send_transcription_email.delay(
            to_email,
            re.match(r"^.{0,100}", transcription_text).group(0) + "...",
            link_to_transcription,
            file_instance.true_file_name,
            subject,
        )
    except Exception as e:
        error_message = f"An error occurred inside transcription: {e}"
        print(error_message)
        sentry_sdk.capture_message(f"Transcription has been failed: {e}", level="error")

        failed_task, created = FailedTranscriptionTask.objects.get_or_create(
            file=file_instance,
            defaults={
                'user': user,
                'language_code': language_code,
                'error_message': str(e),
                'retry_count': 0,
            }
        )
        if not created:
            failed_task.error_message = str(e)
            failed_task.retry_count += 1
            failed_task.last_attempted_at = timezone.now()
            failed_task.save()

        return "error"
    finally:
        sentry_sdk.set_user(None)
        cleanup_files([audio_file_path, file_instance.file.path])


def convert_to_audio(file_path):
    mime_type = magic.from_file(file_path, mime=True)
    if mime_type.startswith("video"):
        audio_file = f"{file_path}.mp3"
        try:
            subprocess.run(["ffmpeg", "-y", "-i", file_path, audio_file], check=True)
            return audio_file
        except subprocess.CalledProcessError as e:
            raise Exception(f"Error converting video to audio: {e}")
    elif mime_type.startswith("audio"):
        return file_path
    else:
        raise Exception(f"Unknown type of media: {mime_type}")


def process_transcription(transcript, file_instance, speakers) -> None:
    try:
        chunks_to_create = []

        for sentence in transcript.get_sentences():
            chunk = TranscriptionChunk(
                file=file_instance,
                speaker_id=speakers.get(sentence.speaker),
                start_time=sentence.start / 1000,
                end_time=sentence.end / 1000,
                text=sentence.text,
                duration=(sentence.end - sentence.start) / 1000,
                words=[
                    {"start": word.start, "end": word.end, "text": word.text}
                    for word in sentence.words
                ],
            )
            chunks_to_create.append(chunk)

        TranscriptionChunk.objects.bulk_create(chunks_to_create, batch_size=1000)

    except Exception as e:
        file_instance.delete()
        sentry_sdk.capture_message(f"Transcription has been failed: {e}", level="error")
        print(e)


def save_chunk(
    file_instance,
    speaker_id: int,
    start_time: float,
    end_time: float,
    text: str,
    duration: float,
    words: list,
):
    TranscriptionChunk.objects.create(
        file=file_instance,
        speaker_id=speaker_id,
        start_time=start_time,
        end_time=end_time,
        text=text,
        duration=duration,
        words=words,
    )


def cleanup_files(file_paths):
    for file_path in file_paths:
        if os.path.isfile(file_path):
            try:
                os.remove(file_path)
                print(f"Deleted file: {file_path}")
            except Exception as e:
                print(f"Error deleting file {file_path}: {e}")
                sentry_sdk.capture_message(
                    f"Error deleting file {file_path}: {e}", level="error"
                )

**Модели**:
class File(models.Model):
    file = FileField()
    true_file_name = models.CharField(max_length=255, **NULLABLE)
    created_at = models.DateTimeField(auto_now_add=True)
    s3_key = UniqueS3KeyField(unique=True, **NULLABLE)
    is_transcribed = models.BooleanField(default=False)
    summary = models.CharField(max_length=200, **NULLABLE)
    share_token = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="associated_files",
    )
    language_code = models.CharField(
        max_length=10, default="ru", **NULLABLE, verbose_name="код языка"
    )
    duration = models.FloatField(**NULLABLE)

    def save(self, *args, **kwargs):
        if self.file and not self.true_file_name:
            self.true_file_name = self.file.name

        super().save(*args, **kwargs)

    def _generate_share_token(self):
        """Generate a new unique share token."""
        self.share_token = uuid.uuid4()
        self.save()

    def __str__(self):
        return f"{self.file}"

    class Meta:
        verbose_name = "файл"
        verbose_name_plural = "файлы"
        ordering = ("pk",)


class Speaker(models.Model):
    file = models.ForeignKey(File, on_delete=models.CASCADE, related_name="speakers")
    name = models.CharField(max_length=100, **NULLABLE)

    def __str__(self):
        return self.name


class TranscriptionChunk(models.Model):
    file = models.ForeignKey(File, on_delete=models.CASCADE, related_name="chunks")
    speaker = models.ForeignKey(Speaker, on_delete=models.CASCADE)
    start_time = models.FloatField()
    end_time = models.FloatField()
    duration = models.FloatField(default=0)
    text = models.JSONField(default=list, **NULLABLE)

    def __str__(self):
        return f"Chunk for {self.file.id} [{self.start_time}]: {self.text[:50]}..."

    @classmethod
    def get_file_transcription_text(cls, file_id) -> str:
        """
        Returns the combined text of all chunks for the specified file.
        """
        return " ".join(
            [
                f"speaker: {chunk['speaker__name']}, start time: {chunk['start_time']}, sentence: {chunk['text']}\n"
                for chunk in cls.objects.filter(file_id=file_id).values(
                    "text", "speaker__name", "start_time"
                )
            ]
        )