cad861a13d
抽出单一 SFX Worker 编排并覆盖计费、翻译、ElevenLabs、OSS 与写回 补齐稳定失败分类、退款和零副作用组合测试 修复画布 Agent 的自动时长、小数范围、Loop 与 Prompt 参数契约 同步测试证据、发布门禁和项目共享记忆
249 lines
6.6 KiB
Rust
249 lines
6.6 KiB
Rust
use std::{error::Error, fmt};
|
|
|
|
use crate::VECTOR_ENGINE_PROVIDER;
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
pub enum AudioStatusHint {
|
|
BadRequest,
|
|
ServiceUnavailable,
|
|
BadGateway,
|
|
GatewayTimeout,
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub enum AudioError {
|
|
InvalidConfig {
|
|
provider: &'static str,
|
|
message: String,
|
|
},
|
|
InvalidRequest {
|
|
provider: &'static str,
|
|
message: String,
|
|
},
|
|
Request {
|
|
provider: &'static str,
|
|
message: String,
|
|
endpoint: Option<String>,
|
|
timeout: bool,
|
|
connect: bool,
|
|
request: bool,
|
|
body: bool,
|
|
status_code: Option<u16>,
|
|
source: Option<String>,
|
|
},
|
|
Upstream {
|
|
provider: &'static str,
|
|
message: String,
|
|
upstream_status: u16,
|
|
raw_excerpt: String,
|
|
},
|
|
ResponseParse {
|
|
provider: &'static str,
|
|
message: String,
|
|
raw_excerpt: String,
|
|
},
|
|
MissingAudio {
|
|
provider: &'static str,
|
|
message: String,
|
|
reason_code: Option<&'static str>,
|
|
},
|
|
}
|
|
|
|
impl AudioError {
|
|
pub fn provider(&self) -> &'static str {
|
|
match self {
|
|
Self::InvalidConfig { provider, .. }
|
|
| Self::InvalidRequest { provider, .. }
|
|
| Self::Request { provider, .. }
|
|
| Self::Upstream { provider, .. }
|
|
| Self::ResponseParse { provider, .. }
|
|
| Self::MissingAudio { provider, .. } => provider,
|
|
}
|
|
}
|
|
|
|
pub fn message(&self) -> &str {
|
|
match self {
|
|
Self::InvalidConfig { message, .. }
|
|
| Self::InvalidRequest { message, .. }
|
|
| Self::Request { message, .. }
|
|
| Self::Upstream { message, .. }
|
|
| Self::ResponseParse { message, .. }
|
|
| Self::MissingAudio { message, .. } => message,
|
|
}
|
|
}
|
|
|
|
/// 可安全写入任务记录和指标的稳定失败分类;不会携带 provider 正文或凭据。
|
|
pub fn reason_code(&self) -> Option<&'static str> {
|
|
match self {
|
|
Self::MissingAudio { reason_code, .. } => *reason_code,
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn status_hint(&self) -> AudioStatusHint {
|
|
match self {
|
|
Self::InvalidConfig { .. } => AudioStatusHint::ServiceUnavailable,
|
|
Self::InvalidRequest { .. } => AudioStatusHint::BadRequest,
|
|
Self::Request {
|
|
timeout,
|
|
status_code,
|
|
..
|
|
} if *timeout => AudioStatusHint::GatewayTimeout,
|
|
Self::Request { status_code, .. }
|
|
if status_code.is_some_and(|status| status >= 500) =>
|
|
{
|
|
AudioStatusHint::BadGateway
|
|
}
|
|
Self::Upstream { .. } | Self::ResponseParse { .. } | Self::MissingAudio { .. } => {
|
|
AudioStatusHint::BadGateway
|
|
}
|
|
Self::Request { .. } => AudioStatusHint::BadGateway,
|
|
}
|
|
}
|
|
|
|
pub fn invalid_config(message: impl Into<String>) -> Self {
|
|
Self::invalid_config_for(VECTOR_ENGINE_PROVIDER, message)
|
|
}
|
|
|
|
pub fn invalid_config_for(provider: &'static str, message: impl Into<String>) -> Self {
|
|
Self::InvalidConfig {
|
|
provider,
|
|
message: message.into(),
|
|
}
|
|
}
|
|
|
|
pub fn invalid_request(message: impl Into<String>) -> Self {
|
|
Self::invalid_request_for(VECTOR_ENGINE_PROVIDER, message)
|
|
}
|
|
|
|
pub fn invalid_request_for(provider: &'static str, message: impl Into<String>) -> Self {
|
|
Self::InvalidRequest {
|
|
provider,
|
|
message: message.into(),
|
|
}
|
|
}
|
|
|
|
pub fn request(
|
|
message: impl Into<String>,
|
|
endpoint: Option<String>,
|
|
timeout: bool,
|
|
connect: bool,
|
|
request: bool,
|
|
body: bool,
|
|
status_code: Option<u16>,
|
|
source: Option<String>,
|
|
) -> Self {
|
|
Self::request_for(
|
|
VECTOR_ENGINE_PROVIDER,
|
|
message,
|
|
endpoint,
|
|
timeout,
|
|
connect,
|
|
request,
|
|
body,
|
|
status_code,
|
|
source,
|
|
)
|
|
}
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub fn request_for(
|
|
provider: &'static str,
|
|
message: impl Into<String>,
|
|
endpoint: Option<String>,
|
|
timeout: bool,
|
|
connect: bool,
|
|
request: bool,
|
|
body: bool,
|
|
status_code: Option<u16>,
|
|
source: Option<String>,
|
|
) -> Self {
|
|
Self::Request {
|
|
provider,
|
|
message: message.into(),
|
|
endpoint,
|
|
timeout,
|
|
connect,
|
|
request,
|
|
body,
|
|
status_code,
|
|
source,
|
|
}
|
|
}
|
|
|
|
pub fn upstream(
|
|
message: impl Into<String>,
|
|
upstream_status: u16,
|
|
raw_excerpt: impl Into<String>,
|
|
) -> Self {
|
|
Self::upstream_for(
|
|
VECTOR_ENGINE_PROVIDER,
|
|
message,
|
|
upstream_status,
|
|
raw_excerpt,
|
|
)
|
|
}
|
|
|
|
pub fn upstream_for(
|
|
provider: &'static str,
|
|
message: impl Into<String>,
|
|
upstream_status: u16,
|
|
raw_excerpt: impl Into<String>,
|
|
) -> Self {
|
|
Self::Upstream {
|
|
provider,
|
|
message: message.into(),
|
|
upstream_status,
|
|
raw_excerpt: raw_excerpt.into(),
|
|
}
|
|
}
|
|
|
|
pub fn response_parse(message: impl Into<String>, raw_excerpt: impl Into<String>) -> Self {
|
|
Self::response_parse_for(VECTOR_ENGINE_PROVIDER, message, raw_excerpt)
|
|
}
|
|
|
|
pub fn response_parse_for(
|
|
provider: &'static str,
|
|
message: impl Into<String>,
|
|
raw_excerpt: impl Into<String>,
|
|
) -> Self {
|
|
Self::ResponseParse {
|
|
provider,
|
|
message: message.into(),
|
|
raw_excerpt: raw_excerpt.into(),
|
|
}
|
|
}
|
|
|
|
pub fn missing_audio(message: impl Into<String>) -> Self {
|
|
Self::missing_audio_for(VECTOR_ENGINE_PROVIDER, message)
|
|
}
|
|
|
|
pub fn missing_audio_for(provider: &'static str, message: impl Into<String>) -> Self {
|
|
Self::MissingAudio {
|
|
provider,
|
|
message: message.into(),
|
|
reason_code: None,
|
|
}
|
|
}
|
|
|
|
pub fn missing_audio_with_reason_for(
|
|
provider: &'static str,
|
|
reason_code: &'static str,
|
|
message: impl Into<String>,
|
|
) -> Self {
|
|
Self::MissingAudio {
|
|
provider,
|
|
message: message.into(),
|
|
reason_code: Some(reason_code),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for AudioError {
|
|
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
formatter.write_str(self.message())
|
|
}
|
|
}
|
|
|
|
impl Error for AudioError {}
|