refactor(python-bridge): type execution errors
Some checks failed
LiteLLM Rust / rust-lint (push) Has been cancelled
LiteLLM Rust / rust-test (push) Has been cancelled

This commit is contained in:
Yujong Lee 2026-09-10 19:35:26 -07:00
parent b4d5b418bc
commit 56796e2a7c
4 changed files with 22 additions and 8 deletions

View file

@ -1475,6 +1475,7 @@ dependencies = [
"pyo3-async-runtimes",
"serde",
"serde_json",
"thiserror 2.0.19",
"tokio",
"tokio-tungstenite",
"tracing",

View file

@ -30,6 +30,7 @@ pyo3.workspace = true
pyo3-async-runtimes.workspace = true
serde.workspace = true
serde_json.workspace = true
thiserror.workspace = true
tokio.workspace = true
[dev-dependencies]

View file

@ -16,6 +16,10 @@ pyo3::create_exception!(
"The provider call was already issued and failed. Args are (status, message); status is 0 when there was no HTTP response."
);
fn upstream_error(status: u16, message: String) -> PyErr {
RustUpstreamError::new_err((status, message))
}
pub(crate) fn core_error_to_pyerr(err: Error) -> PyErr {
match err {
Error::Auth(message) => PyValueError::new_err(message),
@ -46,11 +50,9 @@ pub(crate) fn chat_completions_error_to_pyerr(err: Error) -> PyErr {
// Nothing reached the provider, so serving it on Python cannot double
// bill and is the only way the caller gets an answer at all.
| Error::Connect(_) => RustBridgeDeclined::new_err(err.to_string()),
Error::Http { status, body } => {
RustUpstreamError::new_err((status, format!("{status}: {body}")))
}
Error::Http { status, body } => upstream_error(status, format!("{status}: {body}")),
Error::Network(message) | Error::InvalidResponse(message) => {
RustUpstreamError::new_err((0u16, message))
upstream_error(0, message)
}
}
}
@ -66,7 +68,7 @@ pub(crate) fn ocr_error_to_pyerr(err: Error) -> PyErr {
Error::MissingField("document_url" | "image_url") => {
PyValueError::new_err("Document URL is required")
}
Error::Http { status, body } => RustUpstreamError::new_err((status, body)),
Error::Http { status, body } => upstream_error(status, body),
other => core_error_to_pyerr(other),
}
}

View file

@ -11,6 +11,18 @@ use serde::Serialize;
use tokio::runtime::{Handle, Runtime};
use tokio::time::{self, MissedTickBehavior};
#[derive(Debug, thiserror::Error)]
enum ExecutionError {
#[error("synchronous native routes cannot run from a Tokio context; use the async route")]
SyncRouteInTokioContext,
}
impl From<ExecutionError> for PyErr {
fn from(error: ExecutionError) -> Self {
PyRuntimeError::new_err(error.to_string())
}
}
pub(crate) fn run_sync<T, F>(
py: Python<'_>,
future: F,
@ -39,9 +51,7 @@ where
F: Future<Output = Result<T, Error>> + Send + 'static,
{
if Handle::try_current().is_ok() {
return Err(PyRuntimeError::new_err(
"synchronous native routes cannot run from a Tokio context; use the async route",
));
return Err(ExecutionError::SyncRouteInTokioContext.into());
}
let result = release_gil(py, move || runtime.block_on(wait_for_sync_result(future)))?;