use std::ffi::CStr;
use std::future::Future;
use std::pin::Pin;
use std::sync::{Arc, OnceLock};
use std::time::Duration;
use crate::callable::{
NemoFlowCodecDecodeFn, NemoFlowCodecEncodeFn, NemoFlowCollectorCb, NemoFlowEventSubscriberCb,
NemoFlowFinalizerCb, NemoFlowFreeFn, NemoFlowJsonCb, NemoFlowLlmConditionalCb,
NemoFlowLlmExecCb, NemoFlowLlmExecInterceptCb, NemoFlowLlmRequestCb,
NemoFlowLlmRequestInterceptCb, NemoFlowPluginRegisterCb, NemoFlowPluginValidateCb,
NemoFlowToolConditionalCb, NemoFlowToolExecCb, NemoFlowToolExecInterceptCb,
NemoFlowToolSanitizeCb, wrap_codec_fn, wrap_collector_fn, wrap_event_subscriber,
wrap_finalizer_fn, wrap_llm_conditional_fn, wrap_llm_exec_fn, wrap_llm_exec_intercept_fn,
wrap_llm_request_intercept_fn, wrap_llm_response_fn, wrap_llm_sanitize_request_fn,
wrap_llm_stream_exec_fn, wrap_llm_stream_exec_intercept_fn, wrap_tool_conditional_fn,
wrap_tool_exec_fn, wrap_tool_exec_intercept_fn, wrap_tool_request_intercept_fn,
wrap_tool_sanitize_fn,
};
use crate::convert::{
c_str_to_json, c_str_to_opt_json, c_str_to_string, json_to_c_string, nemo_flow_string_free,
str_to_c_string, unix_micros_to_opt_timestamp,
};
use crate::error::{
NemoFlowStatus, clear_last_error, last_error_message, set_last_error, status_from_error,
status_from_plugin_error,
};
use crate::types::{
FfiAtifExporter, FfiAtofExporter, FfiCodecHandle, FfiLLMHandle, FfiOpenInferenceSubscriber,
FfiOpenTelemetrySubscriber, FfiPluginContext, FfiScopeHandle, FfiScopeStack,
FfiThreadScopeStackBinding, FfiToolHandle, NemoFlowScopeType,
};
pub use crate::types::{nemo_flow_openinference_subscriber_free, nemo_flow_otel_subscriber_free};
use libc::c_char;
use nemo_flow::api::llm as core_llm_api;
use nemo_flow::api::llm::{LlmAttributes, LlmRequest};
use nemo_flow::api::registry as core_registry_api;
use nemo_flow::api::runtime::{LlmExecutionNextFn, LlmStreamExecutionNextFn, ToolExecutionNextFn};
use nemo_flow::api::runtime::{
TASK_SCOPE_STACK, capture_thread_scope_stack, create_scope_stack, current_scope_stack,
restore_thread_scope_stack, scope_stack_active, set_thread_scope_stack,
};
use nemo_flow::api::scope as core_scope_api;
use nemo_flow::api::scope::ScopeAttributes;
use nemo_flow::api::subscriber as core_subscriber_api;
use nemo_flow::api::tool as core_tool_api;
use nemo_flow::api::tool::ToolAttributes;
use nemo_flow::error::Result as FlowResult;
use nemo_flow::plugin::{
ConfigDiagnostic, DiagnosticLevel, Plugin, PluginConfig, PluginError,
PluginRegistrationContext, active_plugin_report, clear_plugin_configuration, deregister_plugin,
initialize_plugins, list_plugin_kinds, register_plugin, validate_plugin_config,
};
use nemo_flow_adaptive::plugin_component::register_adaptive_component;
use tokio::runtime::Runtime;
mod llm;
mod llm_registry;
mod observability;
mod plugin;
mod scope;
mod scope_registry;
mod scope_stack;
mod tool_lifecycle;
mod tool_registry;
pub use llm::*;
pub use llm_registry::*;
pub use observability::*;
pub use plugin::*;
pub use scope::*;
pub use scope_registry::*;
pub use scope_stack::*;
pub use tool_lifecycle::*;
pub use tool_registry::*;
fn tokio_runtime() -> &'static Runtime {
static RT: OnceLock<Runtime> = OnceLock::new();
RT.get_or_init(|| {
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.expect("Failed to create tokio runtime")
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn nemo_flow_tool_request_intercepts(
name: *const c_char,
args_json: *const c_char,
out: *mut *mut c_char,
) -> NemoFlowStatus {
clear_last_error();
let name = match c_str_to_string(name) {
Ok(s) => s,
Err(status) => return status,
};
let args = match c_str_to_json(args_json) {
Some(a) => a,
None => return NemoFlowStatus::InvalidJson,
};
match core_tool_api::tool_request_intercepts(&name, args) {
Ok(result) => {
unsafe { *out = json_to_c_string(&result) };
NemoFlowStatus::Ok
}
Err(e) => status_from_error(&e),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn nemo_flow_tool_conditional_execution(
name: *const c_char,
args_json: *const c_char,
) -> NemoFlowStatus {
clear_last_error();
let name = match c_str_to_string(name) {
Ok(s) => s,
Err(status) => return status,
};
let args = match c_str_to_json(args_json) {
Some(a) => a,
None => return NemoFlowStatus::InvalidJson,
};
match core_tool_api::tool_conditional_execution(&name, &args) {
Ok(()) => NemoFlowStatus::Ok,
Err(e) => status_from_error(&e),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn nemo_flow_llm_request_intercepts(
name: *const c_char,
native_json: *const c_char,
out: *mut *mut c_char,
) -> NemoFlowStatus {
clear_last_error();
let name_str = if name.is_null() {
""
} else {
unsafe { CStr::from_ptr(name) }.to_str().unwrap_or_default()
};
let native = match c_str_to_json(native_json) {
Some(j) => j,
None => return NemoFlowStatus::InvalidJson,
};
let request: LlmRequest = match serde_json::from_value(native) {
Ok(r) => r,
Err(_) => {
set_last_error("failed to parse native_json as LlmRequest");
return NemoFlowStatus::InvalidJson;
}
};
match core_llm_api::llm_request_intercepts(name_str, request) {
Ok(transformed) => {
let result_json = serde_json::to_value(&transformed).unwrap_or(serde_json::Value::Null);
unsafe { *out = json_to_c_string(&result_json) };
NemoFlowStatus::Ok
}
Err(e) => status_from_error(&e),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn nemo_flow_llm_conditional_execution(
native_json: *const c_char,
) -> NemoFlowStatus {
clear_last_error();
let native = match c_str_to_json(native_json) {
Some(j) => j,
None => return NemoFlowStatus::InvalidJson,
};
let request: LlmRequest = match serde_json::from_value(native) {
Ok(r) => r,
Err(_) => {
set_last_error("failed to parse native_json as LlmRequest");
return NemoFlowStatus::InvalidJson;
}
};
match core_llm_api::llm_conditional_execution(&request) {
Ok(()) => NemoFlowStatus::Ok,
Err(e) => status_from_error(&e),
}
}
#[cfg(test)]
#[path = "../../tests/unit/api_tests.rs"]
mod tests;