mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-19 04:54:37 +00:00
* refactor(rust-bridge): return an unstarted Execution from native entrypoints and drive it in Python Rust's run_call built Python streams itself and imported litellm.rust_bridge.streams by name, so sync calls were driven in Rust while async calls came back as a Rust-made coroutine. Every native inference entrypoint now returns one unstarted Execution and public_execution.py drives it with lifecycle.run or lifecycle.drive, wrapping streams and _hidden_params on the Python side. Rust only imports the lifecycle step classes through StepTypes, and python-bridge/src/lifecycle.rs, PythonLifecycle and streams.py are gone. The adapter trusts the _native.pyi contract with a single cast instead of a per-request runtime_checkable isinstance. Binding tests assert the adapter starts the execution and returns its completed value rather than checking callable identity. * refactor(rust-bridge): fold lifecycle and public_execution into one execution module The public stream wrappers only forwarded to the lifecycle streams and added the header dict, so the streams now carry _hidden_params themselves. lifecycle.py no longer held any call lifecycle, just the Execution driver, so it is renamed to execution.py and absorbs the synchronous and asynchronous adapters Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(rust-bridge): keep execution.py free of third-party imports The host-python tests load execution.py into a bare interpreter, so the pydantic header validation broke them. The stream head comes from the binding's encode_stream_head, which always builds a str-keyed dict Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(host-python): preserve pre-async hook coverage after execution handoff * style(rust-bridge): sort litellm_host_python imports Co-Authored-By: Yujong Lee <yujong@berri.ai> * test(rust-bridge): drive unstarted native calls through execution in the split lifecycle tests Co-Authored-By: Yujong Lee <yujong@berri.ai> * fix(rust-bridge): mark execution TypeVar as Final Co-Authored-By: Yujong Lee <yujong@berri.ai> * test(rust-bridge): cover stream close, read failure, and sync suspension in execution 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>
58 lines
2 KiB
Rust
58 lines
2 KiB
Rust
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("can't send non-None value to a just-started execution")]
|
|
SendBeforeStart,
|
|
#[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("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::SendBeforeStart | Error::FileContentType { .. } => {
|
|
PyTypeError::new_err(error.to_string())
|
|
}
|
|
Error::MissingState
|
|
| Error::ExecutionRunning
|
|
| Error::ExecutionClosed
|
|
| Error::ExecutionOutOfOrder
|
|
| Error::ExecutionClosedWhileRunning
|
|
| Error::NativeExecutionClosed
|
|
| 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, thiserror::Error)]
|
|
pub enum InvokeError<E> {
|
|
#[error("{0}")]
|
|
Native(E),
|
|
#[error(transparent)]
|
|
Python(#[from] PyErr),
|
|
}
|