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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
use std::time::Duration;
use tea_protocol::{ProtocolMetadata, RetryClass};
use crate::ModelStreamValueError;
/// Stable provider-neutral model failure classification.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ModelFailureCode {
/// Request failed provider-neutral validation.
InvalidRequest,
/// Prompt and requested output exceed model context.
ContextOverflow,
/// Provider credentials are missing or invalid.
Authentication,
/// Credentials are valid but operation is not permitted.
PermissionDenied,
/// Provider rate limit rejected the operation.
RateLimited,
/// Provider or selected model is temporarily unavailable.
Unavailable,
/// Network or transport failed.
Transport,
/// Provider response could not be normalized safely.
MalformedResponse,
/// Operation was cooperatively cancelled.
Cancelled,
/// Unexpected adapter/runtime failure.
Internal,
}
impl ModelFailureCode {
/// All stable failure codes.
pub const ALL: [Self; 10] = [
Self::InvalidRequest,
Self::ContextOverflow,
Self::Authentication,
Self::PermissionDenied,
Self::RateLimited,
Self::Unavailable,
Self::Transport,
Self::MalformedResponse,
Self::Cancelled,
Self::Internal,
];
}
/// Provider-neutral terminal model failure.
#[derive(Debug, Clone, PartialEq)]
pub struct ModelFailure {
code: ModelFailureCode,
message: String,
retry: RetryClass,
retry_after: Option<Duration>,
metadata: ProtocolMetadata,
safe_diagnostic: bool,
}
impl ModelFailure {
/// Creates a fixed internal adapter failure.
#[must_use]
pub fn internal_adapter_failure() -> Self {
Self {
code: ModelFailureCode::Internal,
message: "model adapter failed internally".to_owned(),
retry: RetryClass::Never,
retry_after: None,
metadata: ProtocolMetadata::default(),
safe_diagnostic: false,
}
}
/// Creates a bounded technical failure without an internal source chain.
///
/// # Errors
///
/// Returns an error when `message` is empty, exceeds 4 KiB, or contains a
/// null character.
pub fn new(
code: ModelFailureCode,
message: impl Into<String>,
retry: RetryClass,
) -> Result<Self, ModelStreamValueError> {
let message = message.into();
if message.is_empty() || message.len() > 4096 || message.contains('\0') {
return Err(ModelStreamValueError::InvalidFailureMessage);
}
Ok(Self {
code,
message,
retry,
retry_after: None,
metadata: ProtocolMetadata::default(),
safe_diagnostic: false,
})
}
/// Creates a provider failure whose message was normalized for display.
///
/// The caller must have removed provider payload fields, bounded the text,
/// and stripped terminal control characters before using this constructor.
///
/// # Errors
///
/// Returns an error when the normalized message violates the model failure
/// bounds.
pub fn safe(
code: ModelFailureCode,
message: impl Into<String>,
retry: RetryClass,
) -> Result<Self, ModelStreamValueError> {
let mut failure = Self::new(code, message, retry)?;
failure.safe_diagnostic = true;
Ok(failure)
}
/// Adds bounded namespaced safe metadata.
#[must_use]
pub fn with_metadata(mut self, metadata: ProtocolMetadata) -> Self {
self.metadata = metadata;
self
}
/// Adds a provider-requested delay before this failure is retried.
#[must_use]
pub fn with_retry_after(mut self, retry_after: Duration) -> Self {
self.retry_after = Some(retry_after);
self
}
/// Returns the stable failure code.
#[must_use]
pub const fn code(&self) -> ModelFailureCode {
self.code
}
/// Returns the English technical message.
#[must_use]
pub fn message(&self) -> &str {
&self.message
}
/// Returns the retry classification.
#[must_use]
pub const fn retry(&self) -> RetryClass {
self.retry
}
/// Returns the provider-requested retry delay when one was supplied.
#[must_use]
pub const fn retry_after(&self) -> Option<Duration> {
self.retry_after
}
/// Returns safe namespaced metadata.
#[must_use]
pub const fn metadata(&self) -> &ProtocolMetadata {
&self.metadata
}
/// Returns whether the message is safe to expose as provider diagnostics.
#[must_use]
pub const fn is_safe_diagnostic(&self) -> bool {
self.safe_diagnostic
}
}