""" Modal Runner for model evaluation. Launches EvalConductorStub to send requests to an engine worker, then on generation completion launches an EvalEncoderStub to run (optional) source separation and ditto embedding calculation. """ import json import logging import random import time import os import re from datetime import datetime import tempfile from uuid import uuid4 import modal from enum import Enum from suno_utils.utils.s3 import check_s3_file_exists from suno_utils.worker.settings import s3_client from suno_utils.worker.modal_base import MOUNT_PATH from suno_utils.worker.feature_eval import ( Prompt, load_prompt_arr, FeatureEval, InlineTagEval, GenreEval, TEST_SETS, DITTO_S3_PATH, ) from suno_utils.gpt.chirp_v2_5 import _get_model_if_needed from suno_utils.worker.loader import S3Loader from suno_utils.worker.tracing import distributed_trace from suno_utils.worker.schema import QueueItem, HistoryPrompt from suno_utils.tasks import ss_vad from suno_utils.tasks.ditto_v2 import preload_models as preload_ditto_models from suno_utils.worker.modal_base import get_modal_base_image_with_flash_attention import suno_utils.gpt.chirp_v2_5 as chirp from suno_utils.tasks.hoot import load_model_list as hoot_load_model_list class EvalTestType(Enum): COVER_AND_ARTIST = "cover_artist" BASE_GENERATION = "base_gen" INLINE_TAG_EVAL = "inline_tag_eval" VOCALIST_GENDER_EVAL = "VOCALIST_GENDER_EVAL" GENRE_EVAL = "genre_eval" ############## CHANGE THESE ############## TEST_TYPE = EvalTestType.BASE_GENERATION MODEL_NAME = "chirp-auk-eval" # model to test, must have an engine modal app running EXTRA_TAGS = "" # additional experiment labels, like config changes if TEST_TYPE == EvalTestType.COVER_AND_ARTIST: EVAL_NAME = "all_genres" # hard or all_genres GENS_PER_SOURCE = 5 FEATURE_EVAL_DATA_PATH = TEST_SETS[EVAL_NAME] elif TEST_TYPE == EvalTestType.BASE_GENERATION: EVAL_NAME = "" # leave blank for this case GENS_PER_SOURCE = 20 FEATURE_EVAL_DATA_PATH = "../../task_eval/labelbox/labelbox_sources.json" elif TEST_TYPE == EvalTestType.INLINE_TAG_EVAL: EVAL_NAME = "" # leave blank for this case GENS_PER_SOURCE = 20 FEATURE_EVAL_DATA_PATH = "../../task_eval/genres_instruments.json" LYRICS_PATH = "../../task_eval/lyrics_with_inline_tags.json" elif TEST_TYPE == EvalTestType.VOCALIST_GENDER_EVAL: EVAL_NAME = "" # leave blank for this case GENS_PER_SOURCE = 5 FEATURE_EVAL_DATA_PATH = "../../task_eval/genre_descriptions.json" LYRICS_PATH = "../../task_eval/lyrics_with_inline_tags.json" elif TEST_TYPE == EvalTestType.GENRE_EVAL: EVAL_NAME = "" # leave blank for this case GENS_PER_SOURCE = 4 FEATURE_EVAL_DATA_PATH = "../../task_eval/rate_your_music_filtered.json" INFER_CONFIG = None # if you want to force inference parameter changes ########################################## DEPLOYMENT_TYPE = "dev" EVAL_CONDUCTOR_DDOG_SERVICE = "eval-conductor-worker" EVAL_ENCODER_DDOG_SERVICE = "eval-encoder-worker" logger = logging.getLogger(__name__) logging.basicConfig() logger.setLevel(logging.INFO) OUTPUT_FOLDER = "../../task_eval/modal_runs" MOUNT_PATH = "/suno/models" APP_NAME = f"eval-orchestrator-{DEPLOYMENT_TYPE}" assert APP_NAME.endswith(DEPLOYMENT_TYPE) if DEPLOYMENT_TYPE == "dev": from suno_utils.worker.modal_model_configuration import MODEL_DEV_FNS as MODAL_FNS elif DEPLOYMENT_TYPE == "prod": from suno_utils.worker.modal_model_configuration import MODEL_PROD_FNS as MODAL_FNS else: err_msg = f"DEPLOYMENT_TYPE {DEPLOYMENT_TYPE} unrecognized" raise AssertionError(err_msg) # Use random seed based on time random_seed = int((time.time() * 1000) % 100000000) print("Random seed set to:", random_seed) random.seed(random_seed) DD_SAMPLE_RATE = "0.0001" # modal traffic lookup MODAL_TRAFFIC_WORKER = "engine-chirpv2_engine_13b_special_8" # looks up the H100 traffic _ = modal.Cls.from_name( f"{MODAL_TRAFFIC_WORKER}_" + f"{'dev' if DEPLOYMENT_TYPE == 'dev' else 'prod'}_240s_H100", "ChirpV2Stub", )().generate class LoadEvalModelsWorker(S3Loader): """Load models for source separation and ditto""" @staticmethod def download_models_ditto(dir_path: str = MOUNT_PATH) -> None: _ = preload_ditto_models(model_filepath=DITTO_S3_PATH) ss_vad_config_path = _get_model_if_needed(ss_vad.YAML_PATH, cache_dir=dir_path) ss_vad_model_path = _get_model_if_needed(ss_vad.MODEL_PATH, cache_dir=dir_path) ss_vad.preload_models( checkpoint_filepath=ss_vad_model_path, config_path=ss_vad_config_path, ) @staticmethod def download_models_tagging(dir_path: str = MOUNT_PATH) -> None: hoot_ckpt_path = chirp._get_model_if_needed(chirp.HOOT_CKPT_PATH, cache_dir=dir_path) hoot_tokenizer_path = chirp._get_model_if_needed(chirp.HOOT_TOKENIZER_PATH, cache_dir=dir_path) # load hoot on cuda as well chirp.preload_hoot_models( hoot_ckpt_path, hoot_tokenizer_path, ) aws_secret = modal.Secret.from_name("studio-aws") sample_rule = json.dumps([{"sample_rate": DD_SAMPLE_RATE}]) SECRETS = [ aws_secret, modal.Secret.from_dict( { "DD_SITE": "datadoghq.com", "DD_ENV": DEPLOYMENT_TYPE, "DD_SERVICE": "chatgpt-worker", "DD_LOGS_ENABLED": "true", "DD_TRACE_ENABLED": "true", "DD_TRACE_SAMPLING_RULES": sample_rule, }, ), modal.Secret.from_name("datadog-metrics"), modal.Secret.from_name("api-callback-token"), ] def download_model_wrapper_a() -> None: if TEST_TYPE == EvalTestType.INLINE_TAG_EVAL: LoadEvalModelsWorker.download_models_tagging() else: LoadEvalModelsWorker.download_models_ditto() base_image = get_modal_base_image_with_flash_attention().add_local_python_source( "suno_utils", copy=False ) image = base_image.run_function(download_model_wrapper_a, secrets=SECRETS) app = modal.App(APP_NAME, image=image) @app.cls( cpu=1.0, secrets=SECRETS, min_containers=5, timeout=600, retries=modal.Retries( max_retries=2, backoff_coefficient=2.0, initial_delay=5.0, ), scaledown_window=400, ) @modal.concurrent(max_inputs=50) class EvalConductorStub(S3Loader): """Expose EvalConductorStub worker as a modal app Responsible for distributing queue items to the appropriate chirp worker based on the model name. When a generation completes, it triggers an EvalEncoderStub to save the evaluation results. Unlike the ConductorStub, this worker does not apply any model or parameter A/B test experiments. """ def __init__(self): # looks up the H100 traffic self.modal_f_chirp_main_H100 = modal.Cls.lookup( f"{MODAL_TRAFFIC_WORKER}_" + f"{'dev' if DEPLOYMENT_TYPE == 'dev' else 'prod'}_240s_H100", "ChirpV2Stub", )().generate self.modal_f_history_loader = modal.Cls.lookup( f"history_encoder-{'dev' if DEPLOYMENT_TYPE == 'dev' else 'prod'}", "HistoryEncoderStub", )().encode_history # key is a tuple of worker name and function name self.modal_chirp_worker_lookup_cache = {} def _get_engine_worker(self, item: QueueItem) -> modal.Function | None: """Get appropriate engine worker based on model name""" if item.model_name is None: raise ValueError("Model name is required to get the GPT worker.") stub_name, stub_function = MODAL_FNS[item.model_name] stub_name_worker = f"{stub_name}_H100" # we don't need to lookup it again if we have already done it if (stub_name_worker, stub_function) in self.modal_chirp_worker_lookup_cache: return self.modal_chirp_worker_lookup_cache[(stub_name_worker, stub_function)] else: try: cls_name = stub_function.split(".")[0] func_name = stub_function.split(".")[1] chirp_f = getattr(modal.Cls.lookup(stub_name_worker, cls_name)(), func_name) self.modal_chirp_worker_lookup_cache[(stub_name_worker, stub_function)] = chirp_f return chirp_f except Exception as e: print( f"Error getting chirp worker for {item.model_name}: {e}. stub_name, stub_function: {stub_name_worker}, {stub_function}" ) def _validate_input_queue_item(self, item: QueueItem) -> bool: """Validate the input queue item to check if the request if valid.""" if ( item.metadata.get("task") in ["infill", "infill_intro", "infill_outro"] ) and not item.is_infill: print(f"WTF is happening with {item.id} -- task is infill but not infill arguments.") return False # add task validations checks -- we should not be passing in clips if task isn't specified if not item.is_cover_condition and item.metadata.get("cover_clip_id", None) is not None: print(f"WTF is happening with {item.id} -- not cover but without cover id passed in.") return False if not item.is_artist_condition and item.metadata.get("artist_clip_id", None) is not None: print(f"WTF is happening with {item.id} -- not artist but without artist id passed in.") return False if ( item.metadata.get("task") == "extend" or item.is_cover_extend or item.is_cover_infill or item.is_artist_extend or item.is_artist_infill ) and item.prompt_audio is None: print(f"WTF is happening with {item.id} -- task is extend but prompt audio id passed in.") return False if item.metadata.get("task") == "upsample" and not item.is_upsample: print(f"WTF is happening with {item.id} -- task is upsample but without upsample arguments.") return False return True @modal.method() @distributed_trace("redirect_and_generate", EVAL_CONDUCTOR_DDOG_SERVICE, env_name=DEPLOYMENT_TYPE) def redirect_and_generate( self, queue_item_json: str, history_prompt: HistoryPrompt | None = None, time_prefix: str = "", task: str | None = None, ) -> None: """This worker will distribute the queue item to the appropriate model worker, then kick off ditto evaluation once the generation is complete. If it is a continue and needs to fetch the history, it will go to the history loader. History loader will pass the item back into redirect_and_generate. Otherwise, it will go through augmentation and to the GPT worker directly. Args: queue_item_json: the queue item in json format. Clip-level only. history_prompt: the history prompt in HistoryPrompt. time_prefix: timestamp label for the evaluation task: ditto task to trigger for evaluation """ item = QueueItem(**json.loads(queue_item_json)) logger.info(f" {item}") # validate the queue item -- should meet expected conditions if not self._validate_input_queue_item(item): raise ValueError("Invalid generation request.") if ( item.prompt_audio or item.is_cover_condition or item.is_artist_condition or item.is_infill ) and history_prompt is None: # This is a continue job, without history, we need to fetch the history first _ = self.modal_f_history_loader.spawn(item.json()) logger.info(f"Request {item.id}: Spawned modal job into history loader") # nothing to be done for here, job isn't in queue yet, except upsample if (item.is_upsample or item.is_stem) and item.ids: for clip_id in item.ids: curr_item = item.model_copy(deep=True, update={"id": clip_id, "ids": None}) # we need to copy the image -- note that this is independent of the job queue self.copy_and_upload_image(curr_item) return if item.model_name not in MODAL_FNS: # this is a bad model name, we should notify at the request level raise ValueError(f"Invalid model name {item.model_name}") # start the audio runner, do a look up first engine_worker = self._get_engine_worker(item) # directly goes to the GPT worker, with empty HistoryPrompt _ = engine_worker.spawn(item.json(), history_prompt) logger.info(f"Request {item.id}: Spawned modal job {engine_worker} item id {item.id}") file_found = False logger.info(f"Waiting for S3 file for {item.id} to be created...") while not file_found: if check_s3_file_exists(f"s3://suno-data-uploads/studio/uploads/{item.id}.mp3"): file_found = True if task is not None: logger.info(f"Found S3 file for {item.id}, launching evaluation.") if task == "tags_inline": EvalInlineTagStub.process.spawn( json.dumps(dict(id=item.id, prompt_text=item.prompt_text, metadata={})), time_prefix=time_prefix, ) elif task == "gender_inline": EvalInlineTagStub.process.spawn( json.dumps(dict(id=item.id, prompt_text=item.prompt_text, metadata={})), time_prefix=time_prefix, ) elif task == "genre_encode": EvalEncodeGenreStub.process.spawn( json.dumps(dict(id=item.id, metadata={})), time_prefix=time_prefix ) else: EvalEncodeStub.process.spawn( json.dumps(dict(id=item.id, metadata={})), time_prefix=time_prefix, task=task ) else: # keep waiting for generation to finish time.sleep(5) @app.cls( cpu=12, gpu="a100", secrets=SECRETS, timeout=600, scaledown_window=400, memory=32000, retries=modal.Retries( max_retries=1, backoff_coefficient=2.0, initial_delay=5.0, ), min_containers=1, max_containers=5, ) @modal.concurrent(max_inputs=10) class EvalEncodeStub: def __init__(self): import torch torch.set_num_threads(8) self.feature_eval = FeatureEval( model_name=MODEL_NAME + "_" + EVAL_NAME + EXTRA_TAGS, ) @modal.method() @distributed_trace("eval_process", EVAL_ENCODER_DDOG_SERVICE, env_name=DEPLOYMENT_TYPE) def process( self, queue_item_json: str, time_prefix="", task="self_sim", ): item = QueueItem(**json.loads(queue_item_json)) s3_id = item.id with tempfile.NamedTemporaryFile(suffix=".mp3") as temp_file: s3_client.download_file("suno-data-uploads", f"studio/uploads/{s3_id}.mp3", temp_file.name) self.feature_eval.run_feature_eval(s3_id, temp_file.name, task, time_prefix) @app.cls( cpu=12, gpu="a100", secrets=SECRETS, timeout=600, scaledown_window=400, memory=32000, retries=modal.Retries( max_retries=1, backoff_coefficient=2.0, initial_delay=5.0, ), min_containers=1, max_containers=5, ) @modal.concurrent(max_inputs=10) class EvalEncodeGenreStub: def __init__(self): import torch torch.set_num_threads(8) _ = preload_ditto_models(model_filepath=DITTO_S3_PATH) self.feature_eval = GenreEval( model_name=MODEL_NAME + "_" + EVAL_NAME + EXTRA_TAGS, ) @modal.method() @distributed_trace("eval_process", EVAL_ENCODER_DDOG_SERVICE, env_name=DEPLOYMENT_TYPE) def process( self, queue_item_json: str, time_prefix="", ): item = QueueItem(**json.loads(queue_item_json)) s3_id = item.id with tempfile.NamedTemporaryFile(suffix=".mp3") as temp_file: self.feature_eval.run_feature_eval(s3_id, time_prefix) @app.cls( cpu=12, gpu="a100", secrets=SECRETS, timeout=600, scaledown_window=400, memory=32000, retries=modal.Retries( max_retries=1, backoff_coefficient=2.0, initial_delay=5.0, ), min_containers=1, max_containers=5, ) @modal.concurrent(max_inputs=10) class EvalInlineTagStub: def __init__(self): import torch torch.set_num_threads(8) hoot_ckpt_path = chirp._get_model_if_needed(chirp.HOOT_CKPT_PATH, cache_dir=MOUNT_PATH) hoot_tokenizer_path = chirp._get_model_if_needed(chirp.HOOT_TOKENIZER_PATH, cache_dir=MOUNT_PATH) model_dict = hoot_load_model_list( "s3://suno-data/checkpoints/hoot_v3/hoot_ckpt.pt", "s3://suno-data/checkpoints/hoot_v3/tokenizer.model", )[0] tokenizer = model_dict["tokenizer"] self.tag_eval = InlineTagEval( model_name=MODEL_NAME + "_" + EVAL_NAME + EXTRA_TAGS, tokenizer=tokenizer, threshold=0.6, tag_type="instrument" if TEST_TYPE == EvalTestType.INLINE_TAG_EVAL else "gender", ) @modal.method() @distributed_trace("eval_process", EVAL_ENCODER_DDOG_SERVICE, env_name=DEPLOYMENT_TYPE) def process( self, queue_item_json: str, time_prefix="", task="", ): item = QueueItem(**json.loads(queue_item_json)) s3_id = item.id lyrics = item.prompt_text self.tag_eval.run_feature_eval(s3_id, lyrics, None, time_prefix) def test_cover_test(source_data, num_gens_per_source=5, time_prefix="", task="self_sim"): cover_mappings = {} conductor = EvalConductorStub() for cover_source in source_data: inputs = [] prompt = Prompt(**cover_source) prompt.cover_arr = load_prompt_arr(prompt.s3_id) cover_mappings[prompt.s3_id] = [] for _ in range(num_gens_per_source): uid = str(uuid4()) cover_mappings[prompt.s3_id].append(uid) inputs.append( json.dumps( dict( id=uid, prompt_text=prompt.lyrics, model_name=MODEL_NAME, metadata={ "lang": "English", "tags": prompt.test_tags, "task": "cover", "type": "gen", "is_bot": False, "artist_end_s": 120, "feature_flags": 3, "cover_clip_id": prompt.s3_id, "edited_clip_id": prompt.s3_id, "forced_infer_config": INFER_CONFIG, }, ), ), ) history = HistoryPrompt(cover_audio=prompt.cover_arr) for input in inputs: conductor.redirect_and_generate.spawn( input, history, time_prefix=time_prefix, task="self_sim" ) time.sleep(2) source_input = json.dumps(dict(id=prompt.s3_id, metadata={})) EvalEncodeStub.process.spawn(source_input, time_prefix=time_prefix, task=task) with open(os.path.join(OUTPUT_FOLDER, f"cover_mappings_{time_prefix}.json"), "w") as f: json.dump(cover_mappings, f, indent=4) def test_artist_test(source_data, num_gens_per_source=5, time_prefix="", task="artist_vox_sim"): artist_mappings = {} conductor = EvalConductorStub() for artist_source in source_data: inputs = [] prompt = Prompt(**artist_source) prompt.artist_arr = load_prompt_arr(prompt.s3_id) artist_mappings[prompt.s3_id] = [] for _ in range(num_gens_per_source): uid = str(uuid4()) artist_mappings[prompt.s3_id].append(uid) inputs.append( json.dumps( dict( id=uid, prompt_text=prompt.test_lyrics, model_name=MODEL_NAME, metadata={ "lang": "English", "tags": prompt.tags, "task": "artist_consistency", "type": "gen", "is_bot": False, "artist_end_s": 120, "feature_flags": 3, "artist_clip_id": prompt.s3_id, "edited_clip_id": prompt.s3_id, "forced_infer_config": INFER_CONFIG, }, ), ), ) history = HistoryPrompt(artist_audio=prompt.artist_arr) for input in inputs: conductor.redirect_and_generate.spawn(input, history, time_prefix=time_prefix, task=task) time.sleep(2) source_input = json.dumps(dict(id=prompt.s3_id, metadata={})) EvalEncodeStub.process.spawn(source_input, time_prefix=time_prefix, task=task) with open(os.path.join(OUTPUT_FOLDER, f"artist_mappings_{time_prefix}.json"), "w") as f: json.dump(artist_mappings, f, indent=4) def test_generation_test(source_data, num_gens_per_source=1, time_prefix=""): genre_mappings = {} conductor = EvalConductorStub() for genre in source_data: genre_mappings[genre] = [] inputs = [] for data in source_data[genre]: for _ in range(num_gens_per_source): style = data["tags"] lyrics = data["lyrics"] uid = str(uuid4()) genre_mappings[genre].append(dict(s3_id=uid, tags=style, lyrics=lyrics)) inputs.append( json.dumps( dict( id=uid, prompt_text=lyrics, model_name=MODEL_NAME, metadata={ "lang": "English", "tags": style, "type": "gen", "is_bot": False, "forced_infer_config": INFER_CONFIG, }, ), ), ) history = None for input in inputs: conductor.redirect_and_generate.spawn( input, history, time_prefix=time_prefix, task="self_sim" ) time.sleep(2) with open( os.path.join(OUTPUT_FOLDER, f"genre_mappings_{MODEL_NAME}{EXTRA_TAGS}_{time_prefix}.json"), "w" ) as f: json.dump(genre_mappings, f, indent=4) def replace_placeholders(template_string, replacements): # Find all placeholders in the format {something} placeholders = re.findall(r"\{[^}]*\}", template_string) # Replace each placeholder with the corresponding element from the replacements list result = template_string for i, placeholder in enumerate(placeholders): if i < len(replacements): result = result.replace(placeholder, replacements[i]) return result def test_inline_tag_test(source_data, eval_lyrics, num_gens_per_source=1, time_prefix=""): genre_mappings = {} conductor = EvalConductorStub() section_names = ["Instrumental Intro", "Verse 1", "Chorus", "Verse 2"] for genre, genre_data in source_data.items(): genre_mappings[genre] = [] inputs = [] instrument_groups = genre_data["instruments"] descriptions = genre_data["descriptions"] genre_lyrics = eval_lyrics[genre] for _ in range(num_gens_per_source): # Create a list to hold all tuples for this iteration iteration_tuples = [] # First, select one random description for each instrument # to use consistently in this iteration instrument_to_description = {} for instrument, desc_options in descriptions.items(): instrument_to_description[instrument] = random.choice(desc_options) # Then generate a prompt for each instrument group using the consistent descriptions for idx, instrument_group in enumerate(instrument_groups): description_list = [] # Add the consistent description for each instrument in the group for instrument in instrument_group: if instrument in instrument_to_description: description_list.append(instrument_to_description[instrument]) # Create the prompt string prompt = f"{', '.join(description_list)}" # Add the tuple to the iteration list iteration_tuples.append([section_names[idx], instrument_group, prompt]) # now we built the prompt style = genre lyrics = replace_placeholders(random.choice(genre_lyrics), [x[2] for x in iteration_tuples]) uid = str(uuid4()) genre_mappings[genre].append( dict(s3_id=uid, tags=style, lyrics=lyrics, mappings=iteration_tuples) ) inputs.append( json.dumps( dict( id=uid, prompt_text=lyrics, model_name=f"{MODEL_NAME}", metadata={ "lang": "English", "tags": style, "type": "gen", "is_bot": False, "forced_infer_config": INFER_CONFIG, }, ), ), ) history = None for input in inputs: conductor.redirect_and_generate.spawn( input, history, time_prefix=time_prefix, task="tags_inline" ) time.sleep(2) with open( os.path.join(OUTPUT_FOLDER, f"tag_mappings_{time_prefix}.json"), "w", ) as f: json.dump(genre_mappings, f, indent=4) def test_gender_test(source_data, eval_lyrics, num_gens_per_source=1, time_prefix=""): genre_mappings = {} conductor = EvalConductorStub() male_verse = ["instrumentals", "male singer", "female singer", "male singer"] female_verse = ["instrumentals", "female singer", "male singer", "female singer"] for genre, genre_data in source_data.items(): genre_mappings[genre] = [] inputs = [] for style_description in genre_data: genre_lyrics = eval_lyrics[genre] for _ in range(num_gens_per_source): lyrics = random.choice(genre_lyrics) for replacements in [male_verse, female_verse]: final_lyrics = replace_placeholders(lyrics, replacements) uid = str(uuid4()) genre_mappings[genre].append( dict( s3_id=uid, tags=style_description, lyrics=final_lyrics, mappings=replacements ) ) inputs.append( json.dumps( dict( id=uid, prompt_text=final_lyrics, model_name=f"{MODEL_NAME}", metadata={ "lang": "English", "tags": "duet, " + style_description, "type": "gen", "is_bot": False, "forced_infer_config": INFER_CONFIG, }, ), ), ) history = None for input in inputs: conductor.redirect_and_generate.spawn( input, history, time_prefix=time_prefix, task="gender_inline" ) time.sleep(2) with open( os.path.join(OUTPUT_FOLDER, f"vocalist_gender_mappings_{time_prefix}.json"), "w", ) as f: json.dump(genre_mappings, f, indent=4) def test_genre_test(source_data, num_gens_per_source=1, time_prefix=""): genre_mappings = {} conductor = EvalConductorStub() for genre in source_data: genre_name = genre["name"] genre_mappings[genre_name] = [] inputs = [] for _ in range(num_gens_per_source): style = genre_name parent_style = genre["parent_genre"] uid = str(uuid4()) genre_mappings[genre_name].append(dict(s3_id=uid, tags=style, parent_style=parent_style)) inputs.append( json.dumps( dict( id=uid, prompt_text="", model_name=MODEL_NAME, metadata={ "lang": "English", "tags": style, "type": "gen", "is_bot": False, "forced_infer_config": INFER_CONFIG, }, ), ), ) history = None for input in inputs: conductor.redirect_and_generate.spawn( input, history, time_prefix=time_prefix, task="genre_encode" ) time.sleep(2) with open( os.path.join(OUTPUT_FOLDER, f"subgenre_mappings_{time_prefix}.json"), "w", ) as f: json.dump(genre_mappings, f, indent=4) @app.local_entrypoint() def main(): # Get current timestamp as readable string time_label = datetime.now().strftime("%Y_%m_%d-%H_%M_%S") if TEST_TYPE == EvalTestType.BASE_GENERATION: with open(FEATURE_EVAL_DATA_PATH) as f: eval_data = json.load(f) test_generation_test(eval_data, num_gens_per_source=GENS_PER_SOURCE, time_prefix=time_label) elif TEST_TYPE == EvalTestType.COVER_AND_ARTIST: # load evaluation sources with open(FEATURE_EVAL_DATA_PATH) as f: eval_data = json.load(f) if not os.path.exists(OUTPUT_FOLDER): os.makedirs(OUTPUT_FOLDER) cover_data = eval_data["cover"] artist_data = eval_data["artist"] test_cover_test(cover_data, num_gens_per_source=GENS_PER_SOURCE, time_prefix=time_label) test_artist_test(artist_data, num_gens_per_source=GENS_PER_SOURCE, time_prefix=time_label) elif TEST_TYPE == EvalTestType.INLINE_TAG_EVAL: with open(FEATURE_EVAL_DATA_PATH) as f: eval_data = json.load(f) with open(LYRICS_PATH) as f: eval_lyrics = json.load(f) if not os.path.exists(OUTPUT_FOLDER): os.makedirs(OUTPUT_FOLDER) test_inline_tag_test( eval_data, eval_lyrics, num_gens_per_source=GENS_PER_SOURCE, time_prefix=time_label ) elif TEST_TYPE == EvalTestType.VOCALIST_GENDER_EVAL: with open(FEATURE_EVAL_DATA_PATH) as f: eval_data = json.load(f) with open(LYRICS_PATH) as f: eval_lyrics = json.load(f) if not os.path.exists(OUTPUT_FOLDER): os.makedirs(OUTPUT_FOLDER) test_gender_test( eval_data, eval_lyrics, num_gens_per_source=GENS_PER_SOURCE, time_prefix=time_label ) elif TEST_TYPE == EvalTestType.GENRE_EVAL: with open(FEATURE_EVAL_DATA_PATH) as f: eval_data = json.load(f) if not os.path.exists(OUTPUT_FOLDER): os.makedirs(OUTPUT_FOLDER) test_genre_test(eval_data, num_gens_per_source=GENS_PER_SOURCE, time_prefix=time_label) else: raise ValueError(f"Test type {TEST_TYPE} not recognized") time.sleep(3600 * 2)