mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-11 03:38:38 +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>
53 lines
1.3 KiB
Rust
53 lines
1.3 KiB
Rust
use num_bigint::BigInt;
|
|
|
|
/// A Python value built only from literals: what `ast.literal_eval` can return.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub enum Value {
|
|
None,
|
|
Bool(bool),
|
|
Int(BigInt),
|
|
Float(f64),
|
|
Complex {
|
|
re: f64,
|
|
im: f64,
|
|
},
|
|
Str(String),
|
|
Bytes(Vec<u8>),
|
|
Tuple(Vec<Value>),
|
|
List(Vec<Value>),
|
|
/// Insertion-ordered, with Python's key equality already applied.
|
|
Dict(Vec<(Value, Value)>),
|
|
/// Literal order, with Python's member equality already applied.
|
|
Set(Vec<Value>),
|
|
}
|
|
|
|
impl Value {
|
|
/// Python's type name, as it appears in `TypeError` messages.
|
|
pub fn type_name(&self) -> &'static str {
|
|
match self {
|
|
Value::None => "NoneType",
|
|
Value::Bool(_) => "bool",
|
|
Value::Int(_) => "int",
|
|
Value::Float(_) => "float",
|
|
Value::Complex { .. } => "complex",
|
|
Value::Str(_) => "str",
|
|
Value::Bytes(_) => "bytes",
|
|
Value::Tuple(_) => "tuple",
|
|
Value::List(_) => "list",
|
|
Value::Dict(_) => "dict",
|
|
Value::Set(_) => "set",
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<i64> for Value {
|
|
fn from(value: i64) -> Self {
|
|
Value::Int(value.into())
|
|
}
|
|
}
|
|
|
|
impl From<&str> for Value {
|
|
fn from(value: &str) -> Self {
|
|
Value::Str(value.to_owned())
|
|
}
|
|
}
|