Skip to main content

nemo_flow_ffi/types/
mod.rs

1// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4//! C-compatible types exposed through the FFI boundary.
5//!
6//! This module defines opaque handle wrappers, enumerations, accessor functions,
7//! and free functions for all types that cross the C FFI boundary. Each opaque
8//! struct wraps a corresponding core type and is heap-allocated; the C consumer
9//! sees only an opaque pointer. All returned C strings must be freed with
10//! [`crate::convert::nemo_flow_string_free`], and all handles must be freed
11//! with their corresponding `nemo_flow_*_free` function.
12
13use libc::c_char;
14use nemo_flow::api::runtime::{ScopeStackHandle, ThreadScopeStackBinding};
15use nemo_flow::plugin::PluginRegistrationContext;
16use serde_json::Value as Json;
17
18use nemo_flow::api::event::Event;
19#[cfg(test)]
20use nemo_flow::api::llm::LlmAttributes;
21use nemo_flow::api::llm::{LlmHandle, LlmRequest};
22#[cfg(test)]
23use nemo_flow::api::scope::ScopeAttributes;
24use nemo_flow::api::scope::{ScopeHandle, ScopeType};
25#[cfg(test)]
26use nemo_flow::api::tool::ToolAttributes;
27use nemo_flow::api::tool::ToolHandle;
28use nemo_flow::codec::traits::{LlmCodec, LlmResponseCodec};
29
30use crate::convert::{json_to_c_string, str_to_c_string};
31#[cfg(test)]
32use crate::{api, convert};
33
34// ---------------------------------------------------------------------------
35// Opaque handle wrappers — each wraps a core type in a Box on the heap.
36// The C consumer sees only `*mut FfiScopeHandle` etc.
37// ---------------------------------------------------------------------------
38
39/// Opaque handle representing an active execution scope.
40pub struct FfiScopeHandle(pub ScopeHandle);
41/// Opaque handle representing an active tool call.
42pub struct FfiToolHandle(pub ToolHandle);
43/// Opaque handle representing an active LLM call.
44pub struct FfiLLMHandle(pub LlmHandle);
45/// Opaque wrapper around an LLM request (headers, content).
46pub struct FfiLLMRequest(pub LlmRequest);
47/// Opaque wrapper around a lifecycle event emitted by the runtime.
48pub struct FfiEvent(pub Event);
49/// Opaque handle to an isolated scope stack for per-request/per-task isolation.
50pub struct FfiScopeStack(pub ScopeStackHandle);
51/// Opaque handle to a captured thread-local scope stack binding.
52pub struct FfiThreadScopeStackBinding(pub ThreadScopeStackBinding);
53/// Opaque ATIF exporter handle.
54pub struct FfiAtifExporter(pub nemo_flow::observability::atif::AtifExporter);
55/// Opaque ATOF JSONL exporter handle.
56pub struct FfiAtofExporter(pub nemo_flow::observability::atof::AtofExporter);
57/// Opaque OpenTelemetry subscriber handle.
58pub struct FfiOpenTelemetrySubscriber(pub nemo_flow::observability::otel::OpenTelemetrySubscriber);
59/// Opaque OpenInference subscriber handle.
60pub struct FfiOpenInferenceSubscriber(
61    pub nemo_flow::observability::openinference::OpenInferenceSubscriber,
62);
63/// Opaque plugin registration context.
64///
65/// This wrapper contains a borrowed raw pointer to an
66/// `nemo_flow::plugin::PluginRegistrationContext`, not an owned heap allocation.
67/// It is only valid for the duration of the plugin registration callback that receives
68/// it. C callers must not store the pointer, use it after the callback returns, or attempt to
69/// free or drop it.
70///
71/// There is intentionally no `nemo_flow_plugin_context_free` function because this FFI
72/// wrapper does not own the underlying registration context.
73pub struct FfiPluginContext(pub *mut PluginRegistrationContext);
74
75/// Opaque handle carrying both request and response codec trait objects.
76///
77/// Created by `nemo_flow_openai_chat_codec_new` (and similar constructors).
78/// Freed by `nemo_flow_codec_free`. The handle carries two `Arc`s pointing
79/// to the same underlying codec instance: one for the `LlmCodec` trait and
80/// one for the `LlmResponseCodec` trait.
81pub struct FfiCodecHandle {
82    #[allow(dead_code)]
83    pub(crate) codec: std::sync::Arc<dyn LlmCodec>,
84    pub(crate) response_codec: std::sync::Arc<dyn LlmResponseCodec>,
85}
86
87// ---------------------------------------------------------------------------
88// Enums exposed to C
89// ---------------------------------------------------------------------------
90
91/// The type of scope in the agent execution hierarchy.
92#[repr(i32)]
93#[derive(Debug, Clone, Copy)]
94pub enum NemoFlowScopeType {
95    /// Top-level agent scope.
96    Agent = 0,
97    /// Generic function scope.
98    Function = 1,
99    /// Tool invocation scope.
100    Tool = 2,
101    /// LLM call scope.
102    Llm = 3,
103    /// Retriever scope (e.g., RAG lookup).
104    Retriever = 4,
105    /// Embedder scope.
106    Embedder = 5,
107    /// Reranker scope.
108    Reranker = 6,
109    /// Guardrail evaluation scope.
110    Guardrail = 7,
111    /// Evaluator scope.
112    Evaluator = 8,
113    /// User-defined custom scope.
114    Custom = 9,
115    /// Unknown or unspecified scope type.
116    Unknown = 10,
117}
118
119impl From<NemoFlowScopeType> for ScopeType {
120    fn from(v: NemoFlowScopeType) -> Self {
121        match v {
122            NemoFlowScopeType::Agent => ScopeType::Agent,
123            NemoFlowScopeType::Function => ScopeType::Function,
124            NemoFlowScopeType::Tool => ScopeType::Tool,
125            NemoFlowScopeType::Llm => ScopeType::Llm,
126            NemoFlowScopeType::Retriever => ScopeType::Retriever,
127            NemoFlowScopeType::Embedder => ScopeType::Embedder,
128            NemoFlowScopeType::Reranker => ScopeType::Reranker,
129            NemoFlowScopeType::Guardrail => ScopeType::Guardrail,
130            NemoFlowScopeType::Evaluator => ScopeType::Evaluator,
131            NemoFlowScopeType::Custom => ScopeType::Custom,
132            NemoFlowScopeType::Unknown => ScopeType::Unknown,
133        }
134    }
135}
136
137impl From<ScopeType> for NemoFlowScopeType {
138    fn from(v: ScopeType) -> Self {
139        match v {
140            ScopeType::Agent => NemoFlowScopeType::Agent,
141            ScopeType::Function => NemoFlowScopeType::Function,
142            ScopeType::Tool => NemoFlowScopeType::Tool,
143            ScopeType::Llm => NemoFlowScopeType::Llm,
144            ScopeType::Retriever => NemoFlowScopeType::Retriever,
145            ScopeType::Embedder => NemoFlowScopeType::Embedder,
146            ScopeType::Reranker => NemoFlowScopeType::Reranker,
147            ScopeType::Guardrail => NemoFlowScopeType::Guardrail,
148            ScopeType::Evaluator => NemoFlowScopeType::Evaluator,
149            ScopeType::Custom => NemoFlowScopeType::Custom,
150            ScopeType::Unknown => NemoFlowScopeType::Unknown,
151        }
152    }
153}
154
155// ---------------------------------------------------------------------------
156// Free functions for opaque handles
157// ---------------------------------------------------------------------------
158
159/// Free a scope handle previously returned by the runtime.
160///
161/// # Safety
162/// `ptr` must be a valid pointer returned by an `nemo_flow_*` function, or null.
163#[unsafe(no_mangle)]
164pub unsafe extern "C" fn nemo_flow_scope_handle_free(ptr: *mut FfiScopeHandle) {
165    if !ptr.is_null() {
166        drop(unsafe { Box::from_raw(ptr) });
167    }
168}
169
170/// Free a tool handle previously returned by the runtime.
171///
172/// # Safety
173/// `ptr` must be a valid pointer returned by an `nemo_flow_*` function, or null.
174#[unsafe(no_mangle)]
175pub unsafe extern "C" fn nemo_flow_tool_handle_free(ptr: *mut FfiToolHandle) {
176    if !ptr.is_null() {
177        drop(unsafe { Box::from_raw(ptr) });
178    }
179}
180
181/// Free an LLM handle previously returned by the runtime.
182///
183/// # Safety
184/// `ptr` must be a valid pointer returned by an `nemo_flow_*` function, or null.
185#[unsafe(no_mangle)]
186pub unsafe extern "C" fn nemo_flow_llm_handle_free(ptr: *mut FfiLLMHandle) {
187    if !ptr.is_null() {
188        drop(unsafe { Box::from_raw(ptr) });
189    }
190}
191
192/// Free an LLM request object.
193///
194/// # Safety
195/// `ptr` must be a valid pointer returned by an `nemo_flow_*` function, or null.
196#[unsafe(no_mangle)]
197pub unsafe extern "C" fn nemo_flow_llm_request_free(ptr: *mut FfiLLMRequest) {
198    if !ptr.is_null() {
199        drop(unsafe { Box::from_raw(ptr) });
200    }
201}
202
203/// Free an event object.
204///
205/// # Safety
206/// `ptr` must be a valid pointer returned by an `nemo_flow_*` function, or null.
207#[unsafe(no_mangle)]
208pub unsafe extern "C" fn nemo_flow_event_free(ptr: *mut FfiEvent) {
209    if !ptr.is_null() {
210        drop(unsafe { Box::from_raw(ptr) });
211    }
212}
213
214/// Free a scope stack handle previously returned by `nemo_flow_scope_stack_create`.
215///
216/// # Safety
217/// `ptr` must be a valid pointer returned by `nemo_flow_scope_stack_create`, or null.
218#[unsafe(no_mangle)]
219pub unsafe extern "C" fn nemo_flow_scope_stack_free(ptr: *mut FfiScopeStack) {
220    if !ptr.is_null() {
221        drop(unsafe { Box::from_raw(ptr) });
222    }
223}
224
225/// Free an ATIF exporter handle previously returned by `nemo_flow_atif_exporter_create`.
226///
227/// # Safety
228/// `ptr` must be a valid pointer returned by `nemo_flow_atif_exporter_create`, or null.
229#[unsafe(no_mangle)]
230pub unsafe extern "C" fn nemo_flow_atif_exporter_free(ptr: *mut FfiAtifExporter) {
231    if !ptr.is_null() {
232        drop(unsafe { Box::from_raw(ptr) });
233    }
234}
235
236/// Free an ATOF JSONL exporter handle previously returned by `nemo_flow_atof_exporter_create`.
237///
238/// # Safety
239/// `ptr` must be a valid pointer returned by `nemo_flow_atof_exporter_create`, or null.
240#[unsafe(no_mangle)]
241pub unsafe extern "C" fn nemo_flow_atof_exporter_free(ptr: *mut FfiAtofExporter) {
242    if !ptr.is_null() {
243        drop(unsafe { Box::from_raw(ptr) });
244    }
245}
246
247/// Free an OpenTelemetry subscriber handle previously returned by
248/// `nemo_flow_otel_subscriber_create`.
249///
250/// # Safety
251/// `ptr` must be a valid pointer returned by `nemo_flow_otel_subscriber_create`, or null.
252#[unsafe(no_mangle)]
253pub unsafe extern "C" fn nemo_flow_otel_subscriber_free(ptr: *mut FfiOpenTelemetrySubscriber) {
254    if !ptr.is_null() {
255        drop(unsafe { Box::from_raw(ptr) });
256    }
257}
258
259/// Free an OpenInference subscriber handle previously returned by
260/// `nemo_flow_openinference_subscriber_create`.
261///
262/// # Safety
263/// `ptr` must be a valid pointer returned by
264/// `nemo_flow_openinference_subscriber_create`, or null.
265#[unsafe(no_mangle)]
266pub unsafe extern "C" fn nemo_flow_openinference_subscriber_free(
267    ptr: *mut FfiOpenInferenceSubscriber,
268) {
269    if !ptr.is_null() {
270        drop(unsafe { Box::from_raw(ptr) });
271    }
272}
273
274/// Free a codec handle previously returned by one of the codec constructor
275/// functions (`nemo_flow_openai_chat_codec_new`, etc.).
276///
277/// # Safety
278/// `handle` must be a valid pointer returned by one of the codec constructor
279/// functions, or null. Double-free is undefined behavior.
280#[unsafe(no_mangle)]
281pub unsafe extern "C" fn nemo_flow_codec_free(handle: *mut FfiCodecHandle) {
282    if !handle.is_null() {
283        drop(unsafe { Box::from_raw(handle) });
284    }
285}
286
287// ---------------------------------------------------------------------------
288// Accessor functions for ScopeHandle
289// ---------------------------------------------------------------------------
290
291/// Return the UUID of a scope handle as a C string. Caller must free the result
292/// with `nemo_flow_string_free`. Returns null if `ptr` is null.
293///
294/// # Safety
295/// `ptr` must be a valid `FfiScopeHandle` pointer or null.
296#[unsafe(no_mangle)]
297pub unsafe extern "C" fn nemo_flow_scope_handle_uuid(ptr: *const FfiScopeHandle) -> *mut c_char {
298    if ptr.is_null() {
299        return std::ptr::null_mut();
300    }
301    str_to_c_string(&unsafe { &*ptr }.0.uuid.to_string())
302}
303
304/// Return the name of a scope handle as a C string. Caller must free the result.
305/// Returns null if `ptr` is null.
306///
307/// # Safety
308/// `ptr` must be a valid `FfiScopeHandle` pointer or null.
309#[unsafe(no_mangle)]
310pub unsafe extern "C" fn nemo_flow_scope_handle_name(ptr: *const FfiScopeHandle) -> *mut c_char {
311    if ptr.is_null() {
312        return std::ptr::null_mut();
313    }
314    str_to_c_string(&unsafe { &*ptr }.0.name)
315}
316
317/// Return the scope type of a scope handle. Returns `Unknown` if `ptr` is null.
318///
319/// # Safety
320/// `ptr` must be a valid `FfiScopeHandle` pointer or null.
321#[unsafe(no_mangle)]
322pub unsafe extern "C" fn nemo_flow_scope_handle_scope_type(
323    ptr: *const FfiScopeHandle,
324) -> NemoFlowScopeType {
325    if ptr.is_null() {
326        return NemoFlowScopeType::Unknown;
327    }
328    unsafe { &*ptr }.0.scope_type.into()
329}
330
331/// Return the bitfield attributes of a scope handle. Returns 0 if `ptr` is null.
332///
333/// # Safety
334/// `ptr` must be a valid `FfiScopeHandle` pointer or null.
335#[unsafe(no_mangle)]
336pub unsafe extern "C" fn nemo_flow_scope_handle_attributes(ptr: *const FfiScopeHandle) -> u32 {
337    if ptr.is_null() {
338        return 0;
339    }
340    unsafe { &*ptr }.0.attributes.bits()
341}
342
343/// Return the parent scope UUID as a C string, or null if there is no parent.
344/// Caller must free the result with `nemo_flow_string_free`.
345///
346/// # Safety
347/// `ptr` must be a valid `FfiScopeHandle` pointer or null.
348#[unsafe(no_mangle)]
349pub unsafe extern "C" fn nemo_flow_scope_handle_parent_uuid(
350    ptr: *const FfiScopeHandle,
351) -> *mut c_char {
352    if ptr.is_null() {
353        return std::ptr::null_mut();
354    }
355    match &unsafe { &*ptr }.0.parent_uuid {
356        Some(u) => str_to_c_string(&u.to_string()),
357        None => std::ptr::null_mut(),
358    }
359}
360
361/// Return the scope data as a JSON C string, or null if no data is set.
362/// Caller must free the result with `nemo_flow_string_free`.
363///
364/// # Safety
365/// `ptr` must be a valid `FfiScopeHandle` pointer or null.
366#[unsafe(no_mangle)]
367pub unsafe extern "C" fn nemo_flow_scope_handle_data(ptr: *const FfiScopeHandle) -> *mut c_char {
368    if ptr.is_null() {
369        return std::ptr::null_mut();
370    }
371    match &unsafe { &*ptr }.0.data {
372        Some(d) => json_to_c_string(d),
373        None => std::ptr::null_mut(),
374    }
375}
376
377/// Return the scope metadata as a JSON C string, or null if no metadata is set.
378/// Caller must free the result with `nemo_flow_string_free`.
379///
380/// # Safety
381/// `ptr` must be a valid `FfiScopeHandle` pointer or null.
382#[unsafe(no_mangle)]
383pub unsafe extern "C" fn nemo_flow_scope_handle_metadata(
384    ptr: *const FfiScopeHandle,
385) -> *mut c_char {
386    if ptr.is_null() {
387        return std::ptr::null_mut();
388    }
389    match &unsafe { &*ptr }.0.metadata {
390        Some(m) => json_to_c_string(m),
391        None => std::ptr::null_mut(),
392    }
393}
394
395// ---------------------------------------------------------------------------
396// Accessor functions for ToolHandle
397// ---------------------------------------------------------------------------
398
399/// Return the UUID of a tool handle as a C string. Caller must free the result.
400///
401/// # Safety
402/// `ptr` must be a valid `FfiToolHandle` pointer or null.
403#[unsafe(no_mangle)]
404pub unsafe extern "C" fn nemo_flow_tool_handle_uuid(ptr: *const FfiToolHandle) -> *mut c_char {
405    if ptr.is_null() {
406        return std::ptr::null_mut();
407    }
408    str_to_c_string(&unsafe { &*ptr }.0.uuid.to_string())
409}
410
411/// Return the name of a tool handle as a C string. Caller must free the result.
412///
413/// # Safety
414/// `ptr` must be a valid `FfiToolHandle` pointer or null.
415#[unsafe(no_mangle)]
416pub unsafe extern "C" fn nemo_flow_tool_handle_name(ptr: *const FfiToolHandle) -> *mut c_char {
417    if ptr.is_null() {
418        return std::ptr::null_mut();
419    }
420    str_to_c_string(&unsafe { &*ptr }.0.name)
421}
422
423/// Return the bitfield attributes of a tool handle. Returns 0 if `ptr` is null.
424///
425/// # Safety
426/// `ptr` must be a valid `FfiToolHandle` pointer or null.
427#[unsafe(no_mangle)]
428pub unsafe extern "C" fn nemo_flow_tool_handle_attributes(ptr: *const FfiToolHandle) -> u32 {
429    if ptr.is_null() {
430        return 0;
431    }
432    unsafe { &*ptr }.0.attributes.bits()
433}
434
435/// Return the parent scope UUID of a tool handle, or null if none.
436/// Caller must free the result with `nemo_flow_string_free`.
437///
438/// # Safety
439/// `ptr` must be a valid `FfiToolHandle` pointer or null.
440#[unsafe(no_mangle)]
441pub unsafe extern "C" fn nemo_flow_tool_handle_parent_uuid(
442    ptr: *const FfiToolHandle,
443) -> *mut c_char {
444    if ptr.is_null() {
445        return std::ptr::null_mut();
446    }
447    match &unsafe { &*ptr }.0.parent_uuid {
448        Some(u) => str_to_c_string(&u.to_string()),
449        None => std::ptr::null_mut(),
450    }
451}
452
453// ---------------------------------------------------------------------------
454// Accessor functions for LlmHandle
455// ---------------------------------------------------------------------------
456
457/// Return the UUID of an LLM handle as a C string. Caller must free the result.
458///
459/// # Safety
460/// `ptr` must be a valid `FfiLLMHandle` pointer or null.
461#[unsafe(no_mangle)]
462pub unsafe extern "C" fn nemo_flow_llm_handle_uuid(ptr: *const FfiLLMHandle) -> *mut c_char {
463    if ptr.is_null() {
464        return std::ptr::null_mut();
465    }
466    str_to_c_string(&unsafe { &*ptr }.0.uuid.to_string())
467}
468
469/// Return the name of an LLM handle as a C string. Caller must free the result.
470///
471/// # Safety
472/// `ptr` must be a valid `FfiLLMHandle` pointer or null.
473#[unsafe(no_mangle)]
474pub unsafe extern "C" fn nemo_flow_llm_handle_name(ptr: *const FfiLLMHandle) -> *mut c_char {
475    if ptr.is_null() {
476        return std::ptr::null_mut();
477    }
478    str_to_c_string(&unsafe { &*ptr }.0.name)
479}
480
481/// Return the bitfield attributes of an LLM handle. Returns 0 if `ptr` is null.
482///
483/// # Safety
484/// `ptr` must be a valid `FfiLLMHandle` pointer or null.
485#[unsafe(no_mangle)]
486pub unsafe extern "C" fn nemo_flow_llm_handle_attributes(ptr: *const FfiLLMHandle) -> u32 {
487    if ptr.is_null() {
488        return 0;
489    }
490    unsafe { &*ptr }.0.attributes.bits()
491}
492
493/// Return the parent scope UUID of an LLM handle, or null if none.
494/// Caller must free the result with `nemo_flow_string_free`.
495///
496/// # Safety
497/// `ptr` must be a valid `FfiLLMHandle` pointer or null.
498#[unsafe(no_mangle)]
499pub unsafe extern "C" fn nemo_flow_llm_handle_parent_uuid(ptr: *const FfiLLMHandle) -> *mut c_char {
500    if ptr.is_null() {
501        return std::ptr::null_mut();
502    }
503    match &unsafe { &*ptr }.0.parent_uuid {
504        Some(u) => str_to_c_string(&u.to_string()),
505        None => std::ptr::null_mut(),
506    }
507}
508
509// ---------------------------------------------------------------------------
510// LlmRequest construction + accessors
511// ---------------------------------------------------------------------------
512
513/// Create a new LLM request object. Returns a heap-allocated `FfiLLMRequest`
514/// that must be freed with `nemo_flow_llm_request_free`. Returns null on
515/// invalid input.
516///
517/// # Parameters
518/// - `headers_json`: JSON object of headers/metadata, or null.
519/// - `content_json`: JSON request content payload, or null.
520///
521/// # Safety
522/// All string arguments must be valid null-terminated C strings or null.
523#[unsafe(no_mangle)]
524pub unsafe extern "C" fn nemo_flow_llm_request_new(
525    headers_json: *const c_char,
526    content_json: *const c_char,
527) -> *mut FfiLLMRequest {
528    let headers = match crate::convert::c_str_to_json(headers_json) {
529        Some(Json::Object(m)) => m,
530        _ => serde_json::Map::new(),
531    };
532    let content = crate::convert::c_str_to_json(content_json).unwrap_or(Json::Null);
533
534    Box::into_raw(Box::new(FfiLLMRequest(LlmRequest { headers, content })))
535}
536
537/// Return the headers of an LLM request as a JSON C string. Caller must free the result.
538///
539/// # Safety
540/// `ptr` must be a valid `FfiLLMRequest` pointer or null.
541#[unsafe(no_mangle)]
542pub unsafe extern "C" fn nemo_flow_llm_request_headers(ptr: *const FfiLLMRequest) -> *mut c_char {
543    if ptr.is_null() {
544        return std::ptr::null_mut();
545    }
546    json_to_c_string(&Json::Object(unsafe { &*ptr }.0.headers.clone()))
547}
548
549/// Return the content of an LLM request as a JSON C string. Caller must free the result.
550///
551/// # Safety
552/// `ptr` must be a valid `FfiLLMRequest` pointer or null.
553#[unsafe(no_mangle)]
554pub unsafe extern "C" fn nemo_flow_llm_request_content(ptr: *const FfiLLMRequest) -> *mut c_char {
555    if ptr.is_null() {
556        return std::ptr::null_mut();
557    }
558    json_to_c_string(&unsafe { &*ptr }.0.content)
559}
560
561// ---------------------------------------------------------------------------
562// Event accessors
563// ---------------------------------------------------------------------------
564
565/// Return the UUID of an event as a C string. Caller must free the result.
566///
567/// # Safety
568/// `ptr` must be a valid `FfiEvent` pointer or null.
569#[unsafe(no_mangle)]
570pub unsafe extern "C" fn nemo_flow_event_uuid(ptr: *const FfiEvent) -> *mut c_char {
571    if ptr.is_null() {
572        return std::ptr::null_mut();
573    }
574    str_to_c_string(&unsafe { &*ptr }.0.uuid().to_string())
575}
576
577/// Return the name of an event as a C string, or null if unnamed.
578/// Caller must free the result with `nemo_flow_string_free`.
579///
580/// # Safety
581/// `ptr` must be a valid `FfiEvent` pointer or null.
582#[unsafe(no_mangle)]
583pub unsafe extern "C" fn nemo_flow_event_name(ptr: *const FfiEvent) -> *mut c_char {
584    if ptr.is_null() {
585        return std::ptr::null_mut();
586    }
587    str_to_c_string(unsafe { &*ptr }.0.name())
588}
589
590/// Return the event discriminator as a C string.
591/// Caller must free the result with `nemo_flow_string_free`.
592///
593/// # Safety
594/// `ptr` must be a valid `FfiEvent` pointer or null.
595#[unsafe(no_mangle)]
596pub unsafe extern "C" fn nemo_flow_event_kind(ptr: *const FfiEvent) -> *mut c_char {
597    if ptr.is_null() {
598        return std::ptr::null_mut();
599    }
600    str_to_c_string(unsafe { &*ptr }.0.kind())
601}
602
603/// Return the ATOF version as a C string.
604///
605/// # Safety
606/// `ptr` must be a valid `FfiEvent` pointer or null.
607#[unsafe(no_mangle)]
608pub unsafe extern "C" fn nemo_flow_event_atof_version(ptr: *const FfiEvent) -> *mut c_char {
609    if ptr.is_null() {
610        return std::ptr::null_mut();
611    }
612    match &unsafe { &*ptr }.0 {
613        Event::Scope(event) => str_to_c_string(&event.base.atof_version),
614        Event::Mark(event) => str_to_c_string(&event.base.atof_version),
615    }
616}
617
618/// Return the ATOF scope category as a C string, or null for mark events.
619///
620/// # Safety
621/// `ptr` must be a valid `FfiEvent` pointer or null.
622#[unsafe(no_mangle)]
623pub unsafe extern "C" fn nemo_flow_event_scope_category(ptr: *const FfiEvent) -> *mut c_char {
624    if ptr.is_null() {
625        return std::ptr::null_mut();
626    }
627    match unsafe { &*ptr }.0.scope_category() {
628        Some(nemo_flow::api::event::ScopeCategory::Start) => str_to_c_string("start"),
629        Some(nemo_flow::api::event::ScopeCategory::End) => str_to_c_string("end"),
630        None => std::ptr::null_mut(),
631    }
632}
633
634/// Return the ATOF category as a C string, or null if absent.
635///
636/// # Safety
637/// `ptr` must be a valid `FfiEvent` pointer or null.
638#[unsafe(no_mangle)]
639pub unsafe extern "C" fn nemo_flow_event_category(ptr: *const FfiEvent) -> *mut c_char {
640    if ptr.is_null() {
641        return std::ptr::null_mut();
642    }
643    match unsafe { &*ptr }.0.category() {
644        Some(category) => str_to_c_string(category.as_str()),
645        None => std::ptr::null_mut(),
646    }
647}
648
649/// Return ATOF attributes as a JSON string array.
650///
651/// # Safety
652/// `ptr` must be a valid `FfiEvent` pointer or null.
653#[unsafe(no_mangle)]
654pub unsafe extern "C" fn nemo_flow_event_attributes_json(ptr: *const FfiEvent) -> *mut c_char {
655    if ptr.is_null() {
656        return std::ptr::null_mut();
657    }
658    match unsafe { &*ptr }.0.attributes() {
659        Some(attributes) => json_to_c_string(&serde_json::json!(attributes)),
660        None => std::ptr::null_mut(),
661    }
662}
663
664/// Return the ATOF category profile as a JSON C string, or null if absent.
665///
666/// # Safety
667/// `ptr` must be a valid `FfiEvent` pointer or null.
668#[unsafe(no_mangle)]
669pub unsafe extern "C" fn nemo_flow_event_category_profile(ptr: *const FfiEvent) -> *mut c_char {
670    if ptr.is_null() {
671        return std::ptr::null_mut();
672    }
673    match unsafe { &*ptr }.0.category_profile() {
674        Some(profile) => {
675            let value = serde_json::to_value(profile).unwrap_or(Json::Null);
676            json_to_c_string(&value)
677        }
678        None => std::ptr::null_mut(),
679    }
680}
681
682/// Return the Agent Trajectory Observability Format (ATOF) data schema as a
683/// JSON C string, or null if absent.
684///
685/// # Safety
686/// `ptr` must be a valid `FfiEvent` pointer or null.
687#[unsafe(no_mangle)]
688pub unsafe extern "C" fn nemo_flow_event_data_schema(ptr: *const FfiEvent) -> *mut c_char {
689    if ptr.is_null() {
690        return std::ptr::null_mut();
691    }
692    match unsafe { &*ptr }.0.data_schema() {
693        Some(schema) => {
694            let value = serde_json::to_value(schema).unwrap_or(Json::Null);
695            json_to_c_string(&value)
696        }
697        None => std::ptr::null_mut(),
698    }
699}
700
701/// Return the raw attribute bitfield for an event, or 0 if it has none.
702///
703/// # Safety
704/// `ptr` must be a valid `FfiEvent` pointer or null.
705#[unsafe(no_mangle)]
706pub unsafe extern "C" fn nemo_flow_event_attributes(ptr: *const FfiEvent) -> u32 {
707    if ptr.is_null() {
708        return 0;
709    }
710    0
711}
712
713/// Return the event data as a JSON C string, or null if no data is set.
714/// Caller must free the result with `nemo_flow_string_free`.
715///
716/// # Safety
717/// `ptr` must be a valid `FfiEvent` pointer or null.
718#[unsafe(no_mangle)]
719pub unsafe extern "C" fn nemo_flow_event_data(ptr: *const FfiEvent) -> *mut c_char {
720    if ptr.is_null() {
721        return std::ptr::null_mut();
722    }
723    match unsafe { &*ptr }.0.data() {
724        Some(d) => json_to_c_string(d),
725        None => std::ptr::null_mut(),
726    }
727}
728
729/// Return the event metadata as a JSON C string, or null if no metadata is set.
730/// Caller must free the result with `nemo_flow_string_free`.
731///
732/// # Safety
733/// `ptr` must be a valid `FfiEvent` pointer or null.
734#[unsafe(no_mangle)]
735pub unsafe extern "C" fn nemo_flow_event_metadata(ptr: *const FfiEvent) -> *mut c_char {
736    if ptr.is_null() {
737        return std::ptr::null_mut();
738    }
739    match unsafe { &*ptr }.0.metadata() {
740        Some(m) => json_to_c_string(m),
741        None => std::ptr::null_mut(),
742    }
743}
744
745/// Return the event timestamp as an RFC 3339 C string. Caller must free the result.
746///
747/// # Safety
748/// `ptr` must be a valid `FfiEvent` pointer or null.
749#[unsafe(no_mangle)]
750pub unsafe extern "C" fn nemo_flow_event_timestamp(ptr: *const FfiEvent) -> *mut c_char {
751    if ptr.is_null() {
752        return std::ptr::null_mut();
753    }
754    str_to_c_string(&unsafe { &*ptr }.0.timestamp().to_rfc3339())
755}
756
757/// Return the event input as a JSON C string, or null if no input is set.
758/// Caller must free the result with `nemo_flow_string_free`.
759///
760/// # Safety
761/// `ptr` must be a valid `FfiEvent` pointer or null.
762#[unsafe(no_mangle)]
763pub unsafe extern "C" fn nemo_flow_event_input(ptr: *const FfiEvent) -> *mut c_char {
764    if ptr.is_null() {
765        return std::ptr::null_mut();
766    }
767    match unsafe { &*ptr }.0.input() {
768        Some(d) => json_to_c_string(d),
769        None => std::ptr::null_mut(),
770    }
771}
772
773/// Return the event output as a JSON C string, or null if no output is set.
774/// Caller must free the result with `nemo_flow_string_free`.
775///
776/// # Safety
777/// `ptr` must be a valid `FfiEvent` pointer or null.
778#[unsafe(no_mangle)]
779pub unsafe extern "C" fn nemo_flow_event_output(ptr: *const FfiEvent) -> *mut c_char {
780    if ptr.is_null() {
781        return std::ptr::null_mut();
782    }
783    match unsafe { &*ptr }.0.output() {
784        Some(d) => json_to_c_string(d),
785        None => std::ptr::null_mut(),
786    }
787}
788
789/// Return the event model name as a C string, or null if no model name is set.
790/// Caller must free the result with `nemo_flow_string_free`.
791///
792/// # Safety
793/// `ptr` must be a valid `FfiEvent` pointer or null.
794#[unsafe(no_mangle)]
795pub unsafe extern "C" fn nemo_flow_event_model_name(ptr: *const FfiEvent) -> *mut c_char {
796    if ptr.is_null() {
797        return std::ptr::null_mut();
798    }
799    match unsafe { &*ptr }.0.model_name() {
800        Some(s) => str_to_c_string(s),
801        None => std::ptr::null_mut(),
802    }
803}
804
805/// Return the event tool call ID as a C string, or null if no tool call ID is set.
806/// Caller must free the result with `nemo_flow_string_free`.
807///
808/// # Safety
809/// `ptr` must be a valid `FfiEvent` pointer or null.
810#[unsafe(no_mangle)]
811pub unsafe extern "C" fn nemo_flow_event_tool_call_id(ptr: *const FfiEvent) -> *mut c_char {
812    if ptr.is_null() {
813        return std::ptr::null_mut();
814    }
815    match unsafe { &*ptr }.0.tool_call_id() {
816        Some(s) => str_to_c_string(s),
817        None => std::ptr::null_mut(),
818    }
819}
820
821/// Return the event parent UUID as a C string, or null if no parent UUID is set.
822/// Caller must free the result with `nemo_flow_string_free`.
823///
824/// # Safety
825/// `ptr` must be a valid `FfiEvent` pointer or null.
826#[unsafe(no_mangle)]
827pub unsafe extern "C" fn nemo_flow_event_parent_uuid(ptr: *const FfiEvent) -> *mut c_char {
828    if ptr.is_null() {
829        return std::ptr::null_mut();
830    }
831    match unsafe { &*ptr }.0.parent_uuid() {
832        Some(u) => str_to_c_string(&u.to_string()),
833        None => std::ptr::null_mut(),
834    }
835}
836
837/// Return the event scope type as a C string, or null if no scope type is set.
838/// Caller must free the result with `nemo_flow_string_free`.
839///
840/// # Safety
841/// `ptr` must be a valid `FfiEvent` pointer or null.
842#[unsafe(no_mangle)]
843pub unsafe extern "C" fn nemo_flow_event_scope_type(ptr: *const FfiEvent) -> *mut c_char {
844    if ptr.is_null() {
845        return std::ptr::null_mut();
846    }
847    match unsafe { &*ptr }.0.scope_type() {
848        Some(st) => str_to_c_string(st.as_str()),
849        None => std::ptr::null_mut(),
850    }
851}
852
853/// Return the annotated request from an LLM start event as a JSON C string,
854/// or null if not available (non-LLM events, or no codec was active).
855/// Caller must free the result with `nemo_flow_string_free`.
856///
857/// # Safety
858/// `ptr` must be a valid `FfiEvent` pointer or null.
859#[unsafe(no_mangle)]
860pub unsafe extern "C" fn nemo_flow_event_annotated_request(ptr: *const FfiEvent) -> *mut c_char {
861    if ptr.is_null() {
862        return std::ptr::null_mut();
863    }
864    match unsafe { &*ptr }.0.annotated_request() {
865        Some(a) => {
866            let value = serde_json::to_value(a.as_ref()).unwrap_or_default();
867            json_to_c_string(&value)
868        }
869        None => std::ptr::null_mut(),
870    }
871}
872
873/// Return the annotated response from an LLM end event as a JSON C string,
874/// or null if not available (non-LLM events, or no response codec was active).
875/// Caller must free the result with `nemo_flow_string_free`.
876///
877/// # Safety
878/// `ptr` must be a valid `FfiEvent` pointer or null.
879#[unsafe(no_mangle)]
880pub unsafe extern "C" fn nemo_flow_event_annotated_response(ptr: *const FfiEvent) -> *mut c_char {
881    if ptr.is_null() {
882        return std::ptr::null_mut();
883    }
884    match unsafe { &*ptr }.0.annotated_response() {
885        Some(a) => {
886            let value = serde_json::to_value(a.as_ref()).unwrap_or_default();
887            json_to_c_string(&value)
888        }
889        None => std::ptr::null_mut(),
890    }
891}
892
893#[cfg(test)]
894#[path = "../../tests/unit/types_tests.rs"]
895mod tests;