mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-16 04:29:31 +00:00
refactor(host-python): collect host failures in a thiserror Error enum (#46361)
* refactor(host-python): collect host failures in a thiserror Error enum missing_state() and the inline PyRuntimeError/PyTypeError messages become Error variants with one From<Error> for PyErr, and InvokeError derives thiserror so From<PyErr> comes from #[from] Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(host-python): note the canonical HostError import alias in AGENTS.md Co-Authored-By: Yujong Lee <yujong@berri.ai> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
327bdcaa7f
commit
ab0875b2da
17 changed files with 115 additions and 83 deletions
1
litellm-rust/Cargo.lock
generated
1
litellm-rust/Cargo.lock
generated
|
|
@ -3929,6 +3929,7 @@ dependencies = [
|
|||
"rstest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.19",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use litellm_host::{
|
|||
interceptors::{RawResponse, RequestContext, WireRequest},
|
||||
lifecycle::{FailureOrigin, Timing, epoch_seconds},
|
||||
};
|
||||
use litellm_host_python::{HookStep, from_py, missing_state, to_py};
|
||||
use litellm_host_python::{Error as HostError, HookStep, from_py, to_py};
|
||||
use pyo3::{
|
||||
exceptions::{PyBaseException, PyException},
|
||||
gc::{PyTraverseError, PyVisit},
|
||||
|
|
@ -160,7 +160,7 @@ impl LegacyLogging {
|
|||
self.response
|
||||
.as_ref()
|
||||
.map(|response| HookStep::Ready(response.clone_ref(py)))
|
||||
.ok_or_else(missing_state)
|
||||
.ok_or_else(|| HostError::MissingState.into())
|
||||
}
|
||||
|
||||
fn dispatch_success(&self, py: Python<'_>) -> PyResult<()> {
|
||||
|
|
@ -209,7 +209,7 @@ impl LegacyLogging {
|
|||
|
||||
fn stream_success(&self, py: Python<'_>, stream: &DeliveredStream) -> PyResult<()> {
|
||||
let logger = self.logger()?;
|
||||
let billing = self.stream_billing().ok_or_else(missing_state)?;
|
||||
let billing = self.stream_billing().ok_or(HostError::MissingState)?;
|
||||
let billed = Streaming::Success.call(
|
||||
py,
|
||||
(
|
||||
|
|
@ -525,7 +525,7 @@ impl LegacyLogging {
|
|||
return self.stream_failure(py);
|
||||
}
|
||||
if origin == FailureOrigin::Call && self.logger.is_some() && self.runs_deployment_hooks() {
|
||||
let error = self.error.as_ref().ok_or_else(missing_state)?;
|
||||
let error = self.error.as_ref().ok_or(HostError::MissingState)?;
|
||||
return Ok(HookStep::Await(
|
||||
DeploymentHooks::after_failure(py, self.call.kwargs(), error, self.call_type())?,
|
||||
Self::resume_deployment_failure,
|
||||
|
|
@ -536,7 +536,7 @@ impl LegacyLogging {
|
|||
|
||||
pub(crate) fn stream_opened(&mut self, py: Python<'_>, head: &Py<PyAny>) -> PyResult<()> {
|
||||
if self.stream_billing().is_none() {
|
||||
return Err(missing_state());
|
||||
return Err(HostError::MissingState.into());
|
||||
}
|
||||
if let Some(key) = &self.cache_key {
|
||||
head.bind(py).set_item("cache_key", key)?;
|
||||
|
|
@ -551,7 +551,7 @@ impl LegacyLogging {
|
|||
}
|
||||
|
||||
pub(crate) fn stream_chunk(&mut self, py: Python<'_>, chunk: &Py<PyAny>) -> PyResult<()> {
|
||||
let stream = self.stream.as_mut().ok_or_else(missing_state)?;
|
||||
let stream = self.stream.as_mut().ok_or(HostError::MissingState)?;
|
||||
if stream.first_chunk.is_none() {
|
||||
stream.first_chunk = Some(datetime(py, epoch_seconds())?);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ Terminal notifications share the selected response or exception. An ordinary not
|
|||
- `PythonBinding::decode_request` receives the keyword view returned by `prepare_arguments` and updated by `arguments_prepared`, not the caller's dict. A binding that decodes from it inherits all composed argument rewrites
|
||||
- A native failure, including one a host op returns as `InvokeError::Native`, is classified exactly once through the binding's `map_error`. A Python exception raised inside the call, and a failure in `prepare_arguments` or `transform_response`, is raised as is
|
||||
- A failing `map_error` is raised with the native error's text as its `__context__`, never swallowed
|
||||
- Consumers import this crate's error as `use litellm_host_python::Error as HostError`, so it never shadows their own `Error`
|
||||
- Use standard PyO3 ownership and conversion APIs
|
||||
- Prefer `Bound<'py, T>` for attached operations/results, `Py<T>` for retention. Binding/unbinding does not copy payloads
|
||||
- Use `pythonize` for selected Serde data, never a JSON-text round trip. Share conversion with `Pythonized<T>`
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ litellm-host.workspace = true
|
|||
bytes.workspace = true
|
||||
futures-util.workspace = true
|
||||
serde.workspace = true
|
||||
thiserror.workspace = true
|
||||
tokio = { workspace = true, features = ["rt", "sync"] }
|
||||
|
||||
pyo3.workspace = true
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ use crate::PythonHostCalls;
|
|||
use crate::handle::{Execution, ExecutionBody, ExecutionStep, PythonLifecycle};
|
||||
use crate::hooks::{HookResume, HookStep, PythonCallEvent, PythonCallHooks};
|
||||
use crate::native::{NativeMachine, NativePoll};
|
||||
use crate::{InvokeError, PythonBinding, missing_state};
|
||||
use crate::{Error, InvokeError, PythonBinding};
|
||||
|
||||
type ProtocolOf<H> = <H as PythonBinding>::Protocol;
|
||||
type ErrorOf<H> = <ProtocolOf<H> as Protocol>::Error;
|
||||
|
|
@ -158,9 +158,7 @@ where
|
|||
ExecutionStep::Open(head) => Execution::suspended(driver, options.lifecycle)
|
||||
.into_sync_stream(py, head)
|
||||
.map(Bound::unbind),
|
||||
ExecutionStep::Await(_) | ExecutionStep::Yield(_) => {
|
||||
Err(PyRuntimeError::new_err("sync call suspended"))
|
||||
}
|
||||
ExecutionStep::Await(_) | ExecutionStep::Yield(_) => Err(Error::SyncCallSuspended.into()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -254,7 +252,7 @@ where
|
|||
Err(error) => self.hook_failed(py, error),
|
||||
}
|
||||
}
|
||||
_ => Err(missing_state()),
|
||||
_ => Err(Error::MissingState.into()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -281,8 +279,8 @@ where
|
|||
return self.failure(py, error, FailureOrigin::Call);
|
||||
}
|
||||
};
|
||||
let start = self.start.take().ok_or_else(missing_state)?;
|
||||
let arguments = self.arguments.as_ref().ok_or_else(missing_state)?;
|
||||
let start = self.start.take().ok_or(Error::MissingState)?;
|
||||
let arguments = self.arguments.as_ref().ok_or(Error::MissingState)?;
|
||||
let machine = match start(py, arguments.bind(py), request) {
|
||||
Ok(machine) => machine,
|
||||
Err(error) => return self.failure(py, error, FailureOrigin::Host),
|
||||
|
|
@ -357,7 +355,7 @@ where
|
|||
Stage::Failed(error) => {
|
||||
Err(PyErr::from_value(error.bind(py).clone().into_any()))
|
||||
}
|
||||
_ => Err(missing_state()),
|
||||
_ => Err(Error::MissingState.into()),
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -365,7 +363,7 @@ where
|
|||
}
|
||||
|
||||
fn begin(&mut self, py: Python<'_>) -> PyResult<ExecutionStep> {
|
||||
let arguments = self.arguments.take().ok_or_else(missing_state)?;
|
||||
let arguments = self.arguments.take().ok_or(Error::MissingState)?;
|
||||
match self.hooks.prepare_arguments(py, arguments, self.started_at) {
|
||||
Ok(step) => self.on_arguments(py, step),
|
||||
Err(error) => self.hook_failed(py, error),
|
||||
|
|
|
|||
|
|
@ -1,20 +1,57 @@
|
|||
use pyo3::prelude::*;
|
||||
use pyo3::{
|
||||
PyErr,
|
||||
exceptions::{PyRuntimeError, PyTypeError},
|
||||
};
|
||||
|
||||
/// A failure host-python raises itself, surfacing in Python as the exception `PyErr::from` picks.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error("missing native call state")]
|
||||
MissingState,
|
||||
#[error("execution is already running")]
|
||||
ExecutionRunning,
|
||||
#[error("execution is closed")]
|
||||
ExecutionClosed,
|
||||
#[error("execution requires start before resume and can only start once")]
|
||||
ExecutionOutOfOrder,
|
||||
#[error("execution was closed while running")]
|
||||
ExecutionClosedWhileRunning,
|
||||
#[error("native execution closed")]
|
||||
NativeExecutionClosed,
|
||||
#[error("sync call suspended")]
|
||||
SyncCallSuspended,
|
||||
#[error("synchronous native routes cannot run from a Tokio context; use the async route")]
|
||||
SyncInsideTokio,
|
||||
#[error("the context body produced no value")]
|
||||
ContextBodyProducedNothing,
|
||||
#[error("file read must return bytes or str, got {got}")]
|
||||
FileContentType { got: String },
|
||||
}
|
||||
|
||||
impl From<Error> for PyErr {
|
||||
fn from(error: Error) -> Self {
|
||||
match error {
|
||||
Error::FileContentType { .. } => PyTypeError::new_err(error.to_string()),
|
||||
Error::MissingState
|
||||
| Error::ExecutionRunning
|
||||
| Error::ExecutionClosed
|
||||
| Error::ExecutionOutOfOrder
|
||||
| Error::ExecutionClosedWhileRunning
|
||||
| Error::NativeExecutionClosed
|
||||
| Error::SyncCallSuspended
|
||||
| Error::SyncInsideTokio
|
||||
| Error::ContextBodyProducedNothing => PyRuntimeError::new_err(error.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Why a custom operation the host answered did not produce a result: the route's own code
|
||||
/// rejected it, which the route classifies like any other native failure, or Python code
|
||||
/// raised, which reaches the caller as it was raised.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum InvokeError<E> {
|
||||
#[error("{0}")]
|
||||
Native(E),
|
||||
Python(PyErr),
|
||||
}
|
||||
|
||||
impl<E> From<PyErr> for InvokeError<E> {
|
||||
fn from(error: PyErr) -> Self {
|
||||
Self::Python(error)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn missing_state() -> PyErr {
|
||||
pyo3::exceptions::PyRuntimeError::new_err("missing native call state")
|
||||
#[error(transparent)]
|
||||
Python(#[from] PyErr),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,13 +3,14 @@
|
|||
|
||||
use bytes::Bytes;
|
||||
use pyo3::{
|
||||
exceptions::PyTypeError,
|
||||
gc::{PyTraverseError, PyVisit},
|
||||
prelude::*,
|
||||
pybacked::PyBackedBytes,
|
||||
types::{PyBytes, PyString},
|
||||
};
|
||||
|
||||
use crate::Error;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct FileContent {
|
||||
pub bytes: Bytes,
|
||||
|
|
@ -50,10 +51,10 @@ impl PythonFileReader {
|
|||
} else if value.is_instance_of::<PyBytes>() {
|
||||
py_bytes(&value)?
|
||||
} else {
|
||||
return Err(PyTypeError::new_err(format!(
|
||||
"file read must return bytes or str, got {}",
|
||||
value.get_type(),
|
||||
)));
|
||||
return Err(Error::FileContentType {
|
||||
got: value.get_type().to_string(),
|
||||
}
|
||||
.into());
|
||||
};
|
||||
Ok(FileContent {
|
||||
bytes,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@ use std::sync::{
|
|||
atomic::{AtomicU64, Ordering},
|
||||
};
|
||||
|
||||
use pyo3::{exceptions::PyRuntimeError, prelude::*};
|
||||
use pyo3::prelude::*;
|
||||
|
||||
use crate::Error;
|
||||
|
||||
/// The caller's `contextvars` context, captured at the Python entry point so blocking Python
|
||||
/// work started from Rust sees the same request-local values as the Python caller.
|
||||
|
|
@ -49,7 +51,7 @@ impl PythonContext {
|
|||
.lock()
|
||||
.expect("context value slot poisoned")
|
||||
.take()
|
||||
.ok_or_else(|| PyRuntimeError::new_err("the context body produced no value"))
|
||||
.ok_or_else(|| Error::ContextBodyProducedNothing.into())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::panic::{AssertUnwindSafe, catch_unwind};
|
||||
|
||||
use crate::panic_to_pyerr;
|
||||
use pyo3::exceptions::{PyBaseException, PyRuntimeError};
|
||||
use crate::{Error, panic_to_pyerr};
|
||||
use pyo3::exceptions::PyBaseException;
|
||||
use pyo3::gc::{PyTraverseError, PyVisit};
|
||||
use pyo3::prelude::*;
|
||||
|
||||
|
|
@ -76,15 +76,13 @@ impl Execution {
|
|||
match (&execution.state, result.is_some()) {
|
||||
(ExecutionState::Created(_), false) | (ExecutionState::Suspended(_), true) => {}
|
||||
(ExecutionState::Running, _) => {
|
||||
return Err(PyRuntimeError::new_err("execution is already running"));
|
||||
return Err(Error::ExecutionRunning.into());
|
||||
}
|
||||
(ExecutionState::Closed, _) => {
|
||||
return Err(PyRuntimeError::new_err("execution is closed"));
|
||||
return Err(Error::ExecutionClosed.into());
|
||||
}
|
||||
_ => {
|
||||
return Err(PyRuntimeError::new_err(
|
||||
"execution requires start before resume and can only start once",
|
||||
));
|
||||
return Err(Error::ExecutionOutOfOrder.into());
|
||||
}
|
||||
}
|
||||
match std::mem::replace(&mut execution.state, ExecutionState::Running) {
|
||||
|
|
@ -116,9 +114,7 @@ impl Execution {
|
|||
drop(body);
|
||||
outcome.and_then(|(step, suspended)| {
|
||||
if suspended {
|
||||
Err(PyRuntimeError::new_err(
|
||||
"execution was closed while running",
|
||||
))
|
||||
Err(Error::ExecutionClosedWhileRunning.into())
|
||||
} else {
|
||||
Ok(step)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use pyo3::{
|
|||
types::PyDict,
|
||||
};
|
||||
|
||||
use crate::{HookResume, HookStep, PythonCallEvent, PythonCallHooks, PythonOwned, missing_state};
|
||||
use crate::{Error, HookResume, HookStep, PythonCallEvent, PythonCallHooks, PythonOwned};
|
||||
|
||||
pub(super) enum ChainStep<T> {
|
||||
Ready(T),
|
||||
|
|
@ -110,7 +110,7 @@ impl<H: PythonCallHooks> ChainHooks for HookAdapter<H> {
|
|||
result: PyResult<Py<PyAny>>,
|
||||
) -> PyResult<ChainStep<Py<PyDict>>> {
|
||||
let Some(Continuation::Arguments(resume)) = self.continuation.take() else {
|
||||
return Err(missing_state());
|
||||
return Err(Error::MissingState.into());
|
||||
};
|
||||
let step = resume(&mut self.hooks, py, result)?;
|
||||
Ok(self.step(step, Continuation::Arguments))
|
||||
|
|
@ -132,7 +132,7 @@ impl<H: PythonCallHooks> ChainHooks for HookAdapter<H> {
|
|||
result: PyResult<Py<PyAny>>,
|
||||
) -> PyResult<ChainStep<Box<WireRequest>>> {
|
||||
let Some(Continuation::Wire(resume)) = self.continuation.take() else {
|
||||
return Err(missing_state());
|
||||
return Err(Error::MissingState.into());
|
||||
};
|
||||
let step = resume(&mut self.hooks, py, result)?;
|
||||
Ok(self.step(step, Continuation::Wire))
|
||||
|
|
@ -154,7 +154,7 @@ impl<H: PythonCallHooks> ChainHooks for HookAdapter<H> {
|
|||
result: PyResult<Py<PyAny>>,
|
||||
) -> PyResult<ChainStep<Py<PyAny>>> {
|
||||
let Some(Continuation::Response(resume)) = self.continuation.take() else {
|
||||
return Err(missing_state());
|
||||
return Err(Error::MissingState.into());
|
||||
};
|
||||
let step = resume(&mut self.hooks, py, result)?;
|
||||
Ok(self.step(step, Continuation::Response))
|
||||
|
|
@ -171,7 +171,7 @@ impl<H: PythonCallHooks> ChainHooks for HookAdapter<H> {
|
|||
result: PyResult<Py<PyAny>>,
|
||||
) -> PyResult<ChainStep<()>> {
|
||||
let Some(Continuation::Event(resume)) = self.continuation.take() else {
|
||||
return Err(missing_state());
|
||||
return Err(Error::MissingState.into());
|
||||
};
|
||||
let step = resume(&mut self.hooks, py, result)?;
|
||||
Ok(self.step(step, Continuation::Event))
|
||||
|
|
|
|||
|
|
@ -10,9 +10,7 @@ use pyo3::{
|
|||
types::PyDict,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
HookStep, PythonCallEvent, PythonCallHooks, PythonOwned, PythonRuntime, missing_state,
|
||||
};
|
||||
use crate::{Error, HookStep, PythonCallEvent, PythonCallHooks, PythonOwned, PythonRuntime};
|
||||
|
||||
use super::adapter::{ChainHooks, ChainStep, HookAdapter};
|
||||
|
||||
|
|
@ -72,7 +70,7 @@ impl HookChain {
|
|||
py: Python<'_>,
|
||||
result: PyResult<Py<PyAny>>,
|
||||
) -> PyResult<HookStep<Self, Py<PyDict>>> {
|
||||
let (index, context) = self.arguments.take().ok_or_else(missing_state)?;
|
||||
let (index, context) = self.arguments.take().ok_or(Error::MissingState)?;
|
||||
let result = resume_unless_cancelled(py, result)?;
|
||||
let step = self.hooks[index].resume_arguments(py, result)?;
|
||||
self.arguments_step(py, index, step, context)
|
||||
|
|
@ -113,7 +111,7 @@ impl HookChain {
|
|||
py: Python<'_>,
|
||||
result: PyResult<Py<PyAny>>,
|
||||
) -> PyResult<HookStep<Self, Box<WireRequest>>> {
|
||||
let (index, context) = self.wire.take().ok_or_else(missing_state)?;
|
||||
let (index, context) = self.wire.take().ok_or(Error::MissingState)?;
|
||||
let result = resume_unless_cancelled(py, result)?;
|
||||
let step = self.hooks[index].resume_wire(py, result)?;
|
||||
self.wire_step(py, index, step, &context)
|
||||
|
|
@ -154,7 +152,7 @@ impl HookChain {
|
|||
py: Python<'_>,
|
||||
result: PyResult<Py<PyAny>>,
|
||||
) -> PyResult<HookStep<Self, Py<PyAny>>> {
|
||||
let (index, context) = self.response.take().ok_or_else(missing_state)?;
|
||||
let (index, context) = self.response.take().ok_or(Error::MissingState)?;
|
||||
let result = resume_unless_cancelled(py, result)?;
|
||||
let step = self.hooks[index].resume_response(py, result)?;
|
||||
self.response_step(py, index, step, context)
|
||||
|
|
@ -195,7 +193,7 @@ impl HookChain {
|
|||
py: Python<'_>,
|
||||
result: PyResult<Py<PyAny>>,
|
||||
) -> PyResult<HookStep<Self, ()>> {
|
||||
let (index, event) = self.event.take().ok_or_else(missing_state)?;
|
||||
let (index, event) = self.event.take().ok_or(Error::MissingState)?;
|
||||
let result = resume_unless_cancelled(py, result)?;
|
||||
let result = self.hooks[index].resume_event(py, result);
|
||||
let step = notification_result(py, is_terminal(&event), result)?;
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ pub use argument::{effective_py_args, lookup, present};
|
|||
pub use binding::PythonBinding;
|
||||
pub use conversion_cache::{FromPythonCache, ToPythonCache};
|
||||
pub use driver::{CallOptions, run_call};
|
||||
pub use error::{InvokeError, missing_state};
|
||||
pub use error::{Error, InvokeError};
|
||||
pub use file_reader::{FileContent, PythonFileReader, py_bytes};
|
||||
pub use fork_gate::RuntimeAlreadyStarted;
|
||||
pub use gil::{PythonContext, attach_blocking, release_count, release_gil};
|
||||
|
|
|
|||
|
|
@ -5,11 +5,10 @@ use futures_util::future::{AbortHandle, Abortable};
|
|||
use litellm_host::call::HostedCompletion;
|
||||
use litellm_host::machine::{HostFailure, Machine, MachineStep};
|
||||
use litellm_host::protocol::Protocol;
|
||||
use pyo3::exceptions::PyRuntimeError;
|
||||
use pyo3::prelude::*;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use crate::missing_state;
|
||||
use crate::Error;
|
||||
use crate::runtime::{poll_async_value, run_async_value, run_sync_value};
|
||||
|
||||
type NativeResult<M> = Result<
|
||||
|
|
@ -65,7 +64,7 @@ where
|
|||
py: Python<'_>,
|
||||
interruption: Option<HostFailure<<M::Protocol as Protocol>::Error>>,
|
||||
) -> PyResult<NativePoll<NativeResult<M>>> {
|
||||
let state = Arc::clone(self.state.as_ref().ok_or_else(missing_state)?);
|
||||
let state = Arc::clone(self.state.as_ref().ok_or(Error::MissingState)?);
|
||||
let future = async move {
|
||||
let mut state = state.lock().await;
|
||||
let result = match interruption {
|
||||
|
|
@ -90,7 +89,7 @@ where
|
|||
run_async_value(py, async move {
|
||||
Abortable::new(future, registration)
|
||||
.await
|
||||
.map_err(|_| PyRuntimeError::new_err("native execution closed"))?
|
||||
.map_err(|_| Error::NativeExecutionClosed)?
|
||||
})?
|
||||
.unbind(),
|
||||
))
|
||||
|
|
@ -103,12 +102,12 @@ where
|
|||
pub(super) fn take_result(&self) -> PyResult<NativeResult<M>> {
|
||||
self.state
|
||||
.as_ref()
|
||||
.ok_or_else(missing_state)?
|
||||
.ok_or(Error::MissingState)?
|
||||
.try_lock()
|
||||
.map_err(|_| missing_state())?
|
||||
.map_err(|_| Error::MissingState)?
|
||||
.result
|
||||
.take()
|
||||
.ok_or_else(missing_state)
|
||||
.ok_or_else(|| Error::MissingState.into())
|
||||
.map(|result| {
|
||||
result.map(|step| match step {
|
||||
MachineStep::Suspended(op) => MachineStep::Suspended(op),
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use std::task::{Context, Poll, Waker};
|
|||
use std::time::Duration;
|
||||
|
||||
use crate::fork_gate::{ForkGate, Refused, RuntimeAlreadyStarted};
|
||||
use crate::{Pythonized, panic_to_pyerr, release_gil};
|
||||
use crate::{Error, Pythonized, panic_to_pyerr, release_gil};
|
||||
use futures_util::FutureExt;
|
||||
use pyo3::exceptions::PyRuntimeError;
|
||||
use pyo3::prelude::*;
|
||||
|
|
@ -112,9 +112,7 @@ where
|
|||
F: Future<Output = PyResult<T>> + 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(Error::SyncInsideTokio.into());
|
||||
}
|
||||
release_gil(py, move || runtime.block_on(wait_for_sync_result(future)))?
|
||||
}
|
||||
|
|
@ -131,9 +129,7 @@ where
|
|||
F: Future<Output = Result<T, E>> + 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(Error::SyncInsideTokio.into());
|
||||
}
|
||||
|
||||
let result = release_gil(py, move || runtime.block_on(wait_for_sync_result(future)))?;
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ use litellm_host::{
|
|||
protocol::Reply,
|
||||
};
|
||||
use litellm_host_python::{
|
||||
CallOptions, HookChain, InvokeError, PythonBinding, PythonHostCalls, PythonOwned,
|
||||
PythonRuntime, missing_state, run_call,
|
||||
CallOptions, Error as HostError, HookChain, InvokeError, PythonBinding, PythonHostCalls,
|
||||
PythonOwned, PythonRuntime, run_call,
|
||||
};
|
||||
use pyo3::{
|
||||
exceptions::{
|
||||
|
|
@ -818,7 +818,7 @@ fn cancellation_ends_the_call_without_terminal_dispatch(#[from(interpreter)] _py
|
|||
match chunk {}
|
||||
}
|
||||
fn encode_response(&mut self, _: Python<'_>, _: String) -> PyResult<Py<PyAny>> {
|
||||
Err(missing_state())
|
||||
Err(HostError::MissingState.into())
|
||||
}
|
||||
fn map_error(&self, _: Python<'_>, error: Error) -> PyResult<Classified> {
|
||||
self.0.push("classify");
|
||||
|
|
@ -835,7 +835,7 @@ fn cancellation_ends_the_call_without_terminal_dispatch(#[from(interpreter)] _py
|
|||
_: Python<'_>,
|
||||
_: (&'static str, Reply<String>),
|
||||
) -> Result<(), InvokeError<Error>> {
|
||||
Err(missing_state().into())
|
||||
Err(PyErr::from(HostError::MissingState).into())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@ pub(crate) mod traces;
|
|||
|
||||
use litellm_callbacks_legacy_python::{LegacyLogging, LoggingOperation, PublicCall};
|
||||
use litellm_host::{call::HostedCompletion, machine::Machine, protocol::Protocol};
|
||||
use litellm_host_python::{Error as HostError, present};
|
||||
use litellm_host_python::{
|
||||
HookChain, PythonBinding, PythonCallHooks, PythonHostCalls, effective_py_args,
|
||||
};
|
||||
use litellm_host_python::{missing_state, present};
|
||||
use pyo3::{
|
||||
gc::{PyTraverseError, PyVisit},
|
||||
prelude::*,
|
||||
|
|
@ -64,7 +64,9 @@ impl RequestView {
|
|||
}
|
||||
|
||||
fn dicts(&self) -> PyResult<&RequestDicts> {
|
||||
self.0.as_ref().ok_or_else(missing_state)
|
||||
self.0
|
||||
.as_ref()
|
||||
.ok_or_else(|| HostError::MissingState.into())
|
||||
}
|
||||
|
||||
pub(crate) fn resolve(
|
||||
|
|
@ -72,7 +74,7 @@ impl RequestView {
|
|||
py: Python<'_>,
|
||||
arguments: &Bound<'_, PyDict>,
|
||||
) -> PyResult<()> {
|
||||
let dicts = self.0.as_mut().ok_or_else(missing_state)?;
|
||||
let dicts = self.0.as_mut().ok_or(HostError::MissingState)?;
|
||||
dicts.request = effective_py_args(dicts.base.bind(py), arguments)?.unbind();
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use litellm_auth::ResolvedCredential;
|
||||
use litellm_host_python::{InvokeError, PythonBinding, missing_state};
|
||||
use litellm_host_python::{Error as HostError, InvokeError, PythonBinding};
|
||||
use litellm_host_python::{PythonHostCalls, PythonOwned};
|
||||
use litellm_inference_ocr::route::{Ocr, OcrCall, OcrOp};
|
||||
use litellm_llms::base_llm::ocr::error::Error;
|
||||
|
|
@ -42,7 +42,7 @@ impl OcrPythonHost {
|
|||
fn handles(&self) -> PyResult<&OcrHostHandles> {
|
||||
match &self.data {
|
||||
OcrHostData::Projected(handles) => Ok(handles),
|
||||
_ => Err(missing_state()),
|
||||
_ => Err(HostError::MissingState.into()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -50,13 +50,13 @@ impl OcrPythonHost {
|
|||
self.handles()?
|
||||
.azure_ad_token_provider
|
||||
.as_ref()
|
||||
.ok_or_else(missing_state)?
|
||||
.ok_or(HostError::MissingState)?
|
||||
.acquire(py)
|
||||
}
|
||||
|
||||
fn projection(&mut self, py: Python<'_>, arguments: &Bound<'_, PyDict>) -> PyResult<OcrCall> {
|
||||
let OcrHostData::Unprojected = self.data else {
|
||||
return Err(missing_state());
|
||||
return Err(HostError::MissingState.into());
|
||||
};
|
||||
self.view.resolve(py, arguments)?;
|
||||
let (request, handles) = project_request(self.view.request(py)?, arguments)?;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue