mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-09-26 19:45:00 +02:00
swtich-to-po3
This commit is contained in:
Generated
+124
@@ -2,6 +2,12 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.11.0"
|
||||
@@ -14,6 +20,21 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "indoc"
|
||||
version = "2.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
|
||||
dependencies = [
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.17"
|
||||
@@ -26,6 +47,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"parking_lot",
|
||||
"pyo3",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
@@ -52,6 +74,21 @@ version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.5"
|
||||
@@ -81,6 +118,12 @@ version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
@@ -90,6 +133,69 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyo3"
|
||||
version = "0.23.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7778bffd85cf38175ac1f545509665d0b9b92a198ca7941f131f85f7a4f9a872"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"indoc",
|
||||
"libc",
|
||||
"memoffset",
|
||||
"once_cell",
|
||||
"portable-atomic",
|
||||
"pyo3-build-config",
|
||||
"pyo3-ffi",
|
||||
"pyo3-macros",
|
||||
"unindent",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-build-config"
|
||||
version = "0.23.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94f6cbe86ef3bf18998d9df6e0f3fc1050a8c5efa409bf712e661a4366e010fb"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"target-lexicon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-ffi"
|
||||
version = "0.23.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9f1b4c431c0bb1c8fb0a338709859eed0d030ff6daa34368d3b152a63dfdd8d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"pyo3-build-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-macros"
|
||||
version = "0.23.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbc2201328f63c4710f68abdf653c89d8dbc2858b88c5d88b0ff38a75288a9da"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"pyo3-macros-backend",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-macros-backend"
|
||||
version = "0.23.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fca6726ad0f3da9c9de093d6f116a93c1a38e417ed73bf138472cf4064f72028"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"pyo3-build-config",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.45"
|
||||
@@ -108,6 +214,12 @@ dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
@@ -174,6 +286,12 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "target-lexicon"
|
||||
version = "0.12.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.50.0"
|
||||
@@ -201,6 +319,12 @@ version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "unindent"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
|
||||
@@ -7,7 +7,12 @@ edition = "2021"
|
||||
name = "langgraph_rust_core"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[features]
|
||||
default = []
|
||||
python-bindings = ["dep:pyo3"]
|
||||
|
||||
[dependencies]
|
||||
pyo3 = { version = "0.23.5", features = ["extension-module"], optional = true }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
parking_lot = "0.12"
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
mod engine;
|
||||
mod lib_c;
|
||||
#[cfg(feature = "python-bindings")]
|
||||
mod lib_py;
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
#[cfg(feature = "python-bindings")]
|
||||
use crate::engine::{
|
||||
parse_callback_envelope_json, run_graph_json_with_callback, run_loop_block_on, AnyOfCondition,
|
||||
Engine, NodeOutcome, WaitCondition,
|
||||
};
|
||||
#[cfg(feature = "python-bindings")]
|
||||
use pyo3::exceptions::PyValueError;
|
||||
#[cfg(feature = "python-bindings")]
|
||||
use pyo3::prelude::*;
|
||||
#[cfg(feature = "python-bindings")]
|
||||
use pyo3::types::PyAny;
|
||||
#[cfg(feature = "python-bindings")]
|
||||
use serde_json::Value;
|
||||
|
||||
#[cfg(feature = "python-bindings")]
|
||||
#[pyclass]
|
||||
struct PyRustEngine {
|
||||
inner: Engine,
|
||||
}
|
||||
|
||||
#[cfg(feature = "python-bindings")]
|
||||
#[pymethods]
|
||||
impl PyRustEngine {
|
||||
#[new]
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
inner: Engine::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn add_async_channel(&self, name: &str) {
|
||||
self.inner.add_async_channel(name);
|
||||
}
|
||||
|
||||
fn publish_json(&self, channel: &str, value_json: &str) -> PyResult<()> {
|
||||
let value: Value = serde_json::from_str(value_json)
|
||||
.map_err(|e| PyValueError::new_err(format!("Invalid JSON value: {e}")))?;
|
||||
self.inner
|
||||
.publish_json(channel, value)
|
||||
.map_err(PyValueError::new_err)
|
||||
}
|
||||
|
||||
fn publish_obj(&self, py: Python<'_>, channel: &str, value: Py<PyAny>) -> PyResult<()> {
|
||||
let value_json = py_obj_to_json_string(py, &value.bind(py))?;
|
||||
self.publish_json(channel, &value_json)
|
||||
}
|
||||
|
||||
fn wait_any_of_json(&self, any_of_json: &str) -> PyResult<String> {
|
||||
let any_of: AnyOfCondition = serde_json::from_str(any_of_json)
|
||||
.map_err(|e| PyValueError::new_err(format!("Invalid any_of JSON: {e}")))?;
|
||||
let event = run_loop_block_on(self.inner.wait_for_any_of_async(&any_of))
|
||||
.map_err(PyValueError::new_err)?;
|
||||
serde_json::to_string(&event)
|
||||
.map_err(|e| PyValueError::new_err(format!("Serialize event failed: {e}")))
|
||||
}
|
||||
|
||||
fn wait_channel(&self, py: Python<'_>, channel: &str, n: usize) -> PyResult<Py<PyAny>> {
|
||||
let cond = WaitCondition::Channel {
|
||||
channel: channel.to_string(),
|
||||
n,
|
||||
};
|
||||
let event =
|
||||
run_loop_block_on(self.inner.wait_for_async(&cond)).map_err(PyValueError::new_err)?;
|
||||
let event_json = serde_json::to_string(&event)
|
||||
.map_err(|e| PyValueError::new_err(format!("Serialize event failed: {e}")))?;
|
||||
json_string_to_py_obj(py, &event_json)
|
||||
}
|
||||
|
||||
fn wait_timer(&self, py: Python<'_>, seconds: f64) -> PyResult<Py<PyAny>> {
|
||||
let cond = WaitCondition::Timer { seconds };
|
||||
let event =
|
||||
run_loop_block_on(self.inner.wait_for_async(&cond)).map_err(PyValueError::new_err)?;
|
||||
let event_json = serde_json::to_string(&event)
|
||||
.map_err(|e| PyValueError::new_err(format!("Serialize event failed: {e}")))?;
|
||||
json_string_to_py_obj(py, &event_json)
|
||||
}
|
||||
|
||||
fn wait_any_of_obj(&self, py: Python<'_>, any_of_payload: Py<PyAny>) -> PyResult<Py<PyAny>> {
|
||||
let payload_json = py_obj_to_json_string(py, &any_of_payload.bind(py))?;
|
||||
let event_json = self.wait_any_of_json(&payload_json)?;
|
||||
json_string_to_py_obj(py, &event_json)
|
||||
}
|
||||
|
||||
fn wait_condition_json(&self, cond_json: &str) -> PyResult<String> {
|
||||
let cond: WaitCondition = serde_json::from_str(cond_json)
|
||||
.map_err(|e| PyValueError::new_err(format!("Invalid condition JSON: {e}")))?;
|
||||
let event =
|
||||
run_loop_block_on(self.inner.wait_for_async(&cond)).map_err(PyValueError::new_err)?;
|
||||
serde_json::to_string(&event)
|
||||
.map_err(|e| PyValueError::new_err(format!("Serialize event failed: {e}")))
|
||||
}
|
||||
|
||||
#[pyo3(signature = (stream_mode=None))]
|
||||
fn start_stream(&self, stream_mode: Option<&str>) -> PyResult<()> {
|
||||
self.inner
|
||||
.start_stream(stream_mode)
|
||||
.map_err(PyValueError::new_err)
|
||||
}
|
||||
|
||||
fn receive_stream_obj(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||||
match run_loop_block_on(self.inner.receive_stream_async()) {
|
||||
Some(value) => {
|
||||
let event_json = serde_json::to_string(&value)
|
||||
.map_err(|e| PyValueError::new_err(format!("Serialize event failed: {e}")))?;
|
||||
json_string_to_py_obj(py, &event_json)
|
||||
}
|
||||
None => Ok(py.None()),
|
||||
}
|
||||
}
|
||||
|
||||
fn send_custom_stream_event_obj(&self, py: Python<'_>, value: Py<PyAny>) -> PyResult<()> {
|
||||
let value_json = py_obj_to_json_string(py, &value.bind(py))?;
|
||||
let parsed: Value = serde_json::from_str(&value_json)
|
||||
.map_err(|e| PyValueError::new_err(format!("Invalid Python JSON value: {e}")))?;
|
||||
self.inner.send_custom_stream_event(parsed);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn close_stream(&self) {
|
||||
self.inner.close_stream();
|
||||
}
|
||||
|
||||
#[pyo3(signature = (entry_point, finish_point, initial_state, callback, stream_mode=None))]
|
||||
fn run_graph_py(
|
||||
&self,
|
||||
py: Python<'_>,
|
||||
entry_point: &str,
|
||||
finish_point: &str,
|
||||
initial_state: Py<PyAny>,
|
||||
callback: Py<PyAny>,
|
||||
stream_mode: Option<&str>,
|
||||
) -> PyResult<Py<PyAny>> {
|
||||
let state_json = py_obj_to_json_string(py, &initial_state.bind(py))?;
|
||||
let initial_state_value: Value = serde_json::from_str(&state_json)
|
||||
.map_err(|e| PyValueError::new_err(format!("Invalid initial state: {e}")))?;
|
||||
|
||||
self.inner
|
||||
.start_stream(stream_mode)
|
||||
.map_err(PyValueError::new_err)?;
|
||||
|
||||
let callback_arc = std::sync::Arc::new(callback);
|
||||
let run_result = run_loop_block_on(run_graph_json_with_callback(
|
||||
entry_point.to_string(),
|
||||
finish_point.to_string(),
|
||||
initial_state_value.clone(),
|
||||
initial_state_value,
|
||||
self.inner.clone(),
|
||||
move |node: String,
|
||||
arg: Value,
|
||||
state_snapshot: Value|
|
||||
-> Result<NodeOutcome<Value, Value>, String> {
|
||||
Python::with_gil(|py| -> Result<NodeOutcome<Value, Value>, String> {
|
||||
let callback_bound = callback_arc.as_ref().bind(py);
|
||||
let arg_json = serde_json::to_string(&arg)
|
||||
.map_err(|e| format!("serialize arg failed: {e}"))?;
|
||||
let state_json = serde_json::to_string(&state_snapshot)
|
||||
.map_err(|e| format!("serialize state failed: {e}"))?;
|
||||
let arg_obj = json_string_to_py_obj(py, &arg_json)
|
||||
.map_err(|e| format!("decode arg failed: {e}"))?;
|
||||
let state_obj = json_string_to_py_obj(py, &state_json)
|
||||
.map_err(|e| format!("decode state failed: {e}"))?;
|
||||
let payload_obj = callback_bound
|
||||
.call1((node.as_str(), arg_obj, state_obj))
|
||||
.map_err(|e| format!("callback failed for `{node}`: {e}"))?;
|
||||
let payload_json = py_obj_to_json_string(py, &payload_obj)
|
||||
.map_err(|e| format!("serialize callback payload failed: {e}"))?;
|
||||
let payload_value: Value = serde_json::from_str(&payload_json)
|
||||
.map_err(|e| format!("decode callback payload failed: {e}"))?;
|
||||
let envelope = serde_json::json!({
|
||||
"ok": true,
|
||||
"payload": {
|
||||
"update": payload_value
|
||||
.get("update")
|
||||
.cloned()
|
||||
.unwrap_or(Value::Null),
|
||||
"sends": payload_value
|
||||
.get("sends")
|
||||
.cloned()
|
||||
.unwrap_or(Value::Array(vec![])),
|
||||
},
|
||||
"suspend": payload_value.get("suspend").cloned(),
|
||||
});
|
||||
parse_callback_envelope_json(&envelope.to_string(), &node)
|
||||
})
|
||||
},
|
||||
));
|
||||
self.inner.close_stream();
|
||||
|
||||
let out = run_result.map_err(PyValueError::new_err)?;
|
||||
let out_json = serde_json::to_string(&out)
|
||||
.map_err(|e| PyValueError::new_err(format!("Serialize state failed: {e}")))?;
|
||||
json_string_to_py_obj(py, &out_json)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "python-bindings")]
|
||||
fn py_obj_to_json_string(py: Python<'_>, obj: &Bound<'_, PyAny>) -> PyResult<String> {
|
||||
let json_mod = py.import("json")?;
|
||||
let dumped = json_mod.call_method1("dumps", (obj,))?;
|
||||
dumped.extract::<String>()
|
||||
}
|
||||
|
||||
#[cfg(feature = "python-bindings")]
|
||||
fn json_string_to_py_obj(py: Python<'_>, value: &str) -> PyResult<Py<PyAny>> {
|
||||
let json_mod = py.import("json")?;
|
||||
let loaded = json_mod.call_method1("loads", (value,))?;
|
||||
Ok(loaded.unbind())
|
||||
}
|
||||
|
||||
#[cfg(feature = "python-bindings")]
|
||||
#[pymodule]
|
||||
fn langgraph_rust_core(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
m.add_class::<PyRustEngine>()?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -8,6 +8,10 @@ name = "langgraph_rust_core"
|
||||
path = "../rust-core/src/lib.rs"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[features]
|
||||
default = []
|
||||
python-bindings = []
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
||||
Reference in New Issue
Block a user