mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-01 02:02:20 +00:00
* feat(rust): add python-compat crate for Python data formats
Add litellm-python-compat, a PyO3-free crate that reproduces the Python
data formats LiteLLM persists, so Rust readers and writers can interoperate
with state written by the Python proxy:
- literal::literal_eval: a linear recursive-descent port of
ast.literal_eval (prefixes, escapes, implicit concatenation, numeric
underscores and radixes, single unary sign, real +/- complex with 3.14
mixed-mode rules, set(), Python-equality key dedup)
- repr::{repr, to_str}: byte-exact repr()/str(), with a printable table
generated from CPython's str.isprintable (Unicode 16.0.0)
- json::{dumps, from_json, to_json}: json.dumps defaults and the
json.loads mapping
- pickle::{loads, dumps}: plain-data pickles via serde-pickle's serde
interface, which keeps dict insertion order
- truthy::truthy: bool() for plain data
Tests replay fixtures generated by CPython 3.14 (values across every
format and pickle protocol 0-5, plus 154 literal_eval source texts).
Accepted divergences are pinned in a KNOWN table that fails once one
starts matching. A criterion bench covers each format and literal_eval
cost by nesting depth, guarding the linear parse: the py_literal grammar
doubled per nested bracket (105 ms at 16 nested dicts; 19 us at 128 now).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* refactor(rust): split python-compat modules and harden the pickle verifier
- Disable class resolution in scripts/verify_rust_pickles.py, and truncate
the export file once instead of removing and appending to it, so the
verifier cannot be pointed at a pre-created file whose rows execute code
through pickle.loads
- Move Error to error.rs and Value to value.rs, leaving lib.rs as the crate
overview, module list and MAX_DEPTH
- Move the generator and verifier to scripts/, beside the Unicode table
generator, leaving tests/ to the Rust tests
- Group the bench by measured surface, give every case a Throughput so
criterion reports bytes per second, and document baseline comparison
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Yujong Lee <yujong@berri.ai>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
39 lines
1.7 KiB
Rust
39 lines
1.7 KiB
Rust
//! Python data formats reproduced in Rust, for state that Python LiteLLM writes and reads.
|
|
//!
|
|
//! Each module mirrors one Python operation over plain data values, and its tests replay
|
|
//! fixtures generated by that operation in CPython (`scripts/generate_fixtures.py`):
|
|
//!
|
|
//! | Module | Python operation |
|
|
//! |---|---|
|
|
//! | [`literal`] | `ast.literal_eval(text)` |
|
|
//! | [`repr`] | `repr(value)` and `str(value)` |
|
|
//! | [`json`] | `json.dumps(value)`, and `json.loads(json.dumps(value))` as a JSON value |
|
|
//! | [`pickle`] | `pickle.loads(data)` and `pickle.dumps(value)` for plain data |
|
|
//! | [`truthy`] | `bool(value)` |
|
|
//!
|
|
//! [`Value`] is the closed data model these formats share. Live Python objects
|
|
//! (descriptors, `__bool__`, `__str__`, callbacks) are out of scope: those belong to the
|
|
//! PyO3 boundary in `litellm-python-bridge`, which runs the real protocol.
|
|
//!
|
|
//! Known limits, each pinned by a test:
|
|
//! - `set` iteration order follows Python's hash order, which this crate does not model
|
|
//! (string hashes are randomized per process). Sets keep their literal order.
|
|
//! - `str` values are Rust `String`s, so lone surrogates cannot be represented.
|
|
//! - [`pickle::loads`] decodes `tuple`, `set`, and `frozenset` as lists.
|
|
|
|
mod error;
|
|
pub mod json;
|
|
pub mod literal;
|
|
pub mod pickle;
|
|
pub mod repr;
|
|
pub mod truthy;
|
|
mod value;
|
|
|
|
pub use error::Error;
|
|
pub use num_bigint::BigInt;
|
|
pub use value::Value;
|
|
|
|
/// Nesting limit for the decoders, which recurse. It keeps untrusted persisted data from
|
|
/// overflowing the Rust stack, and is deliberately stricter than CPython, whose parser takes
|
|
/// about 200 nested brackets and whose unpickler has no limit.
|
|
pub const MAX_DEPTH: usize = 128;
|