File size: 3,102 Bytes
3493993
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
from __future__ import annotations

from app.core.exceptions import MediaAPIError
from app.generation.domain.enums import WorkerErrorCategory


class GenerationError(MediaAPIError):
    code = "GENERATION_ERROR"
    status_code = 400


class GenerationProviderUnavailableError(GenerationError):
    code = "GENERATION_PROVIDER_UNAVAILABLE"
    status_code = 503


class GenerationCapabilityUnsupportedError(GenerationError):
    code = "GENERATION_CAPABILITY_UNSUPPORTED"
    status_code = 422


class GenerationValidationError(GenerationError):
    code = "GENERATION_VALIDATION_ERROR"
    status_code = 422


class GenerationRequestNotFoundError(GenerationError):
    code = "GENERATION_REQUEST_NOT_FOUND"
    status_code = 404


class GenerationJobNotFoundError(GenerationError):
    code = "GENERATION_JOB_NOT_FOUND"
    status_code = 404


class GenerationIdempotencyConflictError(GenerationError):
    code = "GENERATION_IDEMPOTENCY_CONFLICT"
    status_code = 409


class GenerationTransitionError(GenerationError):
    code = "GENERATION_INVALID_STATE_TRANSITION"
    status_code = 409


class GenerationInputAssetNotFoundError(GenerationError):
    code = "GENERATION_INPUT_ASSET_NOT_FOUND"
    status_code = 404


class GenerationProviderJobConflictError(GenerationError):
    """A provider job ID is already bound to a different logical job."""

    code = "GENERATION_PROVIDER_JOB_CONFLICT"
    status_code = 409


class GenerationOutputConflictError(GenerationError):
    code = "GENERATION_OUTPUT_CONFLICT"
    status_code = 409


class GenerationWorkerError(GenerationError):
    """Safe normalised failure returned by a remote generation worker."""

    code = "GENERATION_WORKER_ERROR"
    status_code = 502

    _CLIENT_STATUS_BY_CATEGORY = {
        WorkerErrorCategory.INVALID_REQUEST: 422,
        WorkerErrorCategory.WORKER_UNAVAILABLE: 503,
        WorkerErrorCategory.WORKER_NOT_READY: 503,
        WorkerErrorCategory.TIMEOUT: 504,
        WorkerErrorCategory.RATE_LIMITED: 503,
    }

    def __init__(
        self,
        *,
        category: WorkerErrorCategory,
        message: str,
        retryable: bool,
        http_status: int | None = None,
    ) -> None:
        super().__init__(message)
        self.category = category
        self.retryable = retryable
        self.http_status = http_status
        self.status_code = self._CLIENT_STATUS_BY_CATEGORY.get(category, 502)


class GenerationCancellationError(GenerationWorkerError):
    code = "GENERATION_CANCELLATION_FAILED"

    def __init__(self, message: str = "Generation cancellation could not be confirmed.") -> None:
        super().__init__(
            category=WorkerErrorCategory.CANCELLATION_ERROR,
            message=message,
            retryable=False,
        )


class GenerationOutputError(GenerationWorkerError):
    code = "GENERATION_OUTPUT_INVALID"

    def __init__(self, message: str = "Generation worker output is invalid.") -> None:
        super().__init__(
            category=WorkerErrorCategory.OUTPUT_ERROR,
            message=message,
            retryable=False,
        )