# This file was auto-generated by Fern from our API Definition.

import contextlib
import json
import typing
from json.decoder import JSONDecodeError

from .. import core
from ..core.api_error import ApiError
from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
from ..core.http_response import AsyncHttpResponse, HttpResponse
from ..core.parse_error import ParsingError
from ..core.request_options import RequestOptions
from ..core.serialization import convert_and_respect_annotation_metadata
from ..core.unchecked_base_model import construct_type
from ..errors.forbidden_error import ForbiddenError
from ..errors.unprocessable_entity_error import UnprocessableEntityError
from ..types.allowed_output_formats import AllowedOutputFormats
from ..types.music_upload_response import MusicUploadResponse
from .types.body_compose_music_v_1_music_post_composition_plan import BodyComposeMusicV1MusicPostCompositionPlan
from .types.body_compose_music_v_1_music_post_model_id import BodyComposeMusicV1MusicPostModelId
from .types.body_compose_music_with_a_detailed_response_v_1_music_detailed_post_composition_plan import (
    BodyComposeMusicWithADetailedResponseV1MusicDetailedPostCompositionPlan,
)
from .types.body_compose_music_with_a_detailed_response_v_1_music_detailed_post_model_id import (
    BodyComposeMusicWithADetailedResponseV1MusicDetailedPostModelId,
)
from .types.body_stream_composed_music_v_1_music_stream_post_composition_plan import (
    BodyStreamComposedMusicV1MusicStreamPostCompositionPlan,
)
from .types.body_stream_composed_music_v_1_music_stream_post_model_id import (
    BodyStreamComposedMusicV1MusicStreamPostModelId,
)
from .types.body_stream_composed_music_with_a_detailed_response_v_1_music_detailed_stream_post_composition_plan import (
    BodyStreamComposedMusicWithADetailedResponseV1MusicDetailedStreamPostCompositionPlan,
)
from .types.body_stream_composed_music_with_a_detailed_response_v_1_music_detailed_stream_post_model_id import (
    BodyStreamComposedMusicWithADetailedResponseV1MusicDetailedStreamPostModelId,
)
from .types.music_compose_detailed_request_output_format import MusicComposeDetailedRequestOutputFormat
from .types.music_compose_detailed_stream_request_output_format import MusicComposeDetailedStreamRequestOutputFormat
from .types.music_compose_request_output_format import MusicComposeRequestOutputFormat
from .types.music_separate_stems_request_stem_variation_id import MusicSeparateStemsRequestStemVariationId
from .types.music_stream_request_output_format import MusicStreamRequestOutputFormat
from .types.music_video_to_music_request_model_id import MusicVideoToMusicRequestModelId
from pydantic import ValidationError

# this is used as the default value for optional parameters
OMIT = typing.cast(typing.Any, ...)


class RawMusicClient:
    def __init__(self, *, client_wrapper: SyncClientWrapper):
        self._client_wrapper = client_wrapper

    @contextlib.contextmanager
    def video_to_music(
        self,
        *,
        videos: typing.List[core.File],
        output_format: typing.Optional[AllowedOutputFormats] = None,
        description: typing.Optional[str] = OMIT,
        tags: typing.Optional[typing.List[str]] = OMIT,
        model_id: typing.Optional[MusicVideoToMusicRequestModelId] = OMIT,
        sign_with_c_2_pa: typing.Optional[bool] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> typing.Iterator[HttpResponse[typing.Iterator[bytes]]]:
        """
        Generate background music from one or more video files. Videos are combined in order. Optional description and style tags influence the generated music.

        Parameters
        ----------
        videos : typing.List[core.File]
            See core.File for more documentation

        output_format : typing.Optional[AllowedOutputFormats]
            Output format of the generated audio. Formatted as codec_sample_rate_bitrate. So an mp3 with 22.05kHz sample rate at 32kbs is represented as mp3_22050_32. MP3 with 192kbps bitrate requires you to be subscribed to Creator tier or above. PCM with 44.1kHz sample rate requires you to be subscribed to Pro tier or above. Note that the μ-law format (sometimes written mu-law, often approximated as u-law) is commonly used for Twilio audio inputs.

        description : typing.Optional[str]
            Optional text description of the music you want. A maximum of 1000 characters is allowed.

        tags : typing.Optional[typing.List[str]]
            Optional list of style tags (e.g. ['upbeat', 'cinematic']). A maximum of 10 tags is allowed.

        model_id : typing.Optional[MusicVideoToMusicRequestModelId]
            The model to use for the generation.

        sign_with_c_2_pa : typing.Optional[bool]
            Whether to sign the generated song with C2PA. Applicable only for mp3 files.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.

        Returns
        -------
        typing.Iterator[HttpResponse[typing.Iterator[bytes]]]
            Generated audio file matching the video. Content-Type and file extension depend on the output_format parameter (default mp3).
        """
        with self._client_wrapper.httpx_client.stream(
            "v1/music/video-to-music",
            method="POST",
            params={
                "output_format": output_format,
            },
            data={
                "description": description,
                "tags": tags,
                "model_id": model_id,
                "sign_with_c2pa": sign_with_c_2_pa,
            },
            files={
                "videos": videos,
            },
            request_options=request_options,
            omit=OMIT,
            force_multipart=True,
        ) as _response:

            def _stream() -> HttpResponse[typing.Iterator[bytes]]:
                try:
                    if 200 <= _response.status_code < 300:
                        _chunk_size = request_options.get("chunk_size", 1024) if request_options is not None else 1024
                        return HttpResponse(
                            response=_response, data=(_chunk for _chunk in _response.iter_bytes(chunk_size=_chunk_size))
                        )
                    _response.read()
                    if _response.status_code == 403:
                        raise ForbiddenError(
                            headers=dict(_response.headers),
                            body=typing.cast(
                                typing.Any,
                                construct_type(
                                    type_=typing.Any,  # type: ignore
                                    object_=_response.json(),
                                ),
                            ),
                        )
                    if _response.status_code == 422:
                        raise UnprocessableEntityError(
                            headers=dict(_response.headers),
                            body=typing.cast(
                                typing.Any,
                                construct_type(
                                    type_=typing.Any,  # type: ignore
                                    object_=_response.json(),
                                ),
                            ),
                        )
                    _response_json = _response.json()
                except JSONDecodeError:
                    raise ApiError(
                        status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
                    )
                except ValidationError as e:
                    raise ParsingError(
                        status_code=_response.status_code,
                        headers=dict(_response.headers),
                        body=_response.json(),
                        cause=e,
                    )
                raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

            yield _stream()

    @contextlib.contextmanager
    def compose(
        self,
        *,
        output_format: typing.Optional[MusicComposeRequestOutputFormat] = None,
        prompt: typing.Optional[str] = OMIT,
        composition_plan: typing.Optional[BodyComposeMusicV1MusicPostCompositionPlan] = OMIT,
        music_length_ms: typing.Optional[int] = OMIT,
        model_id: typing.Optional[BodyComposeMusicV1MusicPostModelId] = OMIT,
        seed: typing.Optional[int] = OMIT,
        force_instrumental: typing.Optional[bool] = OMIT,
        finetune_id: typing.Optional[str] = OMIT,
        respect_sections_durations: typing.Optional[bool] = OMIT,
        store_for_inpainting: typing.Optional[bool] = OMIT,
        sign_with_c_2_pa: typing.Optional[bool] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> typing.Iterator[HttpResponse[typing.Iterator[bytes]]]:
        """
        Compose a song from a prompt or a composition plan.

        Parameters
        ----------
        output_format : typing.Optional[MusicComposeRequestOutputFormat]
            Output format of the generated audio. Formatted as codec_sample_rate_bitrate. Use "auto" (the default) to let the API pick the best format for the selected model: mp3_44100_128 for v1 models and mp3_48000_192 for v2 models.

        prompt : typing.Optional[str]
            A simple text prompt to generate a song from. Cannot be used in conjunction with `composition_plan`.

        composition_plan : typing.Optional[BodyComposeMusicV1MusicPostCompositionPlan]
            A detailed composition plan to guide music generation. Cannot be used in conjunction with `prompt`.

        music_length_ms : typing.Optional[int]
            The length of the song to generate in milliseconds. Used only in conjunction with `prompt`. Must be between 3000ms and 600000ms. Optional - if not provided, the model will choose a length based on the prompt.

        model_id : typing.Optional[BodyComposeMusicV1MusicPostModelId]
            The model to use for the generation.

        seed : typing.Optional[int]
            Random seed to initialize the music generation process. Providing the same seed with the same parameters can help achieve more consistent results, but exact reproducibility is not guaranteed and outputs may change across system updates. Cannot be used in conjunction with prompt.

        force_instrumental : typing.Optional[bool]
            If true, guarantees that the generated song will be instrumental. If false, the song may or may not be instrumental depending on the `prompt`. Can only be used with `prompt`.

        finetune_id : typing.Optional[str]
            The ID of the finetune to use for the generation

        respect_sections_durations : typing.Optional[bool]
            Controls how strictly section durations in the `composition_plan` are enforced. Only used with `composition_plan` and only applies to `music_v1`; for `music_v2` section durations are always enforced and this is ignored. When false for `music_v1`, the model may adjust individual section durations for better quality and latency, while preserving the total song duration from the plan.

        store_for_inpainting : typing.Optional[bool]
            Whether to store the generated song for inpainting.

        sign_with_c_2_pa : typing.Optional[bool]
            Whether to sign the generated song with C2PA. Applicable only for mp3 files.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.

        Returns
        -------
        typing.Iterator[HttpResponse[typing.Iterator[bytes]]]
            The generated audio file in the format specified
        """
        with self._client_wrapper.httpx_client.stream(
            "v1/music",
            method="POST",
            params={
                "output_format": output_format,
            },
            json={
                "prompt": prompt,
                "composition_plan": convert_and_respect_annotation_metadata(
                    object_=composition_plan, annotation=BodyComposeMusicV1MusicPostCompositionPlan, direction="write"
                ),
                "music_length_ms": music_length_ms,
                "model_id": model_id,
                "seed": seed,
                "force_instrumental": force_instrumental,
                "finetune_id": finetune_id,
                "respect_sections_durations": respect_sections_durations,
                "store_for_inpainting": store_for_inpainting,
                "sign_with_c2pa": sign_with_c_2_pa,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        ) as _response:

            def _stream() -> HttpResponse[typing.Iterator[bytes]]:
                try:
                    if 200 <= _response.status_code < 300:
                        _chunk_size = request_options.get("chunk_size", 1024) if request_options is not None else 1024
                        return HttpResponse(
                            response=_response, data=(_chunk for _chunk in _response.iter_bytes(chunk_size=_chunk_size))
                        )
                    _response.read()
                    if _response.status_code == 422:
                        raise UnprocessableEntityError(
                            headers=dict(_response.headers),
                            body=typing.cast(
                                typing.Any,
                                construct_type(
                                    type_=typing.Any,  # type: ignore
                                    object_=_response.json(),
                                ),
                            ),
                        )
                    _response_json = _response.json()
                except JSONDecodeError:
                    raise ApiError(
                        status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
                    )
                except ValidationError as e:
                    raise ParsingError(
                        status_code=_response.status_code,
                        headers=dict(_response.headers),
                        body=_response.json(),
                        cause=e,
                    )
                raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

            yield _stream()

    @contextlib.contextmanager
    def compose_detailed(
        self,
        *,
        output_format: typing.Optional[MusicComposeDetailedRequestOutputFormat] = None,
        prompt: typing.Optional[str] = OMIT,
        composition_plan: typing.Optional[
            BodyComposeMusicWithADetailedResponseV1MusicDetailedPostCompositionPlan
        ] = OMIT,
        music_length_ms: typing.Optional[int] = OMIT,
        model_id: typing.Optional[BodyComposeMusicWithADetailedResponseV1MusicDetailedPostModelId] = OMIT,
        seed: typing.Optional[int] = OMIT,
        force_instrumental: typing.Optional[bool] = OMIT,
        finetune_id: typing.Optional[str] = OMIT,
        respect_sections_durations: typing.Optional[bool] = OMIT,
        store_for_inpainting: typing.Optional[bool] = OMIT,
        with_timestamps: typing.Optional[bool] = OMIT,
        sign_with_c_2_pa: typing.Optional[bool] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> typing.Iterator[HttpResponse[typing.Iterator[bytes]]]:
        """
        Compose a song from a prompt or a composition plan.

        Parameters
        ----------
        output_format : typing.Optional[MusicComposeDetailedRequestOutputFormat]
            Output format of the generated audio. Formatted as codec_sample_rate_bitrate. Use "auto" (the default) to let the API pick the best format for the selected model: mp3_44100_128 for v1 models and mp3_48000_192 for v2 models.

        prompt : typing.Optional[str]
            A simple text prompt to generate a song from. Cannot be used in conjunction with `composition_plan`.

        composition_plan : typing.Optional[BodyComposeMusicWithADetailedResponseV1MusicDetailedPostCompositionPlan]
            A detailed composition plan to guide music generation. Cannot be used in conjunction with `prompt`.

        music_length_ms : typing.Optional[int]
            The length of the song to generate in milliseconds. Used only in conjunction with `prompt`. Must be between 3000ms and 600000ms. Optional - if not provided, the model will choose a length based on the prompt.

        model_id : typing.Optional[BodyComposeMusicWithADetailedResponseV1MusicDetailedPostModelId]
            The model to use for the generation.

        seed : typing.Optional[int]
            Random seed to initialize the music generation process. Providing the same seed with the same parameters can help achieve more consistent results, but exact reproducibility is not guaranteed and outputs may change across system updates. Cannot be used in conjunction with prompt.

        force_instrumental : typing.Optional[bool]
            If true, guarantees that the generated song will be instrumental. If false, the song may or may not be instrumental depending on the `prompt`. Can only be used with `prompt`.

        finetune_id : typing.Optional[str]
            The ID of the finetune to use for the generation

        respect_sections_durations : typing.Optional[bool]
            Controls how strictly section durations in the `composition_plan` are enforced. Only used with `composition_plan` and only applies to `music_v1`; for `music_v2` section durations are always enforced and this is ignored. When false for `music_v1`, the model may adjust individual section durations for better quality and latency, while preserving the total song duration from the plan.

        store_for_inpainting : typing.Optional[bool]
            Whether to store the generated song for inpainting.

        with_timestamps : typing.Optional[bool]
            Whether to return the timestamps of the words in the generated song.

        sign_with_c_2_pa : typing.Optional[bool]
            Whether to sign the generated song with C2PA. Applicable only for mp3 files.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.

        Returns
        -------
        typing.Iterator[HttpResponse[typing.Iterator[bytes]]]
            Multipart/mixed response with JSON metadata and binary audio file
        """
        with self._client_wrapper.httpx_client.stream(
            "v1/music/detailed",
            method="POST",
            params={
                "output_format": output_format,
            },
            json={
                "prompt": prompt,
                "composition_plan": convert_and_respect_annotation_metadata(
                    object_=composition_plan,
                    annotation=BodyComposeMusicWithADetailedResponseV1MusicDetailedPostCompositionPlan,
                    direction="write",
                ),
                "music_length_ms": music_length_ms,
                "model_id": model_id,
                "seed": seed,
                "force_instrumental": force_instrumental,
                "finetune_id": finetune_id,
                "respect_sections_durations": respect_sections_durations,
                "store_for_inpainting": store_for_inpainting,
                "with_timestamps": with_timestamps,
                "sign_with_c2pa": sign_with_c_2_pa,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        ) as _response:

            def _stream() -> HttpResponse[typing.Iterator[bytes]]:
                try:
                    if 200 <= _response.status_code < 300:
                        _chunk_size = request_options.get("chunk_size", 1024) if request_options is not None else 1024
                        return HttpResponse(
                            response=_response, data=(_chunk for _chunk in _response.iter_bytes(chunk_size=_chunk_size))
                        )
                    _response.read()
                    if _response.status_code == 422:
                        raise UnprocessableEntityError(
                            headers=dict(_response.headers),
                            body=typing.cast(
                                typing.Any,
                                construct_type(
                                    type_=typing.Any,  # type: ignore
                                    object_=_response.json(),
                                ),
                            ),
                        )
                    _response_json = _response.json()
                except JSONDecodeError:
                    raise ApiError(
                        status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
                    )
                except ValidationError as e:
                    raise ParsingError(
                        status_code=_response.status_code,
                        headers=dict(_response.headers),
                        body=_response.json(),
                        cause=e,
                    )
                raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

            yield _stream()

    @contextlib.contextmanager
    def compose_detailed_stream(
        self,
        *,
        output_format: typing.Optional[MusicComposeDetailedStreamRequestOutputFormat] = None,
        prompt: typing.Optional[str] = OMIT,
        composition_plan: typing.Optional[
            BodyStreamComposedMusicWithADetailedResponseV1MusicDetailedStreamPostCompositionPlan
        ] = OMIT,
        music_length_ms: typing.Optional[int] = OMIT,
        model_id: typing.Optional[BodyStreamComposedMusicWithADetailedResponseV1MusicDetailedStreamPostModelId] = OMIT,
        seed: typing.Optional[int] = OMIT,
        force_instrumental: typing.Optional[bool] = OMIT,
        finetune_id: typing.Optional[str] = OMIT,
        store_for_inpainting: typing.Optional[bool] = OMIT,
        with_timestamps: typing.Optional[bool] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> typing.Iterator[HttpResponse[typing.Iterator[str]]]:
        """
        Stream a song and its detailed metadata using Server-Sent Events (SSE).

        Parameters
        ----------
        output_format : typing.Optional[MusicComposeDetailedStreamRequestOutputFormat]
            Output format of the generated audio. Formatted as codec_sample_rate_bitrate. Use "auto" (the default) to let the API pick the best format for the selected model: mp3_44100_128 for v1 models and mp3_48000_192 for v2 models.

        prompt : typing.Optional[str]
            A simple text prompt to generate a song from. Cannot be used in conjunction with `composition_plan`.

        composition_plan : typing.Optional[BodyStreamComposedMusicWithADetailedResponseV1MusicDetailedStreamPostCompositionPlan]
            A detailed composition plan to guide music generation. Cannot be used in conjunction with `prompt`.

        music_length_ms : typing.Optional[int]
            The length of the song to generate in milliseconds. Used only in conjunction with `prompt`. Must be between 3000ms and 600000ms. Optional - if not provided, the model will choose a length based on the prompt.

        model_id : typing.Optional[BodyStreamComposedMusicWithADetailedResponseV1MusicDetailedStreamPostModelId]
            The model to use for the generation.

        seed : typing.Optional[int]
            Random seed to initialize the music generation process. Providing the same seed with the same parameters can help achieve more consistent results, but exact reproducibility is not guaranteed and outputs may change across system updates. Cannot be used in conjunction with prompt.

        force_instrumental : typing.Optional[bool]
            If true, guarantees that the generated song will be instrumental. If false, the song may or may not be instrumental depending on the `prompt`. Can only be used with `prompt`.

        finetune_id : typing.Optional[str]
            The ID of the finetune to use for the generation

        store_for_inpainting : typing.Optional[bool]
            Whether to store the generated song for inpainting.

        with_timestamps : typing.Optional[bool]
            Whether to return the timestamps of the words in the generated song.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Yields
        ------
        typing.Iterator[HttpResponse[typing.Iterator[str]]]
            Server-Sent Events for composition plan, song metadata, audio chunks with optional word timestamps, and completion.
        """
        with self._client_wrapper.httpx_client.stream(
            "v1/music/detailed/stream",
            method="POST",
            params={
                "output_format": output_format,
            },
            json={
                "prompt": prompt,
                "composition_plan": convert_and_respect_annotation_metadata(
                    object_=composition_plan,
                    annotation=BodyStreamComposedMusicWithADetailedResponseV1MusicDetailedStreamPostCompositionPlan,
                    direction="write",
                ),
                "music_length_ms": music_length_ms,
                "model_id": model_id,
                "seed": seed,
                "force_instrumental": force_instrumental,
                "finetune_id": finetune_id,
                "store_for_inpainting": store_for_inpainting,
                "with_timestamps": with_timestamps,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        ) as _response:

            def _stream() -> HttpResponse[typing.Iterator[str]]:
                try:
                    if 200 <= _response.status_code < 300:

                        def _iter():
                            for _text in _response.iter_lines():
                                try:
                                    if len(_text) == 0:
                                        continue
                                    yield typing.cast(
                                        str,
                                        construct_type(
                                            type_=str,  # type: ignore
                                            object_=json.loads(_text),
                                        ),
                                    )
                                except Exception:
                                    pass
                            return

                        return HttpResponse(response=_response, data=_iter())
                    _response.read()
                    if _response.status_code == 422:
                        raise UnprocessableEntityError(
                            headers=dict(_response.headers),
                            body=typing.cast(
                                typing.Any,
                                construct_type(
                                    type_=typing.Any,  # type: ignore
                                    object_=_response.json(),
                                ),
                            ),
                        )
                    _response_json = _response.json()
                except JSONDecodeError:
                    raise ApiError(
                        status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
                    )
                except ValidationError as e:
                    raise ParsingError(
                        status_code=_response.status_code,
                        headers=dict(_response.headers),
                        body=_response.json(),
                        cause=e,
                    )
                raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

            yield _stream()

    @contextlib.contextmanager
    def stream(
        self,
        *,
        output_format: typing.Optional[MusicStreamRequestOutputFormat] = None,
        prompt: typing.Optional[str] = OMIT,
        composition_plan: typing.Optional[BodyStreamComposedMusicV1MusicStreamPostCompositionPlan] = OMIT,
        music_length_ms: typing.Optional[int] = OMIT,
        model_id: typing.Optional[BodyStreamComposedMusicV1MusicStreamPostModelId] = OMIT,
        seed: typing.Optional[int] = OMIT,
        force_instrumental: typing.Optional[bool] = OMIT,
        finetune_id: typing.Optional[str] = OMIT,
        store_for_inpainting: typing.Optional[bool] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> typing.Iterator[HttpResponse[typing.Iterator[bytes]]]:
        """
        Stream a composed song from a prompt or a composition plan.

        Parameters
        ----------
        output_format : typing.Optional[MusicStreamRequestOutputFormat]
            Output format of the generated audio. Formatted as codec_sample_rate_bitrate. Use "auto" (the default) to let the API pick the best format for the selected model: mp3_44100_128 for v1 models and mp3_48000_192 for v2 models.

        prompt : typing.Optional[str]
            A simple text prompt to generate a song from. Cannot be used in conjunction with `composition_plan`.

        composition_plan : typing.Optional[BodyStreamComposedMusicV1MusicStreamPostCompositionPlan]
            A detailed composition plan to guide music generation. Cannot be used in conjunction with `prompt`.

        music_length_ms : typing.Optional[int]
            The length of the song to generate in milliseconds. Used only in conjunction with `prompt`. Must be between 3000ms and 600000ms. Optional - if not provided, the model will choose a length based on the prompt.

        model_id : typing.Optional[BodyStreamComposedMusicV1MusicStreamPostModelId]
            The model to use for the generation.

        seed : typing.Optional[int]
            Random seed to initialize the music generation process. Providing the same seed with the same parameters can help achieve more consistent results, but exact reproducibility is not guaranteed and outputs may change across system updates. Cannot be used in conjunction with prompt.

        force_instrumental : typing.Optional[bool]
            If true, guarantees that the generated song will be instrumental. If false, the song may or may not be instrumental depending on the `prompt`. Can only be used with `prompt`.

        finetune_id : typing.Optional[str]
            The ID of the finetune to use for the generation

        store_for_inpainting : typing.Optional[bool]
            Whether to store the generated song for inpainting.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.

        Returns
        -------
        typing.Iterator[HttpResponse[typing.Iterator[bytes]]]
            Streaming audio data in the format specified
        """
        with self._client_wrapper.httpx_client.stream(
            "v1/music/stream",
            method="POST",
            params={
                "output_format": output_format,
            },
            json={
                "prompt": prompt,
                "composition_plan": convert_and_respect_annotation_metadata(
                    object_=composition_plan,
                    annotation=BodyStreamComposedMusicV1MusicStreamPostCompositionPlan,
                    direction="write",
                ),
                "music_length_ms": music_length_ms,
                "model_id": model_id,
                "seed": seed,
                "force_instrumental": force_instrumental,
                "finetune_id": finetune_id,
                "store_for_inpainting": store_for_inpainting,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        ) as _response:

            def _stream() -> HttpResponse[typing.Iterator[bytes]]:
                try:
                    if 200 <= _response.status_code < 300:
                        _chunk_size = request_options.get("chunk_size", 1024) if request_options is not None else 1024
                        return HttpResponse(
                            response=_response, data=(_chunk for _chunk in _response.iter_bytes(chunk_size=_chunk_size))
                        )
                    _response.read()
                    if _response.status_code == 422:
                        raise UnprocessableEntityError(
                            headers=dict(_response.headers),
                            body=typing.cast(
                                typing.Any,
                                construct_type(
                                    type_=typing.Any,  # type: ignore
                                    object_=_response.json(),
                                ),
                            ),
                        )
                    _response_json = _response.json()
                except JSONDecodeError:
                    raise ApiError(
                        status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
                    )
                except ValidationError as e:
                    raise ParsingError(
                        status_code=_response.status_code,
                        headers=dict(_response.headers),
                        body=_response.json(),
                        cause=e,
                    )
                raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

            yield _stream()

    def upload(
        self,
        *,
        file: core.File,
        extract_composition_plan: typing.Optional[str] = OMIT,
        with_timestamps: typing.Optional[bool] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[MusicUploadResponse]:
        """
        Upload a music file to be later used for inpainting. Price for uploading is the same as the one for song generation. All uploaded content gets inspected for copyright infringement. If copyrighted content is detected, half of the request cost is still charged.

        Parameters
        ----------
        file : core.File
            See core.File for more documentation

        extract_composition_plan : typing.Optional[str]
            Whether to generate and return the composition plan for the uploaded song. Pass a model id (`music_v1` or `music_v2`) to control which composition plan format is returned. Passing `true`/`false` is deprecated; `true` defaults to the `music_v1` plan format. Enabling this will increase the latency.

        with_timestamps : typing.Optional[bool]
            Whether to transcribe the uploaded song and return word-level timestamps. If True, the response will include words_timestamps but will increase the latency.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[MusicUploadResponse]
            Successfully uploaded music file with optional composition plan
        """
        _response = self._client_wrapper.httpx_client.request(
            "v1/music/upload",
            method="POST",
            data={
                "extract_composition_plan": extract_composition_plan,
                "with_timestamps": with_timestamps,
            },
            files={
                "file": file,
            },
            request_options=request_options,
            omit=OMIT,
            force_multipart=True,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    MusicUploadResponse,
                    construct_type(
                        type_=MusicUploadResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        construct_type(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        except ValidationError as e:
            raise ParsingError(
                status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
            )
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    @contextlib.contextmanager
    def separate_stems(
        self,
        *,
        file: core.File,
        output_format: typing.Optional[AllowedOutputFormats] = None,
        stem_variation_id: typing.Optional[MusicSeparateStemsRequestStemVariationId] = OMIT,
        sign_with_c_2_pa: typing.Optional[bool] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> typing.Iterator[HttpResponse[typing.Iterator[bytes]]]:
        """
        Separate an audio file into individual stems. This endpoint might have high latency, depending on the length of the audio file.

        Parameters
        ----------
        file : core.File
            See core.File for more documentation

        output_format : typing.Optional[AllowedOutputFormats]
            Output format of the generated audio. Formatted as codec_sample_rate_bitrate. So an mp3 with 22.05kHz sample rate at 32kbs is represented as mp3_22050_32. MP3 with 192kbps bitrate requires you to be subscribed to Creator tier or above. PCM with 44.1kHz sample rate requires you to be subscribed to Pro tier or above. Note that the μ-law format (sometimes written mu-law, often approximated as u-law) is commonly used for Twilio audio inputs.

        stem_variation_id : typing.Optional[MusicSeparateStemsRequestStemVariationId]
            The id of the stem variation to use.

        sign_with_c_2_pa : typing.Optional[bool]
            Whether to sign the generated song with C2PA. Applicable only for mp3 files.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.

        Returns
        -------
        typing.Iterator[HttpResponse[typing.Iterator[bytes]]]
            ZIP archive containing separated audio stems. Each stem is provided as a separate audio file in the requested output format.
        """
        with self._client_wrapper.httpx_client.stream(
            "v1/music/stem-separation",
            method="POST",
            params={
                "output_format": output_format,
            },
            data={
                "stem_variation_id": stem_variation_id,
                "sign_with_c2pa": sign_with_c_2_pa,
            },
            files={
                "file": file,
            },
            request_options=request_options,
            omit=OMIT,
            force_multipart=True,
        ) as _response:

            def _stream() -> HttpResponse[typing.Iterator[bytes]]:
                try:
                    if 200 <= _response.status_code < 300:
                        _chunk_size = request_options.get("chunk_size", 1024) if request_options is not None else 1024
                        return HttpResponse(
                            response=_response, data=(_chunk for _chunk in _response.iter_bytes(chunk_size=_chunk_size))
                        )
                    _response.read()
                    if _response.status_code == 422:
                        raise UnprocessableEntityError(
                            headers=dict(_response.headers),
                            body=typing.cast(
                                typing.Any,
                                construct_type(
                                    type_=typing.Any,  # type: ignore
                                    object_=_response.json(),
                                ),
                            ),
                        )
                    _response_json = _response.json()
                except JSONDecodeError:
                    raise ApiError(
                        status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
                    )
                except ValidationError as e:
                    raise ParsingError(
                        status_code=_response.status_code,
                        headers=dict(_response.headers),
                        body=_response.json(),
                        cause=e,
                    )
                raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

            yield _stream()


class AsyncRawMusicClient:
    def __init__(self, *, client_wrapper: AsyncClientWrapper):
        self._client_wrapper = client_wrapper

    @contextlib.asynccontextmanager
    async def video_to_music(
        self,
        *,
        videos: typing.List[core.File],
        output_format: typing.Optional[AllowedOutputFormats] = None,
        description: typing.Optional[str] = OMIT,
        tags: typing.Optional[typing.List[str]] = OMIT,
        model_id: typing.Optional[MusicVideoToMusicRequestModelId] = OMIT,
        sign_with_c_2_pa: typing.Optional[bool] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]:
        """
        Generate background music from one or more video files. Videos are combined in order. Optional description and style tags influence the generated music.

        Parameters
        ----------
        videos : typing.List[core.File]
            See core.File for more documentation

        output_format : typing.Optional[AllowedOutputFormats]
            Output format of the generated audio. Formatted as codec_sample_rate_bitrate. So an mp3 with 22.05kHz sample rate at 32kbs is represented as mp3_22050_32. MP3 with 192kbps bitrate requires you to be subscribed to Creator tier or above. PCM with 44.1kHz sample rate requires you to be subscribed to Pro tier or above. Note that the μ-law format (sometimes written mu-law, often approximated as u-law) is commonly used for Twilio audio inputs.

        description : typing.Optional[str]
            Optional text description of the music you want. A maximum of 1000 characters is allowed.

        tags : typing.Optional[typing.List[str]]
            Optional list of style tags (e.g. ['upbeat', 'cinematic']). A maximum of 10 tags is allowed.

        model_id : typing.Optional[MusicVideoToMusicRequestModelId]
            The model to use for the generation.

        sign_with_c_2_pa : typing.Optional[bool]
            Whether to sign the generated song with C2PA. Applicable only for mp3 files.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.

        Returns
        -------
        typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]
            Generated audio file matching the video. Content-Type and file extension depend on the output_format parameter (default mp3).
        """
        async with self._client_wrapper.httpx_client.stream(
            "v1/music/video-to-music",
            method="POST",
            params={
                "output_format": output_format,
            },
            data={
                "description": description,
                "tags": tags,
                "model_id": model_id,
                "sign_with_c2pa": sign_with_c_2_pa,
            },
            files={
                "videos": videos,
            },
            request_options=request_options,
            omit=OMIT,
            force_multipart=True,
        ) as _response:

            async def _stream() -> AsyncHttpResponse[typing.AsyncIterator[bytes]]:
                try:
                    if 200 <= _response.status_code < 300:
                        _chunk_size = request_options.get("chunk_size", 1024) if request_options is not None else 1024
                        return AsyncHttpResponse(
                            response=_response,
                            data=(_chunk async for _chunk in _response.aiter_bytes(chunk_size=_chunk_size)),
                        )
                    await _response.aread()
                    if _response.status_code == 403:
                        raise ForbiddenError(
                            headers=dict(_response.headers),
                            body=typing.cast(
                                typing.Any,
                                construct_type(
                                    type_=typing.Any,  # type: ignore
                                    object_=_response.json(),
                                ),
                            ),
                        )
                    if _response.status_code == 422:
                        raise UnprocessableEntityError(
                            headers=dict(_response.headers),
                            body=typing.cast(
                                typing.Any,
                                construct_type(
                                    type_=typing.Any,  # type: ignore
                                    object_=_response.json(),
                                ),
                            ),
                        )
                    _response_json = _response.json()
                except JSONDecodeError:
                    raise ApiError(
                        status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
                    )
                except ValidationError as e:
                    raise ParsingError(
                        status_code=_response.status_code,
                        headers=dict(_response.headers),
                        body=_response.json(),
                        cause=e,
                    )
                raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

            yield await _stream()

    @contextlib.asynccontextmanager
    async def compose(
        self,
        *,
        output_format: typing.Optional[MusicComposeRequestOutputFormat] = None,
        prompt: typing.Optional[str] = OMIT,
        composition_plan: typing.Optional[BodyComposeMusicV1MusicPostCompositionPlan] = OMIT,
        music_length_ms: typing.Optional[int] = OMIT,
        model_id: typing.Optional[BodyComposeMusicV1MusicPostModelId] = OMIT,
        seed: typing.Optional[int] = OMIT,
        force_instrumental: typing.Optional[bool] = OMIT,
        finetune_id: typing.Optional[str] = OMIT,
        respect_sections_durations: typing.Optional[bool] = OMIT,
        store_for_inpainting: typing.Optional[bool] = OMIT,
        sign_with_c_2_pa: typing.Optional[bool] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]:
        """
        Compose a song from a prompt or a composition plan.

        Parameters
        ----------
        output_format : typing.Optional[MusicComposeRequestOutputFormat]
            Output format of the generated audio. Formatted as codec_sample_rate_bitrate. Use "auto" (the default) to let the API pick the best format for the selected model: mp3_44100_128 for v1 models and mp3_48000_192 for v2 models.

        prompt : typing.Optional[str]
            A simple text prompt to generate a song from. Cannot be used in conjunction with `composition_plan`.

        composition_plan : typing.Optional[BodyComposeMusicV1MusicPostCompositionPlan]
            A detailed composition plan to guide music generation. Cannot be used in conjunction with `prompt`.

        music_length_ms : typing.Optional[int]
            The length of the song to generate in milliseconds. Used only in conjunction with `prompt`. Must be between 3000ms and 600000ms. Optional - if not provided, the model will choose a length based on the prompt.

        model_id : typing.Optional[BodyComposeMusicV1MusicPostModelId]
            The model to use for the generation.

        seed : typing.Optional[int]
            Random seed to initialize the music generation process. Providing the same seed with the same parameters can help achieve more consistent results, but exact reproducibility is not guaranteed and outputs may change across system updates. Cannot be used in conjunction with prompt.

        force_instrumental : typing.Optional[bool]
            If true, guarantees that the generated song will be instrumental. If false, the song may or may not be instrumental depending on the `prompt`. Can only be used with `prompt`.

        finetune_id : typing.Optional[str]
            The ID of the finetune to use for the generation

        respect_sections_durations : typing.Optional[bool]
            Controls how strictly section durations in the `composition_plan` are enforced. Only used with `composition_plan` and only applies to `music_v1`; for `music_v2` section durations are always enforced and this is ignored. When false for `music_v1`, the model may adjust individual section durations for better quality and latency, while preserving the total song duration from the plan.

        store_for_inpainting : typing.Optional[bool]
            Whether to store the generated song for inpainting.

        sign_with_c_2_pa : typing.Optional[bool]
            Whether to sign the generated song with C2PA. Applicable only for mp3 files.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.

        Returns
        -------
        typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]
            The generated audio file in the format specified
        """
        async with self._client_wrapper.httpx_client.stream(
            "v1/music",
            method="POST",
            params={
                "output_format": output_format,
            },
            json={
                "prompt": prompt,
                "composition_plan": convert_and_respect_annotation_metadata(
                    object_=composition_plan, annotation=BodyComposeMusicV1MusicPostCompositionPlan, direction="write"
                ),
                "music_length_ms": music_length_ms,
                "model_id": model_id,
                "seed": seed,
                "force_instrumental": force_instrumental,
                "finetune_id": finetune_id,
                "respect_sections_durations": respect_sections_durations,
                "store_for_inpainting": store_for_inpainting,
                "sign_with_c2pa": sign_with_c_2_pa,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        ) as _response:

            async def _stream() -> AsyncHttpResponse[typing.AsyncIterator[bytes]]:
                try:
                    if 200 <= _response.status_code < 300:
                        _chunk_size = request_options.get("chunk_size", 1024) if request_options is not None else 1024
                        return AsyncHttpResponse(
                            response=_response,
                            data=(_chunk async for _chunk in _response.aiter_bytes(chunk_size=_chunk_size)),
                        )
                    await _response.aread()
                    if _response.status_code == 422:
                        raise UnprocessableEntityError(
                            headers=dict(_response.headers),
                            body=typing.cast(
                                typing.Any,
                                construct_type(
                                    type_=typing.Any,  # type: ignore
                                    object_=_response.json(),
                                ),
                            ),
                        )
                    _response_json = _response.json()
                except JSONDecodeError:
                    raise ApiError(
                        status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
                    )
                except ValidationError as e:
                    raise ParsingError(
                        status_code=_response.status_code,
                        headers=dict(_response.headers),
                        body=_response.json(),
                        cause=e,
                    )
                raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

            yield await _stream()

    @contextlib.asynccontextmanager
    async def compose_detailed(
        self,
        *,
        output_format: typing.Optional[MusicComposeDetailedRequestOutputFormat] = None,
        prompt: typing.Optional[str] = OMIT,
        composition_plan: typing.Optional[
            BodyComposeMusicWithADetailedResponseV1MusicDetailedPostCompositionPlan
        ] = OMIT,
        music_length_ms: typing.Optional[int] = OMIT,
        model_id: typing.Optional[BodyComposeMusicWithADetailedResponseV1MusicDetailedPostModelId] = OMIT,
        seed: typing.Optional[int] = OMIT,
        force_instrumental: typing.Optional[bool] = OMIT,
        finetune_id: typing.Optional[str] = OMIT,
        respect_sections_durations: typing.Optional[bool] = OMIT,
        store_for_inpainting: typing.Optional[bool] = OMIT,
        with_timestamps: typing.Optional[bool] = OMIT,
        sign_with_c_2_pa: typing.Optional[bool] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]:
        """
        Compose a song from a prompt or a composition plan.

        Parameters
        ----------
        output_format : typing.Optional[MusicComposeDetailedRequestOutputFormat]
            Output format of the generated audio. Formatted as codec_sample_rate_bitrate. Use "auto" (the default) to let the API pick the best format for the selected model: mp3_44100_128 for v1 models and mp3_48000_192 for v2 models.

        prompt : typing.Optional[str]
            A simple text prompt to generate a song from. Cannot be used in conjunction with `composition_plan`.

        composition_plan : typing.Optional[BodyComposeMusicWithADetailedResponseV1MusicDetailedPostCompositionPlan]
            A detailed composition plan to guide music generation. Cannot be used in conjunction with `prompt`.

        music_length_ms : typing.Optional[int]
            The length of the song to generate in milliseconds. Used only in conjunction with `prompt`. Must be between 3000ms and 600000ms. Optional - if not provided, the model will choose a length based on the prompt.

        model_id : typing.Optional[BodyComposeMusicWithADetailedResponseV1MusicDetailedPostModelId]
            The model to use for the generation.

        seed : typing.Optional[int]
            Random seed to initialize the music generation process. Providing the same seed with the same parameters can help achieve more consistent results, but exact reproducibility is not guaranteed and outputs may change across system updates. Cannot be used in conjunction with prompt.

        force_instrumental : typing.Optional[bool]
            If true, guarantees that the generated song will be instrumental. If false, the song may or may not be instrumental depending on the `prompt`. Can only be used with `prompt`.

        finetune_id : typing.Optional[str]
            The ID of the finetune to use for the generation

        respect_sections_durations : typing.Optional[bool]
            Controls how strictly section durations in the `composition_plan` are enforced. Only used with `composition_plan` and only applies to `music_v1`; for `music_v2` section durations are always enforced and this is ignored. When false for `music_v1`, the model may adjust individual section durations for better quality and latency, while preserving the total song duration from the plan.

        store_for_inpainting : typing.Optional[bool]
            Whether to store the generated song for inpainting.

        with_timestamps : typing.Optional[bool]
            Whether to return the timestamps of the words in the generated song.

        sign_with_c_2_pa : typing.Optional[bool]
            Whether to sign the generated song with C2PA. Applicable only for mp3 files.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.

        Returns
        -------
        typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]
            Multipart/mixed response with JSON metadata and binary audio file
        """
        async with self._client_wrapper.httpx_client.stream(
            "v1/music/detailed",
            method="POST",
            params={
                "output_format": output_format,
            },
            json={
                "prompt": prompt,
                "composition_plan": convert_and_respect_annotation_metadata(
                    object_=composition_plan,
                    annotation=BodyComposeMusicWithADetailedResponseV1MusicDetailedPostCompositionPlan,
                    direction="write",
                ),
                "music_length_ms": music_length_ms,
                "model_id": model_id,
                "seed": seed,
                "force_instrumental": force_instrumental,
                "finetune_id": finetune_id,
                "respect_sections_durations": respect_sections_durations,
                "store_for_inpainting": store_for_inpainting,
                "with_timestamps": with_timestamps,
                "sign_with_c2pa": sign_with_c_2_pa,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        ) as _response:

            async def _stream() -> AsyncHttpResponse[typing.AsyncIterator[bytes]]:
                try:
                    if 200 <= _response.status_code < 300:
                        _chunk_size = request_options.get("chunk_size", 1024) if request_options is not None else 1024
                        return AsyncHttpResponse(
                            response=_response,
                            data=(_chunk async for _chunk in _response.aiter_bytes(chunk_size=_chunk_size)),
                        )
                    await _response.aread()
                    if _response.status_code == 422:
                        raise UnprocessableEntityError(
                            headers=dict(_response.headers),
                            body=typing.cast(
                                typing.Any,
                                construct_type(
                                    type_=typing.Any,  # type: ignore
                                    object_=_response.json(),
                                ),
                            ),
                        )
                    _response_json = _response.json()
                except JSONDecodeError:
                    raise ApiError(
                        status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
                    )
                except ValidationError as e:
                    raise ParsingError(
                        status_code=_response.status_code,
                        headers=dict(_response.headers),
                        body=_response.json(),
                        cause=e,
                    )
                raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

            yield await _stream()

    @contextlib.asynccontextmanager
    async def compose_detailed_stream(
        self,
        *,
        output_format: typing.Optional[MusicComposeDetailedStreamRequestOutputFormat] = None,
        prompt: typing.Optional[str] = OMIT,
        composition_plan: typing.Optional[
            BodyStreamComposedMusicWithADetailedResponseV1MusicDetailedStreamPostCompositionPlan
        ] = OMIT,
        music_length_ms: typing.Optional[int] = OMIT,
        model_id: typing.Optional[BodyStreamComposedMusicWithADetailedResponseV1MusicDetailedStreamPostModelId] = OMIT,
        seed: typing.Optional[int] = OMIT,
        force_instrumental: typing.Optional[bool] = OMIT,
        finetune_id: typing.Optional[str] = OMIT,
        store_for_inpainting: typing.Optional[bool] = OMIT,
        with_timestamps: typing.Optional[bool] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[str]]]:
        """
        Stream a song and its detailed metadata using Server-Sent Events (SSE).

        Parameters
        ----------
        output_format : typing.Optional[MusicComposeDetailedStreamRequestOutputFormat]
            Output format of the generated audio. Formatted as codec_sample_rate_bitrate. Use "auto" (the default) to let the API pick the best format for the selected model: mp3_44100_128 for v1 models and mp3_48000_192 for v2 models.

        prompt : typing.Optional[str]
            A simple text prompt to generate a song from. Cannot be used in conjunction with `composition_plan`.

        composition_plan : typing.Optional[BodyStreamComposedMusicWithADetailedResponseV1MusicDetailedStreamPostCompositionPlan]
            A detailed composition plan to guide music generation. Cannot be used in conjunction with `prompt`.

        music_length_ms : typing.Optional[int]
            The length of the song to generate in milliseconds. Used only in conjunction with `prompt`. Must be between 3000ms and 600000ms. Optional - if not provided, the model will choose a length based on the prompt.

        model_id : typing.Optional[BodyStreamComposedMusicWithADetailedResponseV1MusicDetailedStreamPostModelId]
            The model to use for the generation.

        seed : typing.Optional[int]
            Random seed to initialize the music generation process. Providing the same seed with the same parameters can help achieve more consistent results, but exact reproducibility is not guaranteed and outputs may change across system updates. Cannot be used in conjunction with prompt.

        force_instrumental : typing.Optional[bool]
            If true, guarantees that the generated song will be instrumental. If false, the song may or may not be instrumental depending on the `prompt`. Can only be used with `prompt`.

        finetune_id : typing.Optional[str]
            The ID of the finetune to use for the generation

        store_for_inpainting : typing.Optional[bool]
            Whether to store the generated song for inpainting.

        with_timestamps : typing.Optional[bool]
            Whether to return the timestamps of the words in the generated song.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Yields
        ------
        typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[str]]]
            Server-Sent Events for composition plan, song metadata, audio chunks with optional word timestamps, and completion.
        """
        async with self._client_wrapper.httpx_client.stream(
            "v1/music/detailed/stream",
            method="POST",
            params={
                "output_format": output_format,
            },
            json={
                "prompt": prompt,
                "composition_plan": convert_and_respect_annotation_metadata(
                    object_=composition_plan,
                    annotation=BodyStreamComposedMusicWithADetailedResponseV1MusicDetailedStreamPostCompositionPlan,
                    direction="write",
                ),
                "music_length_ms": music_length_ms,
                "model_id": model_id,
                "seed": seed,
                "force_instrumental": force_instrumental,
                "finetune_id": finetune_id,
                "store_for_inpainting": store_for_inpainting,
                "with_timestamps": with_timestamps,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        ) as _response:

            async def _stream() -> AsyncHttpResponse[typing.AsyncIterator[str]]:
                try:
                    if 200 <= _response.status_code < 300:

                        async def _iter():
                            async for _text in _response.aiter_lines():
                                try:
                                    if len(_text) == 0:
                                        continue
                                    yield typing.cast(
                                        str,
                                        construct_type(
                                            type_=str,  # type: ignore
                                            object_=json.loads(_text),
                                        ),
                                    )
                                except Exception:
                                    pass
                            return

                        return AsyncHttpResponse(response=_response, data=_iter())
                    await _response.aread()
                    if _response.status_code == 422:
                        raise UnprocessableEntityError(
                            headers=dict(_response.headers),
                            body=typing.cast(
                                typing.Any,
                                construct_type(
                                    type_=typing.Any,  # type: ignore
                                    object_=_response.json(),
                                ),
                            ),
                        )
                    _response_json = _response.json()
                except JSONDecodeError:
                    raise ApiError(
                        status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
                    )
                except ValidationError as e:
                    raise ParsingError(
                        status_code=_response.status_code,
                        headers=dict(_response.headers),
                        body=_response.json(),
                        cause=e,
                    )
                raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

            yield await _stream()

    @contextlib.asynccontextmanager
    async def stream(
        self,
        *,
        output_format: typing.Optional[MusicStreamRequestOutputFormat] = None,
        prompt: typing.Optional[str] = OMIT,
        composition_plan: typing.Optional[BodyStreamComposedMusicV1MusicStreamPostCompositionPlan] = OMIT,
        music_length_ms: typing.Optional[int] = OMIT,
        model_id: typing.Optional[BodyStreamComposedMusicV1MusicStreamPostModelId] = OMIT,
        seed: typing.Optional[int] = OMIT,
        force_instrumental: typing.Optional[bool] = OMIT,
        finetune_id: typing.Optional[str] = OMIT,
        store_for_inpainting: typing.Optional[bool] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]:
        """
        Stream a composed song from a prompt or a composition plan.

        Parameters
        ----------
        output_format : typing.Optional[MusicStreamRequestOutputFormat]
            Output format of the generated audio. Formatted as codec_sample_rate_bitrate. Use "auto" (the default) to let the API pick the best format for the selected model: mp3_44100_128 for v1 models and mp3_48000_192 for v2 models.

        prompt : typing.Optional[str]
            A simple text prompt to generate a song from. Cannot be used in conjunction with `composition_plan`.

        composition_plan : typing.Optional[BodyStreamComposedMusicV1MusicStreamPostCompositionPlan]
            A detailed composition plan to guide music generation. Cannot be used in conjunction with `prompt`.

        music_length_ms : typing.Optional[int]
            The length of the song to generate in milliseconds. Used only in conjunction with `prompt`. Must be between 3000ms and 600000ms. Optional - if not provided, the model will choose a length based on the prompt.

        model_id : typing.Optional[BodyStreamComposedMusicV1MusicStreamPostModelId]
            The model to use for the generation.

        seed : typing.Optional[int]
            Random seed to initialize the music generation process. Providing the same seed with the same parameters can help achieve more consistent results, but exact reproducibility is not guaranteed and outputs may change across system updates. Cannot be used in conjunction with prompt.

        force_instrumental : typing.Optional[bool]
            If true, guarantees that the generated song will be instrumental. If false, the song may or may not be instrumental depending on the `prompt`. Can only be used with `prompt`.

        finetune_id : typing.Optional[str]
            The ID of the finetune to use for the generation

        store_for_inpainting : typing.Optional[bool]
            Whether to store the generated song for inpainting.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.

        Returns
        -------
        typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]
            Streaming audio data in the format specified
        """
        async with self._client_wrapper.httpx_client.stream(
            "v1/music/stream",
            method="POST",
            params={
                "output_format": output_format,
            },
            json={
                "prompt": prompt,
                "composition_plan": convert_and_respect_annotation_metadata(
                    object_=composition_plan,
                    annotation=BodyStreamComposedMusicV1MusicStreamPostCompositionPlan,
                    direction="write",
                ),
                "music_length_ms": music_length_ms,
                "model_id": model_id,
                "seed": seed,
                "force_instrumental": force_instrumental,
                "finetune_id": finetune_id,
                "store_for_inpainting": store_for_inpainting,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        ) as _response:

            async def _stream() -> AsyncHttpResponse[typing.AsyncIterator[bytes]]:
                try:
                    if 200 <= _response.status_code < 300:
                        _chunk_size = request_options.get("chunk_size", 1024) if request_options is not None else 1024
                        return AsyncHttpResponse(
                            response=_response,
                            data=(_chunk async for _chunk in _response.aiter_bytes(chunk_size=_chunk_size)),
                        )
                    await _response.aread()
                    if _response.status_code == 422:
                        raise UnprocessableEntityError(
                            headers=dict(_response.headers),
                            body=typing.cast(
                                typing.Any,
                                construct_type(
                                    type_=typing.Any,  # type: ignore
                                    object_=_response.json(),
                                ),
                            ),
                        )
                    _response_json = _response.json()
                except JSONDecodeError:
                    raise ApiError(
                        status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
                    )
                except ValidationError as e:
                    raise ParsingError(
                        status_code=_response.status_code,
                        headers=dict(_response.headers),
                        body=_response.json(),
                        cause=e,
                    )
                raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

            yield await _stream()

    async def upload(
        self,
        *,
        file: core.File,
        extract_composition_plan: typing.Optional[str] = OMIT,
        with_timestamps: typing.Optional[bool] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[MusicUploadResponse]:
        """
        Upload a music file to be later used for inpainting. Price for uploading is the same as the one for song generation. All uploaded content gets inspected for copyright infringement. If copyrighted content is detected, half of the request cost is still charged.

        Parameters
        ----------
        file : core.File
            See core.File for more documentation

        extract_composition_plan : typing.Optional[str]
            Whether to generate and return the composition plan for the uploaded song. Pass a model id (`music_v1` or `music_v2`) to control which composition plan format is returned. Passing `true`/`false` is deprecated; `true` defaults to the `music_v1` plan format. Enabling this will increase the latency.

        with_timestamps : typing.Optional[bool]
            Whether to transcribe the uploaded song and return word-level timestamps. If True, the response will include words_timestamps but will increase the latency.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[MusicUploadResponse]
            Successfully uploaded music file with optional composition plan
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v1/music/upload",
            method="POST",
            data={
                "extract_composition_plan": extract_composition_plan,
                "with_timestamps": with_timestamps,
            },
            files={
                "file": file,
            },
            request_options=request_options,
            omit=OMIT,
            force_multipart=True,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    MusicUploadResponse,
                    construct_type(
                        type_=MusicUploadResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        construct_type(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        except ValidationError as e:
            raise ParsingError(
                status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
            )
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    @contextlib.asynccontextmanager
    async def separate_stems(
        self,
        *,
        file: core.File,
        output_format: typing.Optional[AllowedOutputFormats] = None,
        stem_variation_id: typing.Optional[MusicSeparateStemsRequestStemVariationId] = OMIT,
        sign_with_c_2_pa: typing.Optional[bool] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]:
        """
        Separate an audio file into individual stems. This endpoint might have high latency, depending on the length of the audio file.

        Parameters
        ----------
        file : core.File
            See core.File for more documentation

        output_format : typing.Optional[AllowedOutputFormats]
            Output format of the generated audio. Formatted as codec_sample_rate_bitrate. So an mp3 with 22.05kHz sample rate at 32kbs is represented as mp3_22050_32. MP3 with 192kbps bitrate requires you to be subscribed to Creator tier or above. PCM with 44.1kHz sample rate requires you to be subscribed to Pro tier or above. Note that the μ-law format (sometimes written mu-law, often approximated as u-law) is commonly used for Twilio audio inputs.

        stem_variation_id : typing.Optional[MusicSeparateStemsRequestStemVariationId]
            The id of the stem variation to use.

        sign_with_c_2_pa : typing.Optional[bool]
            Whether to sign the generated song with C2PA. Applicable only for mp3 files.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.

        Returns
        -------
        typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]
            ZIP archive containing separated audio stems. Each stem is provided as a separate audio file in the requested output format.
        """
        async with self._client_wrapper.httpx_client.stream(
            "v1/music/stem-separation",
            method="POST",
            params={
                "output_format": output_format,
            },
            data={
                "stem_variation_id": stem_variation_id,
                "sign_with_c2pa": sign_with_c_2_pa,
            },
            files={
                "file": file,
            },
            request_options=request_options,
            omit=OMIT,
            force_multipart=True,
        ) as _response:

            async def _stream() -> AsyncHttpResponse[typing.AsyncIterator[bytes]]:
                try:
                    if 200 <= _response.status_code < 300:
                        _chunk_size = request_options.get("chunk_size", 1024) if request_options is not None else 1024
                        return AsyncHttpResponse(
                            response=_response,
                            data=(_chunk async for _chunk in _response.aiter_bytes(chunk_size=_chunk_size)),
                        )
                    await _response.aread()
                    if _response.status_code == 422:
                        raise UnprocessableEntityError(
                            headers=dict(_response.headers),
                            body=typing.cast(
                                typing.Any,
                                construct_type(
                                    type_=typing.Any,  # type: ignore
                                    object_=_response.json(),
                                ),
                            ),
                        )
                    _response_json = _response.json()
                except JSONDecodeError:
                    raise ApiError(
                        status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
                    )
                except ValidationError as e:
                    raise ParsingError(
                        status_code=_response.status_code,
                        headers=dict(_response.headers),
                        body=_response.json(),
                        cause=e,
                    )
                raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

            yield await _stream()
