mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-20 00:11:50 +00:00
refactor(rust): standardize messages errors
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
4e5a9efd9d
commit
03cd00fbb1
6 changed files with 72 additions and 56 deletions
|
|
@ -2,16 +2,54 @@
|
|||
pub enum Error {
|
||||
#[error("invalid provider: {0}")]
|
||||
InvalidProvider(String),
|
||||
#[error("missing required field: {0}")]
|
||||
MissingField(&'static str),
|
||||
#[error("invalid request: {0}")]
|
||||
InvalidRequest(String),
|
||||
#[error("invalid response: {0}")]
|
||||
InvalidResponse(String),
|
||||
#[error("routing error: {0}")]
|
||||
Routing(String),
|
||||
#[error("unsupported by the Rust messages route: {0}")]
|
||||
Unsupported(&'static str),
|
||||
#[error(transparent)]
|
||||
Auth(#[from] litellm_auth::Error),
|
||||
#[error(transparent)]
|
||||
Transport(#[from] crate::transport::Error),
|
||||
#[error(transparent)]
|
||||
Headers(#[from] crate::http_utils::HeaderError),
|
||||
#[error("stream framing failed: {0}")]
|
||||
StreamFraming(String),
|
||||
#[error("Anthropic SSE frame has no data")]
|
||||
MissingStreamData,
|
||||
#[error("Anthropic stream event is invalid: {0}")]
|
||||
InvalidStreamEvent(String),
|
||||
#[error("Bedrock event payload is invalid: {0}")]
|
||||
InvalidBedrockPayload(String),
|
||||
#[error("Bedrock event payload has invalid base64: {0}")]
|
||||
InvalidBedrockBase64(String),
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub fn is_request(&self) -> bool {
|
||||
match self {
|
||||
Self::InvalidProvider(_)
|
||||
| Self::MissingField(_)
|
||||
| Self::InvalidRequest(_)
|
||||
| Self::Unsupported(_)
|
||||
| Self::Headers(_) => true,
|
||||
Self::Auth(error) => !matches!(error, litellm_auth::Error::MissingApiKey { .. }),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_response(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::InvalidResponse(_)
|
||||
| Self::StreamFraming(_)
|
||||
| Self::MissingStreamData
|
||||
| Self::InvalidStreamEvent(_)
|
||||
| Self::InvalidBedrockPayload(_)
|
||||
| Self::InvalidBedrockBase64(_)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,9 +46,7 @@ pub(super) async fn execute_messages_provider_stream(
|
|||
) -> Result<reqwest::Response, Error> {
|
||||
let request = prepare_provider_request(request)?;
|
||||
if request.provider != ANTHROPIC_MESSAGES_PROVIDER {
|
||||
return Err(Error::InvalidRequest(
|
||||
"streaming messages is not supported for this provider".to_string(),
|
||||
));
|
||||
return Err(Error::Unsupported("streaming messages for this provider"));
|
||||
}
|
||||
|
||||
let mut request_builder = http_client().post(&request.url).json(&request.body);
|
||||
|
|
|
|||
|
|
@ -149,9 +149,7 @@ impl AnthropicBatchesConfig for AnthropicBatchesTransformation {
|
|||
}
|
||||
|
||||
fn transform_create_batch_request(&self) -> Result<Value, Error> {
|
||||
Err(Error::InvalidRequest(
|
||||
"Batch creation not yet implemented for Anthropic".into(),
|
||||
))
|
||||
Err(Error::Unsupported("Anthropic message batch creation"))
|
||||
}
|
||||
|
||||
fn transform_create_batch_response(
|
||||
|
|
@ -159,9 +157,7 @@ impl AnthropicBatchesConfig for AnthropicBatchesTransformation {
|
|||
_response: AnthropicMessageBatch,
|
||||
_now: i64,
|
||||
) -> Result<LiteLlmMessageBatch, Error> {
|
||||
Err(Error::InvalidResponse(
|
||||
"Batch creation not yet implemented for Anthropic".into(),
|
||||
))
|
||||
Err(Error::Unsupported("Anthropic message batch creation"))
|
||||
}
|
||||
|
||||
fn retrieve_batch_url(
|
||||
|
|
@ -171,7 +167,7 @@ impl AnthropicBatchesConfig for AnthropicBatchesTransformation {
|
|||
env_lookup: &dyn Fn(&str) -> Option<String>,
|
||||
) -> Result<String, Error> {
|
||||
if batch_id.is_empty() {
|
||||
return Err(Error::InvalidRequest("batch_id is required".into()));
|
||||
return Err(Error::MissingField("batch_id"));
|
||||
}
|
||||
let mut url = batches_base_url(api_base, env_lookup)?;
|
||||
url.path_segments_mut()
|
||||
|
|
@ -331,14 +327,12 @@ mod tests {
|
|||
fn preserves_python_placeholder_for_batch_creation() {
|
||||
assert!(matches!(
|
||||
ANTHROPIC_BATCHES_TRANSFORMATION.transform_create_batch_request(),
|
||||
Err(Error::InvalidRequest(message))
|
||||
if message == "Batch creation not yet implemented for Anthropic"
|
||||
Err(Error::Unsupported("Anthropic message batch creation"))
|
||||
));
|
||||
let response: AnthropicMessageBatch = serde_json::from_value(json!({})).unwrap();
|
||||
assert!(matches!(
|
||||
ANTHROPIC_BATCHES_TRANSFORMATION.transform_create_batch_response(response, 0),
|
||||
Err(Error::InvalidResponse(message))
|
||||
if message == "Batch creation not yet implemented for Anthropic"
|
||||
Err(Error::Unsupported("Anthropic message batch creation"))
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,12 +68,10 @@ impl AnthropicCountTokensConfig for AnthropicCountTokensTransformation {
|
|||
|
||||
fn validate_request(&self, model: &str, messages: &[AnthropicMessage]) -> Result<(), Error> {
|
||||
if model.is_empty() {
|
||||
return Err(Error::InvalidRequest("model parameter is required".into()));
|
||||
return Err(Error::MissingField("model"));
|
||||
}
|
||||
if messages.is_empty() {
|
||||
return Err(Error::InvalidRequest(
|
||||
"messages parameter is required".into(),
|
||||
));
|
||||
return Err(Error::MissingField("messages"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -143,7 +141,7 @@ mod tests {
|
|||
None,
|
||||
None
|
||||
),
|
||||
Err(Error::InvalidRequest(message)) if message == "model parameter is required"
|
||||
Err(Error::MissingField("model"))
|
||||
));
|
||||
assert!(matches!(
|
||||
ANTHROPIC_COUNT_TOKENS_TRANSFORMATION.transform_request(
|
||||
|
|
@ -152,7 +150,7 @@ mod tests {
|
|||
None,
|
||||
None
|
||||
),
|
||||
Err(Error::InvalidRequest(message)) if message == "messages parameter is required"
|
||||
Err(Error::MissingField("messages"))
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,17 +7,7 @@ use litellm_framing::sse::{SseFrame, SseFramer};
|
|||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{Map, Value};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum AnthropicStreamDecodeError {
|
||||
#[error("stream framing failed: {0}")]
|
||||
Framing(#[from] litellm_framing::Error),
|
||||
#[error("Anthropic SSE frame has no data")]
|
||||
MissingSseData,
|
||||
#[error("Anthropic stream event is invalid: {0}")]
|
||||
InvalidEvent(#[from] serde_json::Error),
|
||||
#[error("Bedrock event payload has invalid base64: {0}")]
|
||||
InvalidBedrockPayload(#[from] base64::DecodeError),
|
||||
}
|
||||
use crate::messages::Error;
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
|
||||
pub struct AnthropicStreamUsage {
|
||||
|
|
@ -148,47 +138,48 @@ struct BedrockChunkPayload {
|
|||
bytes: String,
|
||||
}
|
||||
|
||||
pub fn decode_anthropic_sse_frame(
|
||||
frame: SseFrame,
|
||||
) -> Result<AnthropicMessagesStreamEvent, AnthropicStreamDecodeError> {
|
||||
let data = frame
|
||||
.data
|
||||
.ok_or(AnthropicStreamDecodeError::MissingSseData)?;
|
||||
Ok(serde_json::from_str(&data)?)
|
||||
pub fn decode_anthropic_sse_frame(frame: SseFrame) -> Result<AnthropicMessagesStreamEvent, Error> {
|
||||
let data = frame.data.ok_or(Error::MissingStreamData)?;
|
||||
serde_json::from_str(&data).map_err(|error| Error::InvalidStreamEvent(error.to_string()))
|
||||
}
|
||||
|
||||
pub fn decode_bedrock_anthropic_frame(
|
||||
frame: AwsEventStreamFrame,
|
||||
) -> Result<AnthropicMessagesStreamEvent, AnthropicStreamDecodeError> {
|
||||
let payload: BedrockChunkPayload = serde_json::from_slice(&frame.payload)?;
|
||||
let event = base64::engine::general_purpose::STANDARD.decode(payload.bytes)?;
|
||||
Ok(serde_json::from_slice(&event)?)
|
||||
) -> Result<AnthropicMessagesStreamEvent, Error> {
|
||||
let payload: BedrockChunkPayload = serde_json::from_slice(&frame.payload)
|
||||
.map_err(|error| Error::InvalidBedrockPayload(error.to_string()))?;
|
||||
let event = base64::engine::general_purpose::STANDARD
|
||||
.decode(payload.bytes)
|
||||
.map_err(|error| Error::InvalidBedrockBase64(error.to_string()))?;
|
||||
serde_json::from_slice(&event).map_err(|error| Error::InvalidStreamEvent(error.to_string()))
|
||||
}
|
||||
|
||||
pub fn direct_anthropic_event_stream<S, B, E>(
|
||||
input: S,
|
||||
) -> impl Stream<Item = Result<AnthropicMessagesStreamEvent, AnthropicStreamDecodeError>> + Send
|
||||
) -> impl Stream<Item = Result<AnthropicMessagesStreamEvent, Error>> + Send
|
||||
where
|
||||
S: Stream<Item = Result<B, E>> + Send,
|
||||
B: Buf + Send,
|
||||
E: std::error::Error + Send + Sync + 'static,
|
||||
{
|
||||
SseFramer
|
||||
.frame(input)
|
||||
.map(|frame| decode_anthropic_sse_frame(frame?))
|
||||
SseFramer.frame(input).map(|frame| {
|
||||
let frame = frame.map_err(|error| Error::StreamFraming(error.to_string()))?;
|
||||
decode_anthropic_sse_frame(frame)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn bedrock_anthropic_event_stream<S, B, E>(
|
||||
input: S,
|
||||
) -> impl Stream<Item = Result<AnthropicMessagesStreamEvent, AnthropicStreamDecodeError>> + Send
|
||||
) -> impl Stream<Item = Result<AnthropicMessagesStreamEvent, Error>> + Send
|
||||
where
|
||||
S: Stream<Item = Result<B, E>> + Send,
|
||||
B: Buf + Send,
|
||||
E: std::error::Error + Send + Sync + 'static,
|
||||
{
|
||||
AwsEventStreamFramer
|
||||
.frame(input)
|
||||
.map(|frame| decode_bedrock_anthropic_frame(frame?))
|
||||
AwsEventStreamFramer.frame(input).map(|frame| {
|
||||
let frame = frame.map_err(|error| Error::StreamFraming(error.to_string()))?;
|
||||
decode_bedrock_anthropic_frame(frame)
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
|||
|
|
@ -46,10 +46,7 @@ pub(crate) fn core_error_to_pyerr(error: Error) -> PyErr {
|
|||
),
|
||||
Error::Messages(error) => match error {
|
||||
messages::Error::Auth(source) => auth_is_value_error(source),
|
||||
messages::Error::InvalidProvider(_)
|
||||
| messages::Error::InvalidRequest(_)
|
||||
| messages::Error::Headers(_) => true,
|
||||
_ => false,
|
||||
_ => error.is_request(),
|
||||
},
|
||||
Error::AudioTranscription(error) => match error {
|
||||
audio_transcription::Error::Auth(source) => auth_is_value_error(source),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue