```python:inference/core/workflows/core_steps/sinks/roboflow/dataset_upload/v1.py
from __future__ import annotations

from concurrent.futures import ThreadPoolExecutor
from functools import partial
from typing import List, Literal, Optional, Tuple, Union

import supervision as sv
from fastapi import BackgroundTasks
from inference.core.cache.base import BaseCache
from inference.core.workflows.execution_engine.entities.base import (
    Batch, WorkflowImageData)
from inference.core.workflows.prototypes.block import (BlockResult,
                                                       WorkflowBlock)

WORKSPACE_NAME_CACHE_EXPIRE = 900  # 15 min
TIMESTAMP_FORMAT = "%Y_%m_%d"
DUPLICATED_STATUS = "Duplicated image"
BatchCreationFrequency = Literal["never", "daily", "weekly", "monthly"]


class RoboflowDatasetUploadBlockV1(WorkflowBlock):

    def __init__(
        self,
        cache: BaseCache,
        api_key: Optional[str],
        background_tasks: Optional[BackgroundTasks],
        thread_pool_executor: Optional[ThreadPoolExecutor],
    ):
        self._cache = cache
        self._api_key = api_key
        self._background_tasks = background_tasks
        self._thread_pool_executor = thread_pool_executor

    def run(
        self,
        images: Batch[WorkflowImageData],
        predictions: Optional[Batch[Union[sv.Detections, dict]]],
        target_project: str,
        usage_quota_name: str,
        minutely_usage_limit: int,
        persist_predictions: bool,
        hourly_usage_limit: int,
        daily_usage_limit: int,
        max_image_size: Tuple[int, int],
        compression_level: int,
        registration_tags: List[str],
        disable_sink: bool,
        fire_and_forget: bool,
        labeling_batch_prefix: str,
        labeling_batches_recreation_frequency: BatchCreationFrequency,
        image_name: Optional[Batch[Optional[str]]] = None,
    ) -> BlockResult:
        if self._api_key is None:
            raise ValueError(
                "RoboflowDataCollector block cannot run without Roboflow API key. "
                "If you do not know how to get API key - visit "
                "https://docs.roboflow.com/api-reference/authentication#retrieve-an-api-key to learn how to "
                "retrieve one."
            )
        if disable_sink:
            return [
                {
                    "error_status": False,
                    "message": "Sink was disabled by parameter `disable_sink`",
                }
                for _ in range(len(images))
            ]
        predictions = [None] * len(images) if predictions is None else predictions
        image_names = [None] * len(images) if image_name is None else image_name

        # If fire-and-forget and we have a background mechanism, schedule a single pooled/background job
        if fire_and_forget and (self._background_tasks or self._thread_pool_executor):
            # Worker that will process all registrations sequentially in background
            def _bulk_register(
                imgs: Batch[WorkflowImageData],
                preds: Batch[Optional[Union[sv.Detections, dict]]],
                names: Batch[Optional[str]],
                tgt_project: str,
                usage_quota: str,
                persist_preds: bool,
                minutely_limit: int,
                hourly_limit: int,
                daily_limit: int,
                max_size: Tuple[int, int],
                compression_lvl: int,
                tags: List[str],
                labeling_prefix: str,
                recreation_freq: BatchCreationFrequency,
                cache: BaseCache,
                api_key: str,
            ) -> None:
                # Sequentially register each datapoint in the background worker.
                for img, pred, nm in zip(imgs, preds, names):
                    try:
                        # Call execute_registration directly to avoid partial object creation
                        execute_registration(
                            image=img,
                            prediction=pred,
                            target_project=tgt_project,
                            persist_predictions=persist_preds,
                            usage_quota_name=usage_quota,
                            minutely_usage_limit=minutely_limit,
                            hourly_usage_limit=hourly_limit,
                            daily_usage_limit=daily_limit,
                            max_image_size=max_size,
                            compression_level=compression_lvl,
                            registration_tags=tags,
                            labeling_batch_prefix=labeling_prefix,
                            new_labeling_batch_frequency=recreation_freq,
                            cache=cache,
                            api_key=api_key,
                            image_name=nm,
                        )
                    except Exception:
                        # preserve original behavior: execute_registration logs exceptions itself
                        # swallow exceptions to ensure background worker continues processing
                        pass

            # Schedule single background job using background_tasks preferentially
            if self._background_tasks:
                # BackgroundTasks accepts kwargs; pass them to avoid partial creation
                self._background_tasks.add_task(
                    _bulk_register,
                    images,
                    predictions,
                    image_names,
                    target_project,
                    usage_quota_name,
                    persist_predictions,
                    minutely_usage_limit,
                    hourly_usage_limit,
                    daily_usage_limit,
                    max_image_size,
                    compression_level,
                    registration_tags,
                    labeling_batch_prefix,
                    labeling_batches_recreation_frequency,
                    self._cache,
                    self._api_key,
                )
            else:
                # Submit the bulk worker to the thread pool
                self._thread_pool_executor.submit(
                    _bulk_register,
                    images,
                    predictions,
                    image_names,
                    target_project,
                    usage_quota_name,
                    persist_predictions,
                    minutely_usage_limit,
                    hourly_usage_limit,
                    daily_usage_limit,
                    max_image_size,
                    compression_level,
                    registration_tags,
                    labeling_batch_prefix,
                    labeling_batches_recreation_frequency,
                    self._cache,
                    self._api_key,
                )

            # Return immediate success messages for all items as original behavior
            return [
                {
                    "error_status": False,
                    "message": "Element registration happens in the background task",
                }
                for _ in range(len(images))
            ]

        # Synchronous or no background mechanism: process one-by-one
        result: List[dict] = []
        for image, prediction, img_name in zip(images, predictions, image_names):
            error_status, message = register_datapoint_at_roboflow(
                image=image,
                prediction=prediction,
                target_project=target_project,
                usage_quota_name=usage_quota_name,
                persist_predictions=persist_predictions,
                minutely_usage_limit=minutely_usage_limit,
                hourly_usage_limit=hourly_usage_limit,
                daily_usage_limit=daily_usage_limit,
                max_image_size=max_image_size,
                compression_level=compression_level,
                registration_tags=registration_tags,
                fire_and_forget=fire_and_forget,
                labeling_batch_prefix=labeling_batch_prefix,
                new_labeling_batch_frequency=labeling_batches_recreation_frequency,
                cache=self._cache,
                background_tasks=self._background_tasks,
                thread_pool_executor=self._thread_pool_executor,
                api_key=self._api_key,
                image_name=img_name,
            )
            result.append({"error_status": error_status, "message": message})
        return result


def register_datapoint_at_roboflow(
    image: WorkflowImageData,
    prediction: Optional[Union[sv.Detections, dict]],
    target_project: str,
    usage_quota_name: str,
    persist_predictions: bool,
    minutely_usage_limit: int,
    hourly_usage_limit: int,
    daily_usage_limit: int,
    max_image_size: Tuple[int, int],
    compression_level: int,
    registration_tags: List[str],
    fire_and_forget: bool,
    labeling_batch_prefix: str,
    new_labeling_batch_frequency: BatchCreationFrequency,
    cache: BaseCache,
    background_tasks: Optional[BackgroundTasks],
    thread_pool_executor: Optional[ThreadPoolExecutor],
    api_key: str,
    image_name: Optional[str] = None,
) -> Tuple[bool, str]:
    # Avoid creating a partial unless necessary; call execute_registration directly for synchronous path
    if fire_and_forget:
        # Prefer BackgroundTasks if provided
        if background_tasks:
            # Use add_task with keyword args to avoid creating a partial object
            background_tasks.add_task(
                execute_registration,
                image=image,
                prediction=prediction,
                target_project=target_project,
                persist_predictions=persist_predictions,
                usage_quota_name=usage_quota_name,
                minutely_usage_limit=minutely_usage_limit,
                hourly_usage_limit=hourly_usage_limit,
                daily_usage_limit=daily_usage_limit,
                max_image_size=max_image_size,
                compression_level=compression_level,
                registration_tags=registration_tags,
                labeling_batch_prefix=labeling_batch_prefix,
                new_labeling_batch_frequency=new_labeling_batch_frequency,
                cache=cache,
                api_key=api_key,
                image_name=image_name,
            )
            return False, "Element registration happens in the background task"
        if thread_pool_executor:
            # Submit callable with kwargs directly to avoid creating a partial
            thread_pool_executor.submit(
                execute_registration,
                image=image,
                prediction=prediction,
                target_project=target_project,
                persist_predictions=persist_predictions,
                usage_quota_name=usage_quota_name,
                minutely_usage_limit=minutely_usage_limit,
                hourly_usage_limit=hourly_usage_limit,
                daily_usage_limit=daily_usage_limit,
                max_image_size=max_image_size,
                compression_level=compression_level,
                registration_tags=registration_tags,
                labeling_batch_prefix=labeling_batch_prefix,
                new_labeling_batch_frequency=new_labeling_batch_frequency,
                cache=cache,
                api_key=api_key,
                image_name=image_name,
            )
            return False, "Element registration happens in the background task"
        # If fire_and_forget requested but no background mechanism provided, fall through to synchronous execution

    # Synchronous execution path: call execute_registration and return its result directly
    return execute_registration(
        image=image,
        prediction=prediction,
        target_project=target_project,
        persist_predictions=persist_predictions,
        usage_quota_name=usage_quota_name,
        minutely_usage_limit=minutely_usage_limit,
        hourly_usage_limit=hourly_usage_limit,
        daily_usage_limit=daily_usage_limit,
        max_image_size=max_image_size,
        compression_level=compression_level,
        registration_tags=registration_tags,
        labeling_batch_prefix=labeling_batch_prefix,
        new_labeling_batch_frequency=new_labeling_batch_frequency,
        cache=cache,
        api_key=api_key,
        image_name=image_name,
    )


RECREATION_INTERVAL2TIMESTAMP_GENERATOR = {
    "daily": generate_today_timestamp,
    "weekly": generate_start_timestamp_for_this_week,
    "monthly": generate_start_timestamp_for_this_month,
}
```