MCPcopy Create free account
hub / github.com/NVIDIA/NeMo-Relay / wrap_py_tool_exec_fn

Function wrap_py_tool_exec_fn

crates/python/src/py_callable.rs:259–306  ·  view source on GitHub ↗

Wrap a Python callable `(Json) -> Json` for tool execution intercepts. Supports both sync and async Python callables. If the callable returns a coroutine, it is awaited via the pyo3-async-runtimes bridge.

(
    py_fn: Py<PyAny>,
)

Source from the content-addressed store, hash-verified

257/// Supports both sync and async Python callables. If the callable returns a
258/// coroutine, it is awaited via the pyo3-async-runtimes bridge.
259pub fn wrap_py_tool_exec_fn(
260 py_fn: Py<PyAny>,
261) -> Box<dyn Fn(Json) -> Pin<Box<dyn Future<Output = FlowResult<Json>> + Send>> + Send + Sync> {
262 let py_fn = std::sync::Arc::new(py_fn);
263 Box::new(move |args: Json| {
264 let py_fn = py_fn.clone();
265 Box::pin(async move {
266 // Call the Python function and check if it returns a coroutine
267 let outcome: FlowResult<
268 Result<Json, Pin<Box<dyn Future<Output = PyResult<Py<PyAny>>> + Send>>>,
269 > = Python::attach(|py| {
270 let py_args =
271 json_to_py(py, &args).map_err(|e: PyErr| FlowError::Internal(e.to_string()))?;
272 let result = py_fn
273 .call1(py, (py_args,))
274 .map_err(|e: PyErr| FlowError::Internal(e.to_string()))?;
275
276 // Detect coroutine by checking for __await__
277 let bound = result.bind(py);
278 if bound.getattr("__await__").is_ok() {
279 let future = pyo3_async_runtimes::tokio::into_future(result.into_bound(py))
280 .map_err(|e| FlowError::Internal(e.to_string()))?;
281 Ok(Err(Box::pin(future)
282 as Pin<
283 Box<dyn Future<Output = PyResult<Py<PyAny>>> + Send>,
284 >))
285 } else {
286 let json =
287 py_to_json(bound).map_err(|e: PyErr| FlowError::Internal(e.to_string()))?;
288 Ok(Ok(json))
289 }
290 });
291
292 match outcome? {
293 Ok(json) => Ok(json),
294 Err(future) => {
295 let py_result = future
296 .await
297 .map_err(|e| FlowError::Internal(e.to_string()))?;
298 Python::attach(|py| {
299 py_to_json(py_result.bind(py))
300 .map_err(|e: PyErr| FlowError::Internal(e.to_string()))
301 })
302 }
303 }
304 })
305 })
306}
307
308/// Python-callable wrapper for the Rust `ToolExecutionNextFn`.
309///

Calls 2

json_to_pyFunction · 0.85
py_to_jsonFunction · 0.85