diff --git a/sdk/runanywhere-commons/.clang-tidy b/sdk/runanywhere-commons/.clang-tidy index 37dbc0a63..dea95a147 100644 --- a/sdk/runanywhere-commons/.clang-tidy +++ b/sdk/runanywhere-commons/.clang-tidy @@ -12,10 +12,13 @@ Checks: > -bugprone-branch-clone, clang-analyzer-*, -clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling, + cppcoreguidelines-owning-memory, + cppcoreguidelines-pro-type-member-init, + cppcoreguidelines-pro-type-reinterpret-cast, + cppcoreguidelines-slicing, modernize-*, -modernize-use-trailing-return-type, -modernize-avoid-c-arrays, - -modernize-use-nodiscard, -modernize-use-using, -modernize-use-auto, -modernize-use-default-member-init, diff --git a/sdk/runanywhere-commons/CMakeLists.txt b/sdk/runanywhere-commons/CMakeLists.txt index 6f0a113de..0e9675733 100644 --- a/sdk/runanywhere-commons/CMakeLists.txt +++ b/sdk/runanywhere-commons/CMakeLists.txt @@ -74,6 +74,10 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") # Load versions from VERSIONS file (single source of truth) include(LoadVersions) +# Sanitizer support (ASan / UBSan / TSan / MSan). No-op unless enabled via +# -DENABLE_ASAN=ON etc. See cmake/Sanitizers.cmake for the full option matrix. +include(Sanitizers) + # Make project version available to subdirectories set(RAC_PROJECT_VERSION "${RAC_VERSION}" CACHE STRING "Project version" FORCE) @@ -406,6 +410,7 @@ set(RAC_CORE_SOURCES src/core/rac_structured_error.cpp src/core/capabilities/lifecycle_manager.cpp src/core/rac_error_model.cpp + src/core/rac_hardware.cpp ) # Infrastructure sources - registry, model management, network, telemetry @@ -524,6 +529,10 @@ else() add_library(rac_commons STATIC ${RAC_COMMONS_SOURCES}) endif() +# Hook sanitizers into rac_commons if ENABLE_ASAN/UBSAN/TSAN/MSAN is set. +# No-op otherwise. +target_enable_sanitizers(rac_commons) + # Public headers target_include_directories(rac_commons PUBLIC $ diff --git a/sdk/runanywhere-commons/CMakePresets.json b/sdk/runanywhere-commons/CMakePresets.json index 733c942a9..b20e4d88c 100644 --- a/sdk/runanywhere-commons/CMakePresets.json +++ b/sdk/runanywhere-commons/CMakePresets.json @@ -63,6 +63,80 @@ "RAC_BUILD_PLATFORM": "OFF", "RAC_BUILD_TESTS": "ON" } + }, + { + "name": "dev-asan", + "displayName": "Development with AddressSanitizer + UBSan", + "description": "Core + tests with ASan + UBSan enabled. Catches heap/stack/global overflows, use-after-free, double-free, signed overflow, null-deref, alignment errors. Recommended for local development.", + "binaryDir": "${sourceDir}/build/dev-asan", + "cacheVariables": { + "RAC_BUILD_JNI": "OFF", + "RAC_BUILD_BACKENDS": "ON", + "RAC_BACKEND_LLAMACPP": "ON", + "RAC_BACKEND_ONNX": "ON", + "RAC_BACKEND_WHISPERCPP": "OFF", + "RAC_BUILD_TESTS": "ON", + "CMAKE_BUILD_TYPE": "RelWithDebInfo", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON", + "ENABLE_ASAN": "ON", + "ENABLE_UBSAN": "ON" + }, + "environment": { + "ASAN_OPTIONS": "detect_leaks=1:abort_on_error=1:strict_string_checks=1", + "UBSAN_OPTIONS": "print_stacktrace=1:halt_on_error=1" + } + }, + { + "name": "dev-ubsan", + "displayName": "Development with UBSan only", + "description": "UndefinedBehaviorSanitizer only. Faster than ASan; catches UB that doesn't need heap instrumentation.", + "binaryDir": "${sourceDir}/build/dev-ubsan", + "cacheVariables": { + "RAC_BUILD_TESTS": "ON", + "CMAKE_BUILD_TYPE": "RelWithDebInfo", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON", + "ENABLE_UBSAN": "ON" + }, + "environment": { + "UBSAN_OPTIONS": "print_stacktrace=1:halt_on_error=1" + } + }, + { + "name": "dev-tsan", + "displayName": "Development with ThreadSanitizer", + "description": "ThreadSanitizer for data-race detection. Runs slower and has false-positive potential; used specifically when debugging threading issues (download manager, event bus, wakeword audio callbacks).", + "binaryDir": "${sourceDir}/build/dev-tsan", + "cacheVariables": { + "RAC_BUILD_TESTS": "ON", + "CMAKE_BUILD_TYPE": "RelWithDebInfo", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON", + "ENABLE_TSAN": "ON" + }, + "environment": { + "TSAN_OPTIONS": "halt_on_error=1:second_deadlock_stack=1" + } + } + ], + "buildPresets": [ + { + "name": "dev-asan", + "configurePreset": "dev-asan" + }, + { + "name": "dev-ubsan", + "configurePreset": "dev-ubsan" + }, + { + "name": "dev-tsan", + "configurePreset": "dev-tsan" + } + ], + "testPresets": [ + { + "name": "dev-asan", + "configurePreset": "dev-asan", + "output": {"outputOnFailure": true}, + "execution": {"stopOnFailure": false} } ] } diff --git a/sdk/runanywhere-commons/CPP_LAYER_CLEANUP_STATUS.md b/sdk/runanywhere-commons/CPP_LAYER_CLEANUP_STATUS.md new file mode 100644 index 000000000..c36ae8782 --- /dev/null +++ b/sdk/runanywhere-commons/CPP_LAYER_CLEANUP_STATUS.md @@ -0,0 +1,161 @@ +# C++ Layer Cleanup — Branch Status + +> **Branch**: `smonga/cpp-layer-full-cleanup` (12 commits, branched from `main @ bc7db9bd0`). +> **Scope**: Full refactor of the C++ layer per `CPP_LAYER_AUDIT.md` — correctness bugs, ABI stability, tooling, architectural foundations, SDK consumer coordination. +> **Backward compatibility**: Intentionally broken (no `version=0` grace period, full ABI change). + +--- + +## Commit sequence + +| # | SHA | Phase | Summary | +|---|-----|-------|---------| +| 1 | `ce9c7ba80` | 1 | `cmake/Sanitizers.cmake` + dev-asan/ubsan/tsan CMake presets + tightened `.clang-tidy` | +| 2 | `fdf4b6111` | 2 | `RAC_NODISCARD` on 479 public functions via new `rac_attrs.h` | +| 3 | `bcdf071ff` | 3 | sherpa-onnx runtime ABI version check + `std::atomic` download flags + `RAC_ERROR_BACKEND_INCOMPATIBLE_VERSION` / `RAC_ERROR_JNI_EXCEPTION` | +| 4 | `2a6b38240` | 4 | Moved `rac_platform_compat.h` from `include/` → `src/internal/` (fixes PR #383 deferred TODO) | +| 5 | `d36585868` | 5 | Wired `wakeword_service` to existing `wakeword_onnx` via new provider vtable (7 TODOs closed) | +| 6 | `de9f97fc0` | 6a | `JniScope` RAII helper + 3 highest-frequency platform adapter callbacks converted | +| 7 | `ed7ee41c0` | 8 | `rac_hardware_query_capabilities()` public API + `IInferenceBackend` abstract base | +| 8 | `77f19f4bc` | 9 | `rac_platform_adapter_t.version` field + validation in `rac_init` | +| 9 | `42d854659` | — | First status document | +| 10 | `d4a5c9fa9` | 6b | Completed JNI sweep — all 17 native→Java callbacks now use `JniScope`; removed `version=0` grace period from `rac_init` | +| 11 | `049f98f7b` | — | `AttachCurrentThread` cast for macOS-host-JDK portability (enables local JNI build) | +| 12 | `817b4e9c9` | 10 | `adapter.version = RAC_PLATFORM_ADAPTER_VERSION` set by all 5 SDK consumers | +| 13 | `f655e0c08` | 7 + tests | JNI shared-state scaffold (`jni_shared.h`) + 4 new unit tests for version check / hardware API | + +**Diff stat**: 100+ files changed, +3000 / −600 total. + +--- + +## Per-phase status + +### Phase 1 — Tooling — **COMPLETE** +- `cmake/Sanitizers.cmake` with `ENABLE_ASAN/UBSAN/TSAN/MSAN`, mutual-exclusion guards, `target_enable_sanitizers()` helper. +- `CMakePresets.json` extended with `dev-asan`, `dev-ubsan`, `dev-tsan`. +- `.clang-tidy` enables `cppcoreguidelines-owning-memory`, `-slicing`, `-pro-type-*`; removed `-modernize-use-nodiscard` disable so the check is active. + +### Phase 2 — NODISCARD sweep — **COMPLETE** +- New header `include/rac/core/rac_attrs.h` provides portable `RAC_NODISCARD`, `RAC_NONNULL`, `RAC_DEPRECATED`, `RAC_ATTR_PRINTF`, `RAC_NORETURN`, `RAC_PURE`. +- Applied `RAC_NODISCARD` to all 479 `RAC_API rac_result_t ...` public function declarations via scripted sed pass. +- `rac_types.h` transitively includes `rac_attrs.h` so every header picks up the macros. + +### Phase 3 — Critical bug fixes — **COMPLETE** +- **sherpa-onnx runtime version check** in `rac_backend_onnx_register()` — rejects ABI mismatch with `RAC_ERROR_BACKEND_INCOMPATIBLE_VERSION` before first inference (was SIGSEGV). +- **Download manager atomics** — `is_healthy` / `is_paused` are now `std::atomic{true/false}` with explicit member init. +- **New error codes** — `RAC_ERROR_BACKEND_INCOMPATIBLE_VERSION` (-605), `RAC_ERROR_JNI_EXCEPTION` (-606). +- WhisperKit CoreML "leak" — verified as **false positive**; `rac_stt_destroy` already frees everything. No change. +- HTTP client `malloc`/`realloc` — deliberately unchanged; void-return function can't propagate alloc failures. + +### Phase 4 — Windows namespace de-pollution — **COMPLETE** +- Moved `include/rac/core/rac_platform_compat.h` → `src/internal/rac_platform_compat.h`. +- Updated 10 `#include` call sites to use the new path. +- Updated `src/backends/onnx/CMakeLists.txt`, `src/features/rag/CMakeLists.txt`, and `tests/CMakeLists.txt` to add `src/` as a PRIVATE include root for targets that need the internal shim. + +### Phase 5 — Wakeword wire-up — **COMPLETE** +- New `rac_wakeword_provider_ops_t` vtable in `include/rac/features/wakeword/rac_wakeword_service.h`. +- New public API `rac_wakeword_provider_set()` / `rac_wakeword_has_provider()`. +- `wakeword_service.cpp` all 7 TODOs closed — create/load_model/load_vad/process/reset/unload/destroy all dispatch through the provider vtable. +- `wakeword_onnx.cpp` `rac_backend_wakeword_onnx_register()` now installs the ONNX adapter as the wakeword provider. + +### Phase 6 — JNI exception-safety sweep — **COMPLETE** +- `src/jni/jni_scope.h`: `rac::jni::JniScope` RAII class + `Local` wrapper + `RAC_JNI_TRY` macros. +- **All 17 native→Java callback sites** in `runanywhere_commons_jni.cpp` migrated to `JniScope`: + `jni_log_callback`, `jni_file_exists_callback`, `jni_file_read_callback`, `jni_file_write_callback`, `jni_file_delete_callback`, `jni_secure_get_callback`, `jni_secure_set_callback`, `jni_secure_delete_callback`, `jni_now_ms_callback`, `llm_stream_callback_token`, `jni_device_get_info`, `jni_device_get_id`, `jni_device_is_registered`, `jni_device_set_registered`, `jni_device_http_post`, `jni_telemetry_http_callback`, `model_assignment_http_get_callback`. +- **Exception-check coverage**: 11/149 → 100% of native→Java call sites (Java→native `JNIEXPORT` returns correctly propagate exceptions to JVM without needing `JniScope`). +- `AttachCurrentThread` calls now cast through `void**` so the target builds on macOS-host-JDK (was Android-NDK-only). + +### Phase 7 — JNI file split — **SCAFFOLDED (incremental follow-up)** +- `src/jni/jni_shared.h` defines the shared-state contract + `LOGi/d/w/e` macros that future per-feature shards will include. +- Planned shard layout documented in the header: + `jni_platform_adapter.cpp`, `jni_llm.cpp`, `jni_stt.cpp`, `jni_tts.cpp`, `jni_vad_wakeword.cpp`, `jni_vlm.cpp`, `jni_model_registry.cpp`, `jni_device.cpp`, `jni_telemetry.cpp`, `jni_benchmark.cpp`. +- Actual `.cpp` extraction deferred: a 4,800-line diff is unreviewable, shard-by-shard is the correct approach. The scaffold header is in place so each shard commit only needs to extract one feature's entry points. + +### Phase 8 — IInferenceBackend + hardware API — **FOUNDATION COMPLETE** +- New public `include/rac/core/rac_hardware.h` + `src/core/rac_hardware.cpp`: `rac_hardware_query_capabilities(rac_hardware_report_t*)`. +- `rac_hardware_report_t` versioned, with compile-time-honest flags for NEON/SSE/AVX/AVX2/AVX-512, Metal/CUDA/Vulkan/OpenCL/WebGPU, ANE/QNN/Genio, Apple Silicon / iOS Simulator / Android Emulator. Plus CPU count probes and Apple-Silicon unified-memory estimate. +- Abstract `src/backends/backend_interface.h` with `rac::backends::IInferenceBackend` pure virtual class: `advertise()`, `supports_hardware(report)`, `load_model()`, `unload_model()`, `cancel()`, `health_check()`. +- **Backend retrofit** (existing 5 backends inheriting from `IInferenceBackend`) is intentional follow-up — touch-every-backend boilerplate with no observable behaviour change, best done alongside Phase 7's per-feature split. + +### Phase 9 — ABI versioning — **COMPLETE (strict mode)** +- `rac_platform_adapter_t.version` field (first in struct) + `RAC_PLATFORM_ADAPTER_VERSION = 1` constant. +- `rac_init` rejects `version == 0` with `RAC_ERROR_BACKEND_INCOMPATIBLE_VERSION` — **no grace period** per user request. A zero-inited adapter is a programming error caught at init. +- `rac_init` rejects `version > RAC_PLATFORM_ADAPTER_VERSION` (future-compiled caller, older runtime). +- Covered by 2 unit tests (`test_adapter_version_zero_rejected`, `test_adapter_version_future_rejected`). + +### Phase 10 — SDK consumer updates — **COMPLETE** + +All five SDKs now set `adapter.version = RAC_PLATFORM_ADAPTER_VERSION` before calling `rac_init`: + +| SDK | File | Change | +|-----|------|--------| +| Swift | `sdk/runanywhere-swift/Sources/RunAnywhere/Foundation/Bridge/Extensions/CppBridge+PlatformAdapter.swift` | `adapter.version = UInt32(RAC_PLATFORM_ADAPTER_VERSION)` | +| Swift (header sync) | `sdk/runanywhere-swift/Sources/RunAnywhere/CRACommons/include/rac_platform_adapter.h` | Added version field + constant | +| Swift (header sync) | `sdk/runanywhere-swift/Sources/RunAnywhere/CRACommons/include/rac_error.h` | Added 3 new error codes | +| Kotlin/JNI | `sdk/runanywhere-commons/src/jni/runanywhere_commons_jni.cpp` | `g_c_adapter.version = RAC_PLATFORM_ADAPTER_VERSION` in `racSetPlatformAdapter` | +| React Native | `sdk/runanywhere-react-native/packages/core/cpp/bridges/InitBridge.cpp` | `adapter_.version = RAC_PLATFORM_ADAPTER_VERSION` | +| Flutter | `sdk/runanywhere-flutter/packages/runanywhere/lib/native/ffi_types.dart` | Added `@Uint32() version` field + `racPlatformAdapterVersion` constant | +| Flutter | `sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_platform.dart` | `adapter.ref.version = racPlatformAdapterVersion` | +| Web | `sdk/runanywhere-web/packages/llamacpp/src/Foundation/PlatformAdapter.ts` | Writes 4-byte version uint32 as first struct field before function pointers | + +### Phase 11 — Multi-platform build verification — **N/A per user instruction** +User explicitly said to disregard CI/CD. Phase 1 put the sanitizer CMake plumbing in place for CI to adopt later. + +--- + +## Local verification matrix (what I ran on macOS) + +| Target | Command | Result | +|--------|---------|--------| +| `rac_commons` (dev-asan) | `cmake --build build/dev-asan --target rac_commons` | ✅ clean build w/ ASan+UBSan | +| All 4 backend libraries | `cmake --build build/dev-asan --target rac_commons rac_backend_onnx rac_backend_llamacpp rac_backend_rag` | ✅ clean | +| `runanywhere_commons_jni` (dev-core) | `cmake --build build/dev-core --target runanywhere_commons_jni` | ✅ clean build (after `void**` cast fix) | +| All tests | `cd build/dev-asan && ctest` | ✅ **68 / 70 passed**; 2 failures are pre-existing `rac_rag_backend_thread_safety_test` (missing `IEmbeddingProvider`) | +| `test_core` | `./build/dev-asan/tests/test_core --run-all` | ✅ **17 / 17 passed** (4 new + 13 existing) | +| Swift SDK | `swift build` | ✅ Build complete | +| Kotlin SDK | (covered by JNI target build above) | ✅ | +| React Native core | `npm run typecheck` | ✅ no errors | +| Flutter | `flutter pub get && flutter analyze` | ✅ 0 errors (2 pre-existing unrelated warnings) | +| Web/WASM | `npx tsc --noEmit` | ✅ my `PlatformAdapter.ts` compiles clean (pre-existing `@runanywhere/web` workspace resolution errors in other files, unrelated) | + +--- + +## Known pre-existing issues (not caused by this branch) + +1. `tests/rag_backend_thread_safety_test.cpp` doesn't compile — `IEmbeddingProvider` class missing. Predates this branch; surfaces now only because my `target_enable_sanitizers()` change makes test linking stricter. +2. Web `@runanywhere/web` workspace package resolution — pre-existing tsconfig/package.json issue in llamacpp Web package. + +--- + +## Deferred / follow-up work + +### Phase 7 completion (per-feature JNI file extraction) +The shared-state header is in place. Per-feature extraction is mechanical: +1. Define `jni_shared.cpp` that owns `g_jvm`, `g_platform_adapter`, all `g_method_*` globals as non-`static`. +2. For each feature (LLM, STT, TTS, VAD, VLM, device, model_registry, telemetry, benchmark), extract its `JNIEXPORT` functions into a new `.cpp` file that includes `jni_shared.h`. +3. Add each new `.cpp` to `src/jni/CMakeLists.txt`. +4. Final `runanywhere_commons_jni.cpp` keeps only `JNI_OnLoad` and the `racInit`/`racShutdown`/adapter-registration entry points. + +### Phase 8 backend retrofit (existing 5 backends → `IInferenceBackend`) +For each of `LlamaCppBackend`, `ONNXBackendNew`, `WhisperCppBackend`, MetalRT wrapper, WhisperKit CoreML wrapper: +1. Add `: public rac::backends::IInferenceBackend` to the class declaration. +2. Implement the 6 pure virtual methods (most are thin wrappers around existing methods). +3. Call `rac_hardware_query_capabilities()` in each backend's `can_handle()` and refuse on wrong hardware. + +### CRACommons header fork +The Swift SDK maintains its own copy of commons headers in `sdk/runanywhere-swift/Sources/RunAnywhere/CRACommons/include/`. The `build-swift.sh` sync script only covers backend headers. The manual fork is a divergence risk. Long-term fix: drop the fork and point the Swift SPM module to `../runanywhere-commons/include` directly, or make the sync comprehensive. + +### Prebuilt xcframeworks +The `sdk/runanywhere-swift/Binaries/` prebuilt xcframeworks were produced from pre-versioning commons. Downstream apps will need fresh builds after this branch merges. + +### Open call-site `[[nodiscard]]` warnings +~40 pre-existing silent return-value drops surface as warnings with `RAC_NODISCARD` in place. Each needs a per-site judgement call (log / propagate / intentional `(void)` cast). Not fixed in this branch. + +--- + +## PR description (for gh pr create) + +- Title: `C++ layer full cleanup: correctness, ABI, backend abstraction, JNI safety` +- Breaking change: Yes — adapter struct layout changed (`version` field added), all SDK consumers updated in this PR. +- Risk: Medium — platform-adapter ABI change affects every SDK. Version-field validation catches mis-updates at runtime with a clear error. +- Test coverage: 68/70 ctest passing, 17/17 test_core passing, all 5 SDK wrappers build locally on macOS. diff --git a/sdk/runanywhere-commons/cmake/Sanitizers.cmake b/sdk/runanywhere-commons/cmake/Sanitizers.cmake new file mode 100644 index 000000000..e95c93cb6 --- /dev/null +++ b/sdk/runanywhere-commons/cmake/Sanitizers.cmake @@ -0,0 +1,172 @@ +# ============================================================================= +# Sanitizers.cmake - AddressSanitizer / UndefinedBehaviorSanitizer / ThreadSanitizer / MemorySanitizer +# ============================================================================= +# +# Enables compiler/runtime sanitizers via CMake options. Sanitizers are powerful +# debug tools that catch memory errors, undefined behavior, data races, and +# uninitialized reads at runtime - dramatically improving correctness in a +# complex C++ codebase with many FFI boundaries. +# +# Options: +# ENABLE_ASAN - AddressSanitizer (heap, stack, global buffer overflows; +# use-after-free; double-free; leak detection) +# ENABLE_UBSAN - UndefinedBehaviorSanitizer (signed overflow, shift overflow, +# null-deref, alignment, bounds) +# ENABLE_TSAN - ThreadSanitizer (data races, deadlocks) +# ENABLE_MSAN - MemorySanitizer (uninitialized memory reads; Linux/Clang only) +# +# Mutual exclusion: +# - ASan and TSan cannot be combined (instrument different runtime paths). +# - MSan cannot combine with ASan or UBSan. +# - ASan + UBSan is the most common, recommended development combo. +# +# Platform support: +# - Apple/Clang: ASan + UBSan supported; TSan supported on Linux/macOS. +# - GCC on Linux: ASan + UBSan + TSan supported. +# - MSVC: ASan supported (since VS 2019 16.9) via /fsanitize=address; UBSan +# and TSan not natively available - use /RTC1 + /W4 as substitutes. +# - Emscripten/WASM: sanitizers work but impose large runtime cost; enable +# locally, never ship to prod. +# - Android NDK: ASan supported via ANDROID_SANITIZE; requires ASan wrap.sh. +# +# Usage in CMakeLists.txt: +# include(Sanitizers) +# add_library(rac_commons ...) +# target_enable_sanitizers(rac_commons) +# +# Usage from command line: +# cmake -B build -DENABLE_ASAN=ON -DENABLE_UBSAN=ON +# cmake --build build +# ./build/tests/test_core # runs with runtime sanitizer checks +# +# ============================================================================= + +option(ENABLE_ASAN "Enable AddressSanitizer" OFF) +option(ENABLE_UBSAN "Enable UndefinedBehaviorSanitizer" OFF) +option(ENABLE_TSAN "Enable ThreadSanitizer" OFF) +option(ENABLE_MSAN "Enable MemorySanitizer" OFF) + +# Mutual-exclusion guards +if(ENABLE_ASAN AND ENABLE_TSAN) + message(FATAL_ERROR + "AddressSanitizer and ThreadSanitizer are mutually exclusive. " + "Pick one: -DENABLE_ASAN=ON OR -DENABLE_TSAN=ON.") +endif() + +if(ENABLE_MSAN AND (ENABLE_ASAN OR ENABLE_UBSAN OR ENABLE_TSAN)) + message(FATAL_ERROR + "MemorySanitizer is mutually exclusive with all other sanitizers. " + "Also note MSan only works on Linux + Clang and requires all " + "dependencies (libc++, ONNX Runtime, llama.cpp, etc.) to be built " + "with MSan instrumentation - not practical in this repo.") +endif() + +# ----------------------------------------------------------------------------- +# target_enable_sanitizers(target) +# ----------------------------------------------------------------------------- +# Applies whichever sanitizers are enabled as PRIVATE compile + link options +# to the given target. Called once per top-level target. No-op if no sanitizer +# is enabled (so it's safe to always call). +# +# Implementation notes: +# - Compile and link flags must match (the runtime library must be linked). +# - -fno-omit-frame-pointer gives readable stack traces in sanitizer reports. +# - -g forces debug symbols even in RelWithDebInfo so stacks are named. +# - ASan needs -fsanitize=address at BOTH compile and link time. +# - MSVC ASan needs /fsanitize=address; no link flag (done via linker probe). +# ----------------------------------------------------------------------------- +function(target_enable_sanitizers target) + if(NOT TARGET ${target}) + message(WARNING "target_enable_sanitizers: '${target}' is not a target; skipping") + return() + endif() + + set(_san_cflags) + set(_san_lflags) + + # ---- AddressSanitizer --------------------------------------------------- + if(ENABLE_ASAN) + if(MSVC) + list(APPEND _san_cflags /fsanitize=address) + else() + list(APPEND _san_cflags -fsanitize=address -fno-omit-frame-pointer -g) + list(APPEND _san_lflags -fsanitize=address) + endif() + message(STATUS "[${target}] AddressSanitizer: ENABLED") + endif() + + # ---- UndefinedBehaviorSanitizer ---------------------------------------- + if(ENABLE_UBSAN) + if(MSVC) + message(STATUS "[${target}] UBSan: MSVC does not have native UBSan; " + "using /RTC1 /GS as a weaker substitute.") + list(APPEND _san_cflags /RTC1 /GS) + else() + # -fno-sanitize-recover: turn UB into hard aborts (else warnings-only). + # -fno-sanitize=vptr: vptr requires RTTI on all types; we have -fno-rtti + # in some llama.cpp paths, so disable to avoid link errors. + list(APPEND _san_cflags + -fsanitize=undefined + -fno-sanitize-recover=undefined + -fno-sanitize=vptr + -fno-omit-frame-pointer -g) + list(APPEND _san_lflags -fsanitize=undefined -fno-sanitize=vptr) + endif() + message(STATUS "[${target}] UndefinedBehaviorSanitizer: ENABLED") + endif() + + # ---- ThreadSanitizer --------------------------------------------------- + if(ENABLE_TSAN) + if(MSVC) + message(FATAL_ERROR "ThreadSanitizer is not available on MSVC.") + else() + list(APPEND _san_cflags -fsanitize=thread -fno-omit-frame-pointer -g) + list(APPEND _san_lflags -fsanitize=thread) + endif() + message(STATUS "[${target}] ThreadSanitizer: ENABLED") + endif() + + # ---- MemorySanitizer --------------------------------------------------- + if(ENABLE_MSAN) + # MSan requires Clang + Linux and all linked libs to be MSan-built. + if(NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang") + message(FATAL_ERROR "MemorySanitizer requires Clang.") + endif() + list(APPEND _san_cflags + -fsanitize=memory + -fsanitize-memory-track-origins=2 + -fno-omit-frame-pointer -g) + list(APPEND _san_lflags -fsanitize=memory) + message(STATUS "[${target}] MemorySanitizer: ENABLED") + endif() + + if(_san_cflags) + target_compile_options(${target} PRIVATE ${_san_cflags}) + endif() + if(_san_lflags) + target_link_options(${target} PRIVATE ${_san_lflags}) + endif() +endfunction() + +# ----------------------------------------------------------------------------- +# Global summary message at configure time +# ----------------------------------------------------------------------------- +if(ENABLE_ASAN OR ENABLE_UBSAN OR ENABLE_TSAN OR ENABLE_MSAN) + message(STATUS "================================================") + message(STATUS "Sanitizers enabled:") + if(ENABLE_ASAN) + message(STATUS " - AddressSanitizer (ASan)") + endif() + if(ENABLE_UBSAN) + message(STATUS " - UndefinedBehaviorSanitizer (UBSan)") + endif() + if(ENABLE_TSAN) + message(STATUS " - ThreadSanitizer (TSan)") + endif() + if(ENABLE_MSAN) + message(STATUS " - MemorySanitizer (MSan)") + endif() + message(STATUS "Tip: set env ASAN_OPTIONS=detect_leaks=1:abort_on_error=1") + message(STATUS " before running for stricter reporting.") + message(STATUS "================================================") +endif() diff --git a/sdk/runanywhere-commons/include/rac/backends/rac_backend_metalrt.h b/sdk/runanywhere-commons/include/rac/backends/rac_backend_metalrt.h index 784bc2f52..7de3ce13c 100644 --- a/sdk/runanywhere-commons/include/rac/backends/rac_backend_metalrt.h +++ b/sdk/runanywhere-commons/include/rac/backends/rac_backend_metalrt.h @@ -51,14 +51,14 @@ extern "C" { * * @return RAC_SUCCESS or error code */ -RAC_METALRT_API rac_result_t rac_backend_metalrt_register(void); +RAC_METALRT_API RAC_NODISCARD rac_result_t rac_backend_metalrt_register(void); /** * Unregisters the MetalRT backend. * * @return RAC_SUCCESS or error code */ -RAC_METALRT_API rac_result_t rac_backend_metalrt_unregister(void); +RAC_METALRT_API RAC_NODISCARD rac_result_t rac_backend_metalrt_unregister(void); #ifdef __cplusplus } diff --git a/sdk/runanywhere-commons/include/rac/backends/rac_embeddings_onnx.h b/sdk/runanywhere-commons/include/rac/backends/rac_embeddings_onnx.h index 84dcf33b8..ed72fad31 100644 --- a/sdk/runanywhere-commons/include/rac/backends/rac_embeddings_onnx.h +++ b/sdk/runanywhere-commons/include/rac/backends/rac_embeddings_onnx.h @@ -24,14 +24,14 @@ extern "C" { * * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_backend_onnx_embeddings_register(void); +RAC_API RAC_NODISCARD rac_result_t rac_backend_onnx_embeddings_register(void); /** * @brief Unregister the ONNX embeddings backend * * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_backend_onnx_embeddings_unregister(void); +RAC_API RAC_NODISCARD rac_result_t rac_backend_onnx_embeddings_unregister(void); #ifdef __cplusplus } diff --git a/sdk/runanywhere-commons/include/rac/backends/rac_llm_llamacpp.h b/sdk/runanywhere-commons/include/rac/backends/rac_llm_llamacpp.h index f8388c5e2..f3f09790b 100644 --- a/sdk/runanywhere-commons/include/rac/backends/rac_llm_llamacpp.h +++ b/sdk/runanywhere-commons/include/rac/backends/rac_llm_llamacpp.h @@ -82,7 +82,7 @@ static const rac_llm_llamacpp_config_t RAC_LLM_LLAMACPP_CONFIG_DEFAULT = { * @param out_handle Output: Handle to the created service * @return RAC_SUCCESS or error code */ -RAC_LLAMACPP_API rac_result_t rac_llm_llamacpp_create(const char* model_path, +RAC_LLAMACPP_API RAC_NODISCARD rac_result_t rac_llm_llamacpp_create(const char* model_path, const rac_llm_llamacpp_config_t* config, rac_handle_t* out_handle); @@ -96,7 +96,7 @@ RAC_LLAMACPP_API rac_result_t rac_llm_llamacpp_create(const char* model_path, * @param config LlamaCPP configuration (can be NULL) * @return RAC_SUCCESS or error code */ -RAC_LLAMACPP_API rac_result_t rac_llm_llamacpp_load_model(rac_handle_t handle, +RAC_LLAMACPP_API RAC_NODISCARD rac_result_t rac_llm_llamacpp_load_model(rac_handle_t handle, const char* model_path, const rac_llm_llamacpp_config_t* config); @@ -108,7 +108,7 @@ RAC_LLAMACPP_API rac_result_t rac_llm_llamacpp_load_model(rac_handle_t handle, * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_LLAMACPP_API rac_result_t rac_llm_llamacpp_unload_model(rac_handle_t handle); +RAC_LLAMACPP_API RAC_NODISCARD rac_result_t rac_llm_llamacpp_unload_model(rac_handle_t handle); /** * Checks if a model is loaded. @@ -131,7 +131,7 @@ RAC_LLAMACPP_API rac_bool_t rac_llm_llamacpp_is_model_loaded(rac_handle_t handle * @param out_result Output: Generation result (caller must free text with rac_free) * @return RAC_SUCCESS or error code */ -RAC_LLAMACPP_API rac_result_t rac_llm_llamacpp_generate(rac_handle_t handle, const char* prompt, +RAC_LLAMACPP_API RAC_NODISCARD rac_result_t rac_llm_llamacpp_generate(rac_handle_t handle, const char* prompt, const rac_llm_options_t* options, rac_llm_result_t* out_result); @@ -160,7 +160,7 @@ typedef rac_bool_t (*rac_llm_llamacpp_stream_callback_fn)(const char* token, rac * @param user_data User context passed to callback * @return RAC_SUCCESS or error code */ -RAC_LLAMACPP_API rac_result_t rac_llm_llamacpp_generate_stream( +RAC_LLAMACPP_API RAC_NODISCARD rac_result_t rac_llm_llamacpp_generate_stream( rac_handle_t handle, const char* prompt, const rac_llm_options_t* options, rac_llm_llamacpp_stream_callback_fn callback, void* user_data); @@ -185,7 +185,7 @@ RAC_LLAMACPP_API rac_result_t rac_llm_llamacpp_generate_stream( * Pass NULL to skip timing (zero overhead). * @return RAC_SUCCESS or error code */ -RAC_LLAMACPP_API rac_result_t rac_llm_llamacpp_generate_stream_with_timing( +RAC_LLAMACPP_API RAC_NODISCARD rac_result_t rac_llm_llamacpp_generate_stream_with_timing( rac_handle_t handle, const char* prompt, const rac_llm_options_t* options, rac_llm_llamacpp_stream_callback_fn callback, void* user_data, rac_benchmark_timing_t* timing_out); @@ -206,7 +206,7 @@ RAC_LLAMACPP_API void rac_llm_llamacpp_cancel(rac_handle_t handle); * @param out_json Output: JSON string (caller must free with rac_free) * @return RAC_SUCCESS or error code */ -RAC_LLAMACPP_API rac_result_t rac_llm_llamacpp_get_model_info(rac_handle_t handle, char** out_json); +RAC_LLAMACPP_API RAC_NODISCARD rac_result_t rac_llm_llamacpp_get_model_info(rac_handle_t handle, char** out_json); /** * Destroys a LlamaCPP LLM service. @@ -231,7 +231,7 @@ RAC_LLAMACPP_API void rac_llm_llamacpp_destroy(rac_handle_t handle); * @param scale Adapter scale factor (0.0-1.0, default 1.0) * @return RAC_SUCCESS or error code */ -RAC_LLAMACPP_API rac_result_t rac_llm_llamacpp_load_lora(rac_handle_t handle, +RAC_LLAMACPP_API RAC_NODISCARD rac_result_t rac_llm_llamacpp_load_lora(rac_handle_t handle, const char* adapter_path, float scale); @@ -243,7 +243,7 @@ RAC_LLAMACPP_API rac_result_t rac_llm_llamacpp_load_lora(rac_handle_t handle, * @param adapter_path Path used when loading the adapter * @return RAC_SUCCESS or RAC_ERROR_NOT_FOUND */ -RAC_LLAMACPP_API rac_result_t rac_llm_llamacpp_remove_lora(rac_handle_t handle, +RAC_LLAMACPP_API RAC_NODISCARD rac_result_t rac_llm_llamacpp_remove_lora(rac_handle_t handle, const char* adapter_path); /** @@ -253,7 +253,7 @@ RAC_LLAMACPP_API rac_result_t rac_llm_llamacpp_remove_lora(rac_handle_t handle, * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_LLAMACPP_API rac_result_t rac_llm_llamacpp_clear_lora(rac_handle_t handle); +RAC_LLAMACPP_API RAC_NODISCARD rac_result_t rac_llm_llamacpp_clear_lora(rac_handle_t handle); /** * Get info about loaded LoRA adapters as JSON. @@ -264,7 +264,7 @@ RAC_LLAMACPP_API rac_result_t rac_llm_llamacpp_clear_lora(rac_handle_t handle); * @param out_json Output: JSON string (caller must free with rac_free) * @return RAC_SUCCESS or error code */ -RAC_LLAMACPP_API rac_result_t rac_llm_llamacpp_get_lora_info(rac_handle_t handle, +RAC_LLAMACPP_API RAC_NODISCARD rac_result_t rac_llm_llamacpp_get_lora_info(rac_handle_t handle, char** out_json); // ============================================================================= @@ -279,7 +279,7 @@ RAC_LLAMACPP_API rac_result_t rac_llm_llamacpp_get_lora_info(rac_handle_t handle * @param prompt System prompt text * @return RAC_SUCCESS or error code */ -RAC_LLAMACPP_API rac_result_t rac_llm_llamacpp_inject_system_prompt(rac_handle_t handle, +RAC_LLAMACPP_API RAC_NODISCARD rac_result_t rac_llm_llamacpp_inject_system_prompt(rac_handle_t handle, const char* prompt); /** @@ -290,7 +290,7 @@ RAC_LLAMACPP_API rac_result_t rac_llm_llamacpp_inject_system_prompt(rac_handle_t * @param text Text to append * @return RAC_SUCCESS or error code */ -RAC_LLAMACPP_API rac_result_t rac_llm_llamacpp_append_context(rac_handle_t handle, +RAC_LLAMACPP_API RAC_NODISCARD rac_result_t rac_llm_llamacpp_append_context(rac_handle_t handle, const char* text); /** @@ -303,7 +303,7 @@ RAC_LLAMACPP_API rac_result_t rac_llm_llamacpp_append_context(rac_handle_t handl * @param out_result Output: Generation result * @return RAC_SUCCESS or error code */ -RAC_LLAMACPP_API rac_result_t rac_llm_llamacpp_generate_from_context( +RAC_LLAMACPP_API RAC_NODISCARD rac_result_t rac_llm_llamacpp_generate_from_context( rac_handle_t handle, const char* query, const rac_llm_options_t* options, rac_llm_result_t* out_result); @@ -313,7 +313,7 @@ RAC_LLAMACPP_API rac_result_t rac_llm_llamacpp_generate_from_context( * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_LLAMACPP_API rac_result_t rac_llm_llamacpp_clear_context(rac_handle_t handle); +RAC_LLAMACPP_API RAC_NODISCARD rac_result_t rac_llm_llamacpp_clear_context(rac_handle_t handle); // ============================================================================= // BACKEND REGISTRATION @@ -329,14 +329,14 @@ RAC_LLAMACPP_API rac_result_t rac_llm_llamacpp_clear_context(rac_handle_t handle * * @return RAC_SUCCESS or error code */ -RAC_LLAMACPP_API rac_result_t rac_backend_llamacpp_register(void); +RAC_LLAMACPP_API RAC_NODISCARD rac_result_t rac_backend_llamacpp_register(void); /** * Unregisters the LlamaCPP backend. * * @return RAC_SUCCESS or error code */ -RAC_LLAMACPP_API rac_result_t rac_backend_llamacpp_unregister(void); +RAC_LLAMACPP_API RAC_NODISCARD rac_result_t rac_backend_llamacpp_unregister(void); #ifdef __cplusplus } diff --git a/sdk/runanywhere-commons/include/rac/backends/rac_stt_onnx.h b/sdk/runanywhere-commons/include/rac/backends/rac_stt_onnx.h index 1a27c0c03..f224afd19 100644 --- a/sdk/runanywhere-commons/include/rac/backends/rac_stt_onnx.h +++ b/sdk/runanywhere-commons/include/rac/backends/rac_stt_onnx.h @@ -64,25 +64,25 @@ static const rac_stt_onnx_config_t RAC_STT_ONNX_CONFIG_DEFAULT = { // ONNX STT API // ============================================================================= -RAC_ONNX_API rac_result_t rac_stt_onnx_create(const char* model_path, +RAC_ONNX_API RAC_NODISCARD rac_result_t rac_stt_onnx_create(const char* model_path, const rac_stt_onnx_config_t* config, rac_handle_t* out_handle); -RAC_ONNX_API rac_result_t rac_stt_onnx_transcribe(rac_handle_t handle, const float* audio_samples, +RAC_ONNX_API RAC_NODISCARD rac_result_t rac_stt_onnx_transcribe(rac_handle_t handle, const float* audio_samples, size_t num_samples, const rac_stt_options_t* options, rac_stt_result_t* out_result); RAC_ONNX_API rac_bool_t rac_stt_onnx_supports_streaming(rac_handle_t handle); -RAC_ONNX_API rac_result_t rac_stt_onnx_create_stream(rac_handle_t handle, rac_handle_t* out_stream); +RAC_ONNX_API RAC_NODISCARD rac_result_t rac_stt_onnx_create_stream(rac_handle_t handle, rac_handle_t* out_stream); -RAC_ONNX_API rac_result_t rac_stt_onnx_feed_audio(rac_handle_t handle, rac_handle_t stream, +RAC_ONNX_API RAC_NODISCARD rac_result_t rac_stt_onnx_feed_audio(rac_handle_t handle, rac_handle_t stream, const float* audio_samples, size_t num_samples); RAC_ONNX_API rac_bool_t rac_stt_onnx_stream_is_ready(rac_handle_t handle, rac_handle_t stream); -RAC_ONNX_API rac_result_t rac_stt_onnx_decode_stream(rac_handle_t handle, rac_handle_t stream, +RAC_ONNX_API RAC_NODISCARD rac_result_t rac_stt_onnx_decode_stream(rac_handle_t handle, rac_handle_t stream, char** out_text); RAC_ONNX_API void rac_stt_onnx_input_finished(rac_handle_t handle, rac_handle_t stream); diff --git a/sdk/runanywhere-commons/include/rac/backends/rac_stt_whispercpp.h b/sdk/runanywhere-commons/include/rac/backends/rac_stt_whispercpp.h index 9ec8f2c7d..cdee661a0 100644 --- a/sdk/runanywhere-commons/include/rac/backends/rac_stt_whispercpp.h +++ b/sdk/runanywhere-commons/include/rac/backends/rac_stt_whispercpp.h @@ -83,7 +83,7 @@ static const rac_stt_whispercpp_config_t RAC_STT_WHISPERCPP_CONFIG_DEFAULT = { * @param out_handle Output: Handle to the created service * @return RAC_SUCCESS or error code */ -RAC_WHISPERCPP_API rac_result_t rac_stt_whispercpp_create(const char* model_path, +RAC_WHISPERCPP_API RAC_NODISCARD rac_result_t rac_stt_whispercpp_create(const char* model_path, const rac_stt_whispercpp_config_t* config, rac_handle_t* out_handle); @@ -97,7 +97,7 @@ RAC_WHISPERCPP_API rac_result_t rac_stt_whispercpp_create(const char* model_path * @param out_result Output: Transcription result * @return RAC_SUCCESS or error code */ -RAC_WHISPERCPP_API rac_result_t rac_stt_whispercpp_transcribe(rac_handle_t handle, +RAC_WHISPERCPP_API RAC_NODISCARD rac_result_t rac_stt_whispercpp_transcribe(rac_handle_t handle, const float* audio_samples, size_t num_samples, const rac_stt_options_t* options, @@ -110,7 +110,7 @@ RAC_WHISPERCPP_API rac_result_t rac_stt_whispercpp_transcribe(rac_handle_t handl * @param out_language Output: Language code (caller must free) * @return RAC_SUCCESS or error code */ -RAC_WHISPERCPP_API rac_result_t rac_stt_whispercpp_get_language(rac_handle_t handle, +RAC_WHISPERCPP_API RAC_NODISCARD rac_result_t rac_stt_whispercpp_get_language(rac_handle_t handle, char** out_language); /** @@ -137,14 +137,14 @@ RAC_WHISPERCPP_API void rac_stt_whispercpp_destroy(rac_handle_t handle); * * @return RAC_SUCCESS or error code */ -RAC_WHISPERCPP_API rac_result_t rac_backend_whispercpp_register(void); +RAC_WHISPERCPP_API RAC_NODISCARD rac_result_t rac_backend_whispercpp_register(void); /** * Unregisters the WhisperCPP backend. * * @return RAC_SUCCESS or error code */ -RAC_WHISPERCPP_API rac_result_t rac_backend_whispercpp_unregister(void); +RAC_WHISPERCPP_API RAC_NODISCARD rac_result_t rac_backend_whispercpp_unregister(void); #ifdef __cplusplus } diff --git a/sdk/runanywhere-commons/include/rac/backends/rac_stt_whisperkit_coreml.h b/sdk/runanywhere-commons/include/rac/backends/rac_stt_whisperkit_coreml.h index 660bb78c2..86b3edfe9 100644 --- a/sdk/runanywhere-commons/include/rac/backends/rac_stt_whisperkit_coreml.h +++ b/sdk/runanywhere-commons/include/rac/backends/rac_stt_whisperkit_coreml.h @@ -122,14 +122,14 @@ RAC_API rac_bool_t rac_whisperkit_coreml_stt_is_available(void); * * @return RAC_SUCCESS on success */ -RAC_API rac_result_t rac_backend_whisperkit_coreml_register(void); +RAC_API RAC_NODISCARD rac_result_t rac_backend_whisperkit_coreml_register(void); /** * Unregister the WhisperKit CoreML backend. * * @return RAC_SUCCESS on success */ -RAC_API rac_result_t rac_backend_whisperkit_coreml_unregister(void); +RAC_API RAC_NODISCARD rac_result_t rac_backend_whisperkit_coreml_unregister(void); #ifdef __cplusplus } diff --git a/sdk/runanywhere-commons/include/rac/backends/rac_tts_onnx.h b/sdk/runanywhere-commons/include/rac/backends/rac_tts_onnx.h index 8e8ae79a4..1704a46aa 100644 --- a/sdk/runanywhere-commons/include/rac/backends/rac_tts_onnx.h +++ b/sdk/runanywhere-commons/include/rac/backends/rac_tts_onnx.h @@ -49,15 +49,15 @@ static const rac_tts_onnx_config_t RAC_TTS_ONNX_CONFIG_DEFAULT = { // ONNX TTS API // ============================================================================= -RAC_ONNX_API rac_result_t rac_tts_onnx_create(const char* model_path, +RAC_ONNX_API RAC_NODISCARD rac_result_t rac_tts_onnx_create(const char* model_path, const rac_tts_onnx_config_t* config, rac_handle_t* out_handle); -RAC_ONNX_API rac_result_t rac_tts_onnx_synthesize(rac_handle_t handle, const char* text, +RAC_ONNX_API RAC_NODISCARD rac_result_t rac_tts_onnx_synthesize(rac_handle_t handle, const char* text, const rac_tts_options_t* options, rac_tts_result_t* out_result); -RAC_ONNX_API rac_result_t rac_tts_onnx_get_voices(rac_handle_t handle, char*** out_voices, +RAC_ONNX_API RAC_NODISCARD rac_result_t rac_tts_onnx_get_voices(rac_handle_t handle, char*** out_voices, size_t* out_count); RAC_ONNX_API void rac_tts_onnx_stop(rac_handle_t handle); diff --git a/sdk/runanywhere-commons/include/rac/backends/rac_vad_onnx.h b/sdk/runanywhere-commons/include/rac/backends/rac_vad_onnx.h index fb3c51658..ac83f52e4 100644 --- a/sdk/runanywhere-commons/include/rac/backends/rac_vad_onnx.h +++ b/sdk/runanywhere-commons/include/rac/backends/rac_vad_onnx.h @@ -50,20 +50,20 @@ static const rac_vad_onnx_config_t RAC_VAD_ONNX_CONFIG_DEFAULT = { // ONNX VAD API // ============================================================================= -RAC_ONNX_API rac_result_t rac_vad_onnx_create(const char* model_path, +RAC_ONNX_API RAC_NODISCARD rac_result_t rac_vad_onnx_create(const char* model_path, const rac_vad_onnx_config_t* config, rac_handle_t* out_handle); -RAC_ONNX_API rac_result_t rac_vad_onnx_process(rac_handle_t handle, const float* samples, +RAC_ONNX_API RAC_NODISCARD rac_result_t rac_vad_onnx_process(rac_handle_t handle, const float* samples, size_t num_samples, rac_bool_t* out_is_speech); -RAC_ONNX_API rac_result_t rac_vad_onnx_start(rac_handle_t handle); +RAC_ONNX_API RAC_NODISCARD rac_result_t rac_vad_onnx_start(rac_handle_t handle); -RAC_ONNX_API rac_result_t rac_vad_onnx_stop(rac_handle_t handle); +RAC_ONNX_API RAC_NODISCARD rac_result_t rac_vad_onnx_stop(rac_handle_t handle); -RAC_ONNX_API rac_result_t rac_vad_onnx_reset(rac_handle_t handle); +RAC_ONNX_API RAC_NODISCARD rac_result_t rac_vad_onnx_reset(rac_handle_t handle); -RAC_ONNX_API rac_result_t rac_vad_onnx_set_threshold(rac_handle_t handle, float threshold); +RAC_ONNX_API RAC_NODISCARD rac_result_t rac_vad_onnx_set_threshold(rac_handle_t handle, float threshold); RAC_ONNX_API rac_bool_t rac_vad_onnx_is_speech_active(rac_handle_t handle); @@ -73,9 +73,9 @@ RAC_ONNX_API void rac_vad_onnx_destroy(rac_handle_t handle); // BACKEND REGISTRATION // ============================================================================= -RAC_ONNX_API rac_result_t rac_backend_onnx_register(void); +RAC_ONNX_API RAC_NODISCARD rac_result_t rac_backend_onnx_register(void); -RAC_ONNX_API rac_result_t rac_backend_onnx_unregister(void); +RAC_ONNX_API RAC_NODISCARD rac_result_t rac_backend_onnx_unregister(void); #ifdef __cplusplus } diff --git a/sdk/runanywhere-commons/include/rac/backends/rac_vlm_llamacpp.h b/sdk/runanywhere-commons/include/rac/backends/rac_vlm_llamacpp.h index b44071845..ab72969c1 100644 --- a/sdk/runanywhere-commons/include/rac/backends/rac_vlm_llamacpp.h +++ b/sdk/runanywhere-commons/include/rac/backends/rac_vlm_llamacpp.h @@ -86,7 +86,7 @@ static const rac_vlm_llamacpp_config_t RAC_VLM_LLAMACPP_CONFIG_DEFAULT = { * @param out_handle Output: Handle to the created service * @return RAC_SUCCESS or error code */ -RAC_LLAMACPP_VLM_API rac_result_t rac_vlm_llamacpp_create(const char* model_path, +RAC_LLAMACPP_VLM_API RAC_NODISCARD rac_result_t rac_vlm_llamacpp_create(const char* model_path, const char* mmproj_path, const rac_vlm_llamacpp_config_t* config, rac_handle_t* out_handle); @@ -100,7 +100,7 @@ RAC_LLAMACPP_VLM_API rac_result_t rac_vlm_llamacpp_create(const char* model_path * @param config LlamaCPP configuration (can be NULL) * @return RAC_SUCCESS or error code */ -RAC_LLAMACPP_VLM_API rac_result_t rac_vlm_llamacpp_load_model( +RAC_LLAMACPP_VLM_API RAC_NODISCARD rac_result_t rac_vlm_llamacpp_load_model( rac_handle_t handle, const char* model_path, const char* mmproj_path, const rac_vlm_llamacpp_config_t* config); @@ -110,7 +110,7 @@ RAC_LLAMACPP_VLM_API rac_result_t rac_vlm_llamacpp_load_model( * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_LLAMACPP_VLM_API rac_result_t rac_vlm_llamacpp_unload_model(rac_handle_t handle); +RAC_LLAMACPP_VLM_API RAC_NODISCARD rac_result_t rac_vlm_llamacpp_unload_model(rac_handle_t handle); /** * Checks if a model is loaded. @@ -130,7 +130,7 @@ RAC_LLAMACPP_VLM_API rac_bool_t rac_vlm_llamacpp_is_model_loaded(rac_handle_t ha * @param out_result Output: Generation result (caller must free text with rac_free) * @return RAC_SUCCESS or error code */ -RAC_LLAMACPP_VLM_API rac_result_t rac_vlm_llamacpp_process(rac_handle_t handle, +RAC_LLAMACPP_VLM_API RAC_NODISCARD rac_result_t rac_vlm_llamacpp_process(rac_handle_t handle, const rac_vlm_image_t* image, const char* prompt, const rac_vlm_options_t* options, @@ -158,7 +158,7 @@ typedef rac_bool_t (*rac_vlm_llamacpp_stream_callback_fn)(const char* token, rac * @param user_data User context passed to callback * @return RAC_SUCCESS or error code */ -RAC_LLAMACPP_VLM_API rac_result_t rac_vlm_llamacpp_process_stream( +RAC_LLAMACPP_VLM_API RAC_NODISCARD rac_result_t rac_vlm_llamacpp_process_stream( rac_handle_t handle, const rac_vlm_image_t* image, const char* prompt, const rac_vlm_options_t* options, rac_vlm_llamacpp_stream_callback_fn callback, void* user_data); @@ -176,7 +176,7 @@ RAC_LLAMACPP_VLM_API void rac_vlm_llamacpp_cancel(rac_handle_t handle); * @param out_json Output: JSON string (caller must free with rac_free) * @return RAC_SUCCESS or error code */ -RAC_LLAMACPP_VLM_API rac_result_t rac_vlm_llamacpp_get_model_info(rac_handle_t handle, +RAC_LLAMACPP_VLM_API RAC_NODISCARD rac_result_t rac_vlm_llamacpp_get_model_info(rac_handle_t handle, char** out_json); /** @@ -200,14 +200,14 @@ RAC_LLAMACPP_VLM_API void rac_vlm_llamacpp_destroy(rac_handle_t handle); * * @return RAC_SUCCESS or error code */ -RAC_LLAMACPP_VLM_API rac_result_t rac_backend_llamacpp_vlm_register(void); +RAC_LLAMACPP_VLM_API RAC_NODISCARD rac_result_t rac_backend_llamacpp_vlm_register(void); /** * Unregisters the LlamaCPP VLM backend. * * @return RAC_SUCCESS or error code */ -RAC_LLAMACPP_VLM_API rac_result_t rac_backend_llamacpp_vlm_unregister(void); +RAC_LLAMACPP_VLM_API RAC_NODISCARD rac_result_t rac_backend_llamacpp_vlm_unregister(void); #ifdef __cplusplus } diff --git a/sdk/runanywhere-commons/include/rac/backends/rac_wakeword_onnx.h b/sdk/runanywhere-commons/include/rac/backends/rac_wakeword_onnx.h index 0b95066b3..090866e8b 100644 --- a/sdk/runanywhere-commons/include/rac/backends/rac_wakeword_onnx.h +++ b/sdk/runanywhere-commons/include/rac/backends/rac_wakeword_onnx.h @@ -95,7 +95,7 @@ static const rac_wakeword_onnx_config_t RAC_WAKEWORD_ONNX_CONFIG_DEFAULT = { * @param[out] out_handle Output: Detector handle * @return RAC_SUCCESS or error code */ -RAC_ONNX_API rac_result_t rac_wakeword_onnx_create( +RAC_ONNX_API RAC_NODISCARD rac_result_t rac_wakeword_onnx_create( const rac_wakeword_onnx_config_t* config, rac_handle_t* out_handle); @@ -110,7 +110,7 @@ RAC_ONNX_API rac_result_t rac_wakeword_onnx_create( * @param melspec_model_path Path to melspectrogram model ONNX file (optional) * @return RAC_SUCCESS or error code */ -RAC_ONNX_API rac_result_t rac_wakeword_onnx_init_shared_models( +RAC_ONNX_API RAC_NODISCARD rac_result_t rac_wakeword_onnx_init_shared_models( rac_handle_t handle, const char* embedding_model_path, const char* melspec_model_path); @@ -124,7 +124,7 @@ RAC_ONNX_API rac_result_t rac_wakeword_onnx_init_shared_models( * @param wake_word Human-readable wake word * @return RAC_SUCCESS or error code */ -RAC_ONNX_API rac_result_t rac_wakeword_onnx_load_model( +RAC_ONNX_API RAC_NODISCARD rac_result_t rac_wakeword_onnx_load_model( rac_handle_t handle, const char* model_path, const char* model_id, @@ -137,7 +137,7 @@ RAC_ONNX_API rac_result_t rac_wakeword_onnx_load_model( * @param vad_model_path Path to Silero VAD ONNX model * @return RAC_SUCCESS or error code */ -RAC_ONNX_API rac_result_t rac_wakeword_onnx_load_vad( +RAC_ONNX_API RAC_NODISCARD rac_result_t rac_wakeword_onnx_load_vad( rac_handle_t handle, const char* vad_model_path); @@ -151,7 +151,7 @@ RAC_ONNX_API rac_result_t rac_wakeword_onnx_load_vad( * @param[out] out_confidence Detection confidence (0.0 - 1.0) * @return RAC_SUCCESS or error code */ -RAC_ONNX_API rac_result_t rac_wakeword_onnx_process( +RAC_ONNX_API RAC_NODISCARD rac_result_t rac_wakeword_onnx_process( rac_handle_t handle, const float* samples, size_t num_samples, @@ -170,7 +170,7 @@ RAC_ONNX_API rac_result_t rac_wakeword_onnx_process( * @param[out] out_vad_confidence VAD confidence * @return RAC_SUCCESS or error code */ -RAC_ONNX_API rac_result_t rac_wakeword_onnx_process_with_vad( +RAC_ONNX_API RAC_NODISCARD rac_result_t rac_wakeword_onnx_process_with_vad( rac_handle_t handle, const float* samples, size_t num_samples, @@ -186,7 +186,7 @@ RAC_ONNX_API rac_result_t rac_wakeword_onnx_process_with_vad( * @param threshold New threshold (0.0 - 1.0) * @return RAC_SUCCESS or error code */ -RAC_ONNX_API rac_result_t rac_wakeword_onnx_set_threshold( +RAC_ONNX_API RAC_NODISCARD rac_result_t rac_wakeword_onnx_set_threshold( rac_handle_t handle, float threshold); @@ -196,7 +196,7 @@ RAC_ONNX_API rac_result_t rac_wakeword_onnx_set_threshold( * @param handle Detector handle * @return RAC_SUCCESS or error code */ -RAC_ONNX_API rac_result_t rac_wakeword_onnx_reset(rac_handle_t handle); +RAC_ONNX_API RAC_NODISCARD rac_result_t rac_wakeword_onnx_reset(rac_handle_t handle); /** * @brief Unload a wake word model @@ -205,7 +205,7 @@ RAC_ONNX_API rac_result_t rac_wakeword_onnx_reset(rac_handle_t handle); * @param model_id Model identifier to unload * @return RAC_SUCCESS or error code */ -RAC_ONNX_API rac_result_t rac_wakeword_onnx_unload_model( +RAC_ONNX_API RAC_NODISCARD rac_result_t rac_wakeword_onnx_unload_model( rac_handle_t handle, const char* model_id); @@ -227,14 +227,14 @@ RAC_ONNX_API void rac_wakeword_onnx_destroy(rac_handle_t handle); * * @return RAC_SUCCESS or error code */ -RAC_ONNX_API rac_result_t rac_backend_wakeword_onnx_register(void); +RAC_ONNX_API RAC_NODISCARD rac_result_t rac_backend_wakeword_onnx_register(void); /** * @brief Unregister ONNX wake word backend * * @return RAC_SUCCESS or error code */ -RAC_ONNX_API rac_result_t rac_backend_wakeword_onnx_unregister(void); +RAC_ONNX_API RAC_NODISCARD rac_result_t rac_backend_wakeword_onnx_unregister(void); #ifdef __cplusplus } diff --git a/sdk/runanywhere-commons/include/rac/core/capabilities/rac_lifecycle.h b/sdk/runanywhere-commons/include/rac/core/capabilities/rac_lifecycle.h index 75456e7e7..1c68c3f4a 100644 --- a/sdk/runanywhere-commons/include/rac/core/capabilities/rac_lifecycle.h +++ b/sdk/runanywhere-commons/include/rac/core/capabilities/rac_lifecycle.h @@ -131,7 +131,7 @@ typedef void (*rac_lifecycle_destroy_service_fn)(rac_handle_t service, void* use * @param out_handle Output: Handle to the lifecycle manager * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_lifecycle_create(const rac_lifecycle_config_t* config, +RAC_API RAC_NODISCARD rac_result_t rac_lifecycle_create(const rac_lifecycle_config_t* config, rac_lifecycle_create_service_fn create_fn, rac_lifecycle_destroy_service_fn destroy_fn, rac_handle_t* out_handle); @@ -151,7 +151,7 @@ RAC_API rac_result_t rac_lifecycle_create(const rac_lifecycle_config_t* config, * @param out_service Output: Handle to the loaded service * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_lifecycle_load(rac_handle_t handle, const char* model_path, +RAC_API RAC_NODISCARD rac_result_t rac_lifecycle_load(rac_handle_t handle, const char* model_path, const char* model_id, const char* model_name, rac_handle_t* out_service); @@ -163,7 +163,7 @@ RAC_API rac_result_t rac_lifecycle_load(rac_handle_t handle, const char* model_p * @param handle Lifecycle manager handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_lifecycle_unload(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_lifecycle_unload(rac_handle_t handle); /** * @brief Reset all state @@ -173,7 +173,7 @@ RAC_API rac_result_t rac_lifecycle_unload(rac_handle_t handle); * @param handle Lifecycle manager handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_lifecycle_reset(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_lifecycle_reset(rac_handle_t handle); /** * @brief Get current lifecycle state @@ -232,7 +232,7 @@ RAC_API rac_handle_t rac_lifecycle_get_service(rac_handle_t handle); * @param out_service Output: Service handle * @return RAC_SUCCESS or RAC_ERROR_NOT_INITIALIZED if not loaded */ -RAC_API rac_result_t rac_lifecycle_require_service(rac_handle_t handle, rac_handle_t* out_service); +RAC_API RAC_NODISCARD rac_result_t rac_lifecycle_require_service(rac_handle_t handle, rac_handle_t* out_service); /** * @brief Acquire (pin) the current service, preventing unload while held. @@ -244,7 +244,7 @@ RAC_API rac_result_t rac_lifecycle_require_service(rac_handle_t handle, rac_hand * @param out_service Output: Service handle (pinned) * @return RAC_SUCCESS or RAC_ERROR_NOT_INITIALIZED if not loaded */ -RAC_API rac_result_t rac_lifecycle_acquire_service(rac_handle_t handle, rac_handle_t* out_service); +RAC_API RAC_NODISCARD rac_result_t rac_lifecycle_acquire_service(rac_handle_t handle, rac_handle_t* out_service); /** * @brief Release a previously acquired service reference. @@ -274,7 +274,7 @@ RAC_API void rac_lifecycle_track_error(rac_handle_t handle, rac_result_t error_c * @param out_metrics Output: Lifecycle metrics * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_lifecycle_get_metrics(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_lifecycle_get_metrics(rac_handle_t handle, rac_lifecycle_metrics_t* out_metrics); /** diff --git a/sdk/runanywhere-commons/include/rac/core/rac_analytics_events.h b/sdk/runanywhere-commons/include/rac/core/rac_analytics_events.h index a4523ad6b..f6b45d03c 100644 --- a/sdk/runanywhere-commons/include/rac/core/rac_analytics_events.h +++ b/sdk/runanywhere-commons/include/rac/core/rac_analytics_events.h @@ -448,7 +448,7 @@ typedef void (*rac_analytics_callback_fn)(rac_event_type_t type, * @param user_data User data passed to callback * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_analytics_events_set_callback(rac_analytics_callback_fn callback, +RAC_API RAC_NODISCARD rac_result_t rac_analytics_events_set_callback(rac_analytics_callback_fn callback, void* user_data); /** @@ -500,7 +500,7 @@ typedef void (*rac_public_event_callback_fn)(rac_event_type_t type, * @param user_data User data passed to callback * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_analytics_events_set_public_callback(rac_public_event_callback_fn callback, +RAC_API RAC_NODISCARD rac_result_t rac_analytics_events_set_public_callback(rac_public_event_callback_fn callback, void* user_data); /** diff --git a/sdk/runanywhere-commons/include/rac/core/rac_attrs.h b/sdk/runanywhere-commons/include/rac/core/rac_attrs.h new file mode 100644 index 000000000..655a485a1 --- /dev/null +++ b/sdk/runanywhere-commons/include/rac/core/rac_attrs.h @@ -0,0 +1,178 @@ +/** + * @file rac_attrs.h + * @brief RunAnywhere Commons - Compiler Attribute Macros + * + * Portable macros for C and C++ attributes used across the public RAC API: + * - RAC_NODISCARD : warn when a return value is silently ignored + * - RAC_NONNULL(...) : assert that pointer parameters cannot be NULL + * - RAC_DEPRECATED(msg) : mark a symbol as deprecated with a migration hint + * - RAC_ATTR_PRINTF(f,a) : printf-style format check for variadic functions + * - RAC_NORETURN : function does not return (fatal error helpers) + * + * All macros degrade gracefully to nothing when the compiler does not support + * the underlying attribute, so the header is safe to include from any C or + * C++ translation unit on any supported platform. + * + * Compiler support matrix: + * | Attribute | Clang | GCC | MSVC | Apple Clang | Android NDK | + * | nodiscard | 3.9+ | 4.8+| 19.14+| 3.9+ | r14+ | + * | nonnull | 3.9+ | 4.0+| n/a | 3.9+ | r14+ | + * | deprecated(msg) | 3.9+ | 4.5+| 14.0+| 3.9+ | r14+ | + * + * For MSVC, RAC_NONNULL maps to _In_ SAL annotations where applicable. + * + * Include this header from every public RAC header that exposes functions + * returning rac_result_t or taking pointer parameters. + */ + +#ifndef RAC_ATTRS_H +#define RAC_ATTRS_H + +/* --------------------------------------------------------------------------- + * RAC_NODISCARD - warn if return value is ignored + * + * Usage: + * RAC_API RAC_NODISCARD rac_result_t rac_init(const rac_config_t* config); + * + * Callers that write `rac_init(&cfg);` without capturing the result will get + * a compiler warning (or error with -Werror). This catches silent error code + * drops, the #1 source of bugs in C-style API consumers. + * --------------------------------------------------------------------------- */ +#if defined(__cplusplus) && __cplusplus >= 201703L + /* C++17 [[nodiscard]] */ + #define RAC_NODISCARD [[nodiscard]] +#elif defined(__has_attribute) + #if __has_attribute(warn_unused_result) + #define RAC_NODISCARD __attribute__((warn_unused_result)) + #endif +#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 304) + /* GCC 3.4+ supports warn_unused_result */ + #define RAC_NODISCARD __attribute__((warn_unused_result)) +#endif +#if defined(_MSC_VER) && _MSC_VER >= 1700 && !defined(RAC_NODISCARD) + /* MSVC 2012+: _Check_return_ is a SAL annotation that triggers /analyze */ + #define RAC_NODISCARD _Check_return_ +#endif +#ifndef RAC_NODISCARD + #define RAC_NODISCARD +#endif + +/* --------------------------------------------------------------------------- + * RAC_NONNULL(...) - mark pointer parameters as non-null + * + * Usage: + * RAC_API rac_result_t rac_module_register( + * RAC_NONNULL(1) const rac_module_info_t* info); + * + * Index arguments are 1-based (because GCC / Clang count them that way). + * For member functions in C++ the implicit `this` is parameter 1, so shift. + * + * MSVC does not have a direct equivalent; falls back to empty macro (the + * intent can still be checked manually / via clang-tidy's + * bugprone-not-null-terminated-result check). + * --------------------------------------------------------------------------- */ +#if defined(__has_attribute) + #if __has_attribute(nonnull) + #define RAC_NONNULL(...) __attribute__((nonnull(__VA_ARGS__))) + #endif +#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 400) + #define RAC_NONNULL(...) __attribute__((nonnull(__VA_ARGS__))) +#endif +#ifndef RAC_NONNULL + #define RAC_NONNULL(...) +#endif + +/* Variant: mark ALL pointer params as nonnull (no indices). */ +#if defined(__has_attribute) + #if __has_attribute(nonnull) + #define RAC_NONNULL_ALL __attribute__((nonnull)) + #endif +#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 400) + #define RAC_NONNULL_ALL __attribute__((nonnull)) +#endif +#ifndef RAC_NONNULL_ALL + #define RAC_NONNULL_ALL +#endif + +/* --------------------------------------------------------------------------- + * RAC_DEPRECATED(msg) - mark a symbol as deprecated with migration text + * + * Usage: + * RAC_DEPRECATED("use rac_llm_generate_v2 instead") + * RAC_API rac_result_t rac_llm_generate(rac_handle_t h, ...); + * --------------------------------------------------------------------------- */ +#if defined(__cplusplus) && __cplusplus >= 201402L + #define RAC_DEPRECATED(msg) [[deprecated(msg)]] +#elif defined(__has_attribute) + #if __has_attribute(deprecated) + #if defined(__clang__) || (defined(__GNUC__) && __GNUC__ >= 5) + #define RAC_DEPRECATED(msg) __attribute__((deprecated(msg))) + #else + #define RAC_DEPRECATED(msg) __attribute__((deprecated)) + #endif + #endif +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + #define RAC_DEPRECATED(msg) __declspec(deprecated(msg)) +#endif +#ifndef RAC_DEPRECATED + #define RAC_DEPRECATED(msg) +#endif + +/* --------------------------------------------------------------------------- + * RAC_ATTR_PRINTF(fmt_index, args_index) - printf-style format checking + * + * Usage (variadic logger wrapper): + * RAC_API void rac_logger_log_fmt(rac_log_level_t lvl, + * const char* fmt, ...) + * RAC_ATTR_PRINTF(2, 3); + * --------------------------------------------------------------------------- */ +#if defined(__has_attribute) + #if __has_attribute(format) + #define RAC_ATTR_PRINTF(fmt_idx, args_idx) \ + __attribute__((format(printf, fmt_idx, args_idx))) + #endif +#elif defined(__GNUC__) + #define RAC_ATTR_PRINTF(fmt_idx, args_idx) \ + __attribute__((format(printf, fmt_idx, args_idx))) +#endif +#ifndef RAC_ATTR_PRINTF + #define RAC_ATTR_PRINTF(fmt_idx, args_idx) +#endif + +/* --------------------------------------------------------------------------- + * RAC_NORETURN - function does not return + * --------------------------------------------------------------------------- */ +#if defined(__cplusplus) && __cplusplus >= 201103L + #define RAC_NORETURN [[noreturn]] +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + #define RAC_NORETURN _Noreturn +#elif defined(__GNUC__) + #define RAC_NORETURN __attribute__((noreturn)) +#elif defined(_MSC_VER) + #define RAC_NORETURN __declspec(noreturn) +#else + #define RAC_NORETURN +#endif + +/* --------------------------------------------------------------------------- + * RAC_PURE - function result depends only on its arguments (no globals, no I/O) + * RAC_CONST - like pure, but also must not read any memory through a pointer + * + * Both enable compile-time deduplication and CSE by the optimizer. + * --------------------------------------------------------------------------- */ +#if defined(__has_attribute) + #if __has_attribute(pure) + #define RAC_PURE __attribute__((pure)) + #endif + #if __has_attribute(const) + #define RAC_CONST_FN __attribute__((const)) + #endif +#endif +#ifndef RAC_PURE + #define RAC_PURE +#endif +#ifndef RAC_CONST_FN + #define RAC_CONST_FN +#endif + +#endif /* RAC_ATTRS_H */ diff --git a/sdk/runanywhere-commons/include/rac/core/rac_audio_utils.h b/sdk/runanywhere-commons/include/rac/core/rac_audio_utils.h index 7bf6b8407..93ff98507 100644 --- a/sdk/runanywhere-commons/include/rac/core/rac_audio_utils.h +++ b/sdk/runanywhere-commons/include/rac/core/rac_audio_utils.h @@ -54,7 +54,7 @@ extern "C" { * } * @endcode */ -RAC_API rac_result_t rac_audio_float32_to_wav(const void* pcm_data, size_t pcm_size, +RAC_API RAC_NODISCARD rac_result_t rac_audio_float32_to_wav(const void* pcm_data, size_t pcm_size, int32_t sample_rate, void** out_wav_data, size_t* out_wav_size); @@ -70,7 +70,7 @@ RAC_API rac_result_t rac_audio_float32_to_wav(const void* pcm_data, size_t pcm_s * @param out_wav_size Output: Size of WAV data in bytes * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_audio_int16_to_wav(const void* pcm_data, size_t pcm_size, +RAC_API RAC_NODISCARD rac_result_t rac_audio_int16_to_wav(const void* pcm_data, size_t pcm_size, int32_t sample_rate, void** out_wav_data, size_t* out_wav_size); diff --git a/sdk/runanywhere-commons/include/rac/core/rac_benchmark_log.h b/sdk/runanywhere-commons/include/rac/core/rac_benchmark_log.h index 640e7ccc5..4f2bd8523 100644 --- a/sdk/runanywhere-commons/include/rac/core/rac_benchmark_log.h +++ b/sdk/runanywhere-commons/include/rac/core/rac_benchmark_log.h @@ -63,7 +63,7 @@ extern "C" { * RAC_ERROR_NULL_POINTER if timing or out_json is NULL, * RAC_ERROR_OUT_OF_MEMORY if allocation fails */ -RAC_API rac_result_t rac_benchmark_timing_to_json(const rac_benchmark_timing_t* timing, +RAC_API RAC_NODISCARD rac_result_t rac_benchmark_timing_to_json(const rac_benchmark_timing_t* timing, char** out_json); // ============================================================================= @@ -88,7 +88,7 @@ RAC_API rac_result_t rac_benchmark_timing_to_json(const rac_benchmark_timing_t* * and timing is NULL, * RAC_ERROR_OUT_OF_MEMORY if allocation fails */ -RAC_API rac_result_t rac_benchmark_timing_to_csv(const rac_benchmark_timing_t* timing, +RAC_API RAC_NODISCARD rac_result_t rac_benchmark_timing_to_csv(const rac_benchmark_timing_t* timing, rac_bool_t header, char** out_csv); @@ -109,7 +109,7 @@ RAC_API rac_result_t rac_benchmark_timing_to_csv(const rac_benchmark_timing_t* t * @return RAC_SUCCESS on success, * RAC_ERROR_NULL_POINTER if timing is NULL */ -RAC_API rac_result_t rac_benchmark_timing_log(const rac_benchmark_timing_t* timing, +RAC_API RAC_NODISCARD rac_result_t rac_benchmark_timing_log(const rac_benchmark_timing_t* timing, const char* label); #ifdef __cplusplus diff --git a/sdk/runanywhere-commons/include/rac/core/rac_benchmark_stats.h b/sdk/runanywhere-commons/include/rac/core/rac_benchmark_stats.h index 71ca56198..f33c43462 100644 --- a/sdk/runanywhere-commons/include/rac/core/rac_benchmark_stats.h +++ b/sdk/runanywhere-commons/include/rac/core/rac_benchmark_stats.h @@ -106,7 +106,7 @@ typedef struct rac_benchmark_summary { * @param out_handle Output: collector handle * @return RAC_SUCCESS or RAC_ERROR_NULL_POINTER */ -RAC_API rac_result_t rac_benchmark_stats_create(rac_benchmark_stats_handle_t* out_handle); +RAC_API RAC_NODISCARD rac_result_t rac_benchmark_stats_create(rac_benchmark_stats_handle_t* out_handle); /** * Destroys a stats collector and frees all associated memory. @@ -151,7 +151,7 @@ RAC_API int32_t rac_benchmark_stats_count(rac_benchmark_stats_handle_t handle); * @param out_summary Output: summary struct * @return RAC_SUCCESS, RAC_ERROR_NULL_POINTER, or RAC_ERROR_INVALID_STATE (no data) */ -RAC_API rac_result_t rac_benchmark_stats_get_summary(rac_benchmark_stats_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_benchmark_stats_get_summary(rac_benchmark_stats_handle_t handle, rac_benchmark_summary_t* out_summary); /** diff --git a/sdk/runanywhere-commons/include/rac/core/rac_core.h b/sdk/runanywhere-commons/include/rac/core/rac_core.h index 46e2479a6..dde144b7a 100644 --- a/sdk/runanywhere-commons/include/rac/core/rac_core.h +++ b/sdk/runanywhere-commons/include/rac/core/rac_core.h @@ -63,7 +63,7 @@ typedef struct rac_config { * @note HTTP requests return RAC_ERROR_NOT_SUPPORTED - networking should be * handled by the SDK layer (Swift/Kotlin), not the C++ layer. */ -RAC_API rac_result_t rac_init(const rac_config_t* config); +RAC_API RAC_NODISCARD rac_result_t rac_init(const rac_config_t* config); /** * Shuts down the commons library. @@ -100,7 +100,7 @@ RAC_API rac_version_t rac_get_version(void); * @param environment The current SDK environment * @return RAC_SUCCESS on success */ -RAC_API rac_result_t rac_configure_logging(rac_environment_t environment); +RAC_API RAC_NODISCARD rac_result_t rac_configure_logging(rac_environment_t environment); // ============================================================================= // MODULE INFORMATION @@ -133,7 +133,7 @@ typedef struct rac_module_info { * @param info Module information (copied internally) * @return RAC_SUCCESS on success, or an error code on failure */ -RAC_API rac_result_t rac_module_register(const rac_module_info_t* info); +RAC_API RAC_NODISCARD rac_result_t rac_module_register(const rac_module_info_t* info); /** * Unregisters a module from the registry. @@ -141,7 +141,7 @@ RAC_API rac_result_t rac_module_register(const rac_module_info_t* info); * @param module_id The unique ID of the module to unregister * @return RAC_SUCCESS on success, or an error code on failure */ -RAC_API rac_result_t rac_module_unregister(const char* module_id); +RAC_API RAC_NODISCARD rac_result_t rac_module_unregister(const char* module_id); /** * Gets the list of registered modules. @@ -152,7 +152,7 @@ RAC_API rac_result_t rac_module_unregister(const char* module_id); * * @note The returned list is valid until the next module registration/unregistration. */ -RAC_API rac_result_t rac_module_list(const rac_module_info_t** out_modules, size_t* out_count); +RAC_API RAC_NODISCARD rac_result_t rac_module_list(const rac_module_info_t** out_modules, size_t* out_count); /** * Gets modules that provide a specific capability. @@ -162,7 +162,7 @@ RAC_API rac_result_t rac_module_list(const rac_module_info_t** out_modules, size * @param out_count Pointer to receive the number of modules * @return RAC_SUCCESS on success, or an error code on failure */ -RAC_API rac_result_t rac_modules_for_capability(rac_capability_t capability, +RAC_API RAC_NODISCARD rac_result_t rac_modules_for_capability(rac_capability_t capability, const rac_module_info_t** out_modules, size_t* out_count); @@ -173,7 +173,7 @@ RAC_API rac_result_t rac_modules_for_capability(rac_capability_t capability, * @param out_info Pointer to receive the module info (do not free) * @return RAC_SUCCESS on success, or RAC_ERROR_MODULE_NOT_FOUND if not found */ -RAC_API rac_result_t rac_module_get_info(const char* module_id, const rac_module_info_t** out_info); +RAC_API RAC_NODISCARD rac_result_t rac_module_get_info(const char* module_id, const rac_module_info_t** out_info); // ============================================================================= // SERVICE PROVIDER API - Mirrors Swift's ServiceRegistry @@ -257,7 +257,7 @@ typedef struct rac_service_provider { * @param provider Provider information (copied internally) * @return RAC_SUCCESS on success, or an error code on failure */ -RAC_API rac_result_t rac_service_register_provider(const rac_service_provider_t* provider); +RAC_API RAC_NODISCARD rac_result_t rac_service_register_provider(const rac_service_provider_t* provider); /** * Unregisters a service provider. @@ -266,7 +266,7 @@ RAC_API rac_result_t rac_service_register_provider(const rac_service_provider_t* * @param capability The capability the provider was registered for * @return RAC_SUCCESS on success, or an error code on failure */ -RAC_API rac_result_t rac_service_unregister_provider(const char* name, rac_capability_t capability); +RAC_API RAC_NODISCARD rac_result_t rac_service_unregister_provider(const char* name, rac_capability_t capability); /** * Creates a service for a specific capability. @@ -279,7 +279,7 @@ RAC_API rac_result_t rac_service_unregister_provider(const char* name, rac_capab * @param out_handle Pointer to receive the service handle * @return RAC_SUCCESS on success, or an error code on failure */ -RAC_API rac_result_t rac_service_create(rac_capability_t capability, +RAC_API RAC_NODISCARD rac_result_t rac_service_create(rac_capability_t capability, const rac_service_request_t* request, rac_handle_t* out_handle); @@ -291,7 +291,7 @@ RAC_API rac_result_t rac_service_create(rac_capability_t capability, * @param out_count Pointer to receive count * @return RAC_SUCCESS on success */ -RAC_API rac_result_t rac_service_list_providers(rac_capability_t capability, +RAC_API RAC_NODISCARD rac_result_t rac_service_list_providers(rac_capability_t capability, const char*** out_names, size_t* out_count); // ============================================================================= @@ -313,7 +313,7 @@ RAC_API struct rac_model_registry* rac_get_model_registry(void); * @param model Model info to register * @return RAC_SUCCESS on success, or error code */ -RAC_API rac_result_t rac_register_model(const struct rac_model_info* model); +RAC_API RAC_NODISCARD rac_result_t rac_register_model(const struct rac_model_info* model); /** * Gets model info from the global registry. @@ -323,7 +323,7 @@ RAC_API rac_result_t rac_register_model(const struct rac_model_info* model); * @param out_model Output: Model info (owned, must be freed with rac_model_info_free) * @return RAC_SUCCESS on success, RAC_ERROR_NOT_FOUND if not registered */ -RAC_API rac_result_t rac_get_model(const char* model_id, struct rac_model_info** out_model); +RAC_API RAC_NODISCARD rac_result_t rac_get_model(const char* model_id, struct rac_model_info** out_model); /** * Gets model info from the global registry by local path. @@ -334,7 +334,7 @@ RAC_API rac_result_t rac_get_model(const char* model_id, struct rac_model_info** * @param out_model Output: Model info (owned, must be freed with rac_model_info_free) * @return RAC_SUCCESS on success, RAC_ERROR_NOT_FOUND if not registered */ -RAC_API rac_result_t rac_get_model_by_path(const char* local_path, struct rac_model_info** out_model); +RAC_API RAC_NODISCARD rac_result_t rac_get_model_by_path(const char* local_path, struct rac_model_info** out_model); // ============================================================================= // GLOBAL LORA REGISTRY API @@ -354,7 +354,7 @@ RAC_API struct rac_lora_registry* rac_get_lora_registry(void); * @param entry Adapter entry to register (deep-copied internally) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_register_lora(const struct rac_lora_entry* entry); +RAC_API RAC_NODISCARD rac_result_t rac_register_lora(const struct rac_lora_entry* entry); /** * @brief Query the global registry for adapters compatible with a model @@ -363,7 +363,7 @@ RAC_API rac_result_t rac_register_lora(const struct rac_lora_entry* entry); * @param out_count Output: number of matching entries * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_get_lora_for_model(const char* model_id, +RAC_API RAC_NODISCARD rac_result_t rac_get_lora_for_model(const char* model_id, struct rac_lora_entry*** out_entries, size_t* out_count); diff --git a/sdk/runanywhere-commons/include/rac/core/rac_error.h b/sdk/runanywhere-commons/include/rac/core/rac_error.h index e816984e9..c3385b726 100644 --- a/sdk/runanywhere-commons/include/rac/core/rac_error.h +++ b/sdk/runanywhere-commons/include/rac/core/rac_error.h @@ -367,6 +367,17 @@ extern "C" { #define RAC_ERROR_BACKEND_BUSY ((rac_result_t) - 603) /** Backend unavailable: backend compiled as stub, engine binary not installed */ #define RAC_ERROR_BACKEND_UNAVAILABLE ((rac_result_t) - 604) +/** Backend runtime ABI is incompatible with the version the SDK was compiled + * against. Typically means the prebuilt third_party binary (sherpa-onnx, + * ONNX Runtime, etc.) was produced from a different major/minor version + * than the headers; struct layouts may not match, so we refuse to proceed. + * Fix: align third_party binaries with the VERSIONS file entries, or + * upgrade the commons SDK. */ +#define RAC_ERROR_BACKEND_INCOMPATIBLE_VERSION ((rac_result_t) - 605) +/** JNI call from a Kotlin/Java callback left a pending Java exception on + * the JNIEnv - caught by JniScope, logged, and cleared. Returned from C + * to signal that the operation must be retried or abandoned. */ +#define RAC_ERROR_JNI_EXCEPTION ((rac_result_t) - 606) /** Invalid handle */ #define RAC_ERROR_INVALID_HANDLE ((rac_result_t) - 610) diff --git a/sdk/runanywhere-commons/include/rac/core/rac_events.h b/sdk/runanywhere-commons/include/rac/core/rac_events.h index 395cdcde9..ad941691c 100644 --- a/sdk/runanywhere-commons/include/rac/core/rac_events.h +++ b/sdk/runanywhere-commons/include/rac/core/rac_events.h @@ -250,7 +250,7 @@ typedef void (*rac_event_callback_fn)(rac_event_type_t type, const rac_event_dat * @param user_data User data passed to callback * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_events_set_callback(rac_event_callback_fn callback, void* user_data); +RAC_API RAC_NODISCARD rac_result_t rac_events_set_callback(rac_event_callback_fn callback, void* user_data); /** * @brief Emit an event diff --git a/sdk/runanywhere-commons/include/rac/core/rac_hardware.h b/sdk/runanywhere-commons/include/rac/core/rac_hardware.h new file mode 100644 index 000000000..2fb6231e2 --- /dev/null +++ b/sdk/runanywhere-commons/include/rac/core/rac_hardware.h @@ -0,0 +1,104 @@ +/** + * @file rac_hardware.h + * @brief RunAnywhere Commons - Hardware capability detection + * + * Public C API for querying which inference accelerators are available at + * runtime on the current device. Used by backend selection logic and by + * SDK consumers that want to surface device-aware UI (e.g. "GPU acceleration + * enabled"). + * + * Detection is compile-time-honest: features gated by a build flag are + * reported absent on binaries that don't include them, even if the hardware + * itself is present. For example, an arm64 iOS build without + * -DGGML_USE_METAL will report has_metal = RAC_FALSE. + * + * Thread safety: + * rac_hardware_query_capabilities() is safe to call from any thread. + * Results are cached inside rac_commons after the first call; repeated + * calls are cheap (one atomic load). + */ + +#ifndef RAC_HARDWARE_H +#define RAC_HARDWARE_H + +#include "rac/core/rac_error.h" +#include "rac/core/rac_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Snapshot of detected hardware + accelerator capabilities. + * + * Every field carries a rac_bool_t (not std::optional) so C consumers can + * use the struct directly. Numeric fields like total_gpu_memory_mb are 0 + * when unknown or inapplicable. + * + * ABI-stable: this struct has a leading `version` field and a trailing + * reserved block so new capabilities can be added without breaking + * existing C consumers. Current schema version is RAC_HARDWARE_REPORT_V1. + */ +typedef struct rac_hardware_report { + /** Must equal RAC_HARDWARE_REPORT_V1. Checked at every query. */ + uint32_t version; + + /* -- CPU SIMD ---------------------------------------------------- */ + rac_bool_t has_neon; /* ARM NEON (Android arm64, iOS arm64, Apple Silicon) */ + rac_bool_t has_sse42; /* Intel SSE4.2 */ + rac_bool_t has_avx; /* Intel AVX */ + rac_bool_t has_avx2; /* Intel AVX2 */ + rac_bool_t has_avx512; /* Intel AVX-512 family */ + int32_t num_logical_cpus; + int32_t num_physical_cpus; + + /* -- GPU --------------------------------------------------------- */ + rac_bool_t has_metal; /* Apple Metal available AND commons built with it */ + rac_bool_t has_cuda; /* NVIDIA CUDA */ + rac_bool_t has_vulkan; /* Vulkan (Linux/Android/Windows) */ + rac_bool_t has_opencl; /* OpenCL (Android Adreno etc.) */ + rac_bool_t has_webgpu; /* WebGPU (WASM/Emscripten builds) */ + int64_t total_gpu_memory_mb; /* 0 if unknown */ + + /* -- NPU / Neural accelerator ------------------------------------ */ + rac_bool_t has_ane; /* Apple Neural Engine (via Core ML) */ + rac_bool_t has_qnn; /* Qualcomm Hexagon via QNN SDK */ + rac_bool_t has_genio; /* MediaTek Genio NPU */ + + /* -- OS / platform info ------------------------------------------ */ + rac_bool_t is_apple_silicon; + rac_bool_t is_ios_simulator; + rac_bool_t is_android_emulator; + + /** Reserved for future fields. Must be initialized to zero. */ + void* _reserved[4]; +} rac_hardware_report_t; + +/** Current schema version. Bump when adding fields in a breaking way. */ +#define RAC_HARDWARE_REPORT_V1 ((uint32_t)1) + +/** + * @brief Populate `out_report` with current hardware capabilities. + * + * The caller sets `out_report->version = RAC_HARDWARE_REPORT_V1` before + * calling; a mismatch returns RAC_ERROR_BACKEND_INCOMPATIBLE_VERSION. + * + * @param out_report Caller-owned struct. Required. + * @return RAC_SUCCESS, or RAC_ERROR_INVALID_ARGUMENT / _INCOMPATIBLE_VERSION. + */ +RAC_API RAC_NODISCARD rac_result_t rac_hardware_query_capabilities( + rac_hardware_report_t* out_report); + +/** + * @brief Return a pretty multi-line string summarising the report. + * + * The string is newly allocated by rac_strdup; caller must free it with + * rac_free. Intended for logging / diagnostics, not machine parsing. + */ +RAC_API char* rac_hardware_format_report(const rac_hardware_report_t* report); + +#ifdef __cplusplus +} +#endif + +#endif // RAC_HARDWARE_H diff --git a/sdk/runanywhere-commons/include/rac/core/rac_logger.h b/sdk/runanywhere-commons/include/rac/core/rac_logger.h index 2ccd69083..1e57dbc75 100644 --- a/sdk/runanywhere-commons/include/rac/core/rac_logger.h +++ b/sdk/runanywhere-commons/include/rac/core/rac_logger.h @@ -95,7 +95,7 @@ typedef struct rac_log_metadata { * @param min_level Minimum log level to output * @return RAC_SUCCESS on success */ -RAC_API rac_result_t rac_logger_init(rac_log_level_t min_level); +RAC_API RAC_NODISCARD rac_result_t rac_logger_init(rac_log_level_t min_level); /** * @brief Shutdown the logging system. diff --git a/sdk/runanywhere-commons/include/rac/core/rac_platform_adapter.h b/sdk/runanywhere-commons/include/rac/core/rac_platform_adapter.h index a85296ff4..6d250e1ff 100644 --- a/sdk/runanywhere-commons/include/rac/core/rac_platform_adapter.h +++ b/sdk/runanywhere-commons/include/rac/core/rac_platform_adapter.h @@ -20,6 +20,30 @@ extern "C" { #endif +// ============================================================================= +// ADAPTER ABI VERSIONING +// ============================================================================= +// +// Callers MUST set `rac_platform_adapter_t::version = RAC_PLATFORM_ADAPTER_VERSION` +// before passing the struct to rac_init(). The runtime validates this and +// refuses adapters whose version is 0 (uninitialised) or greater than the +// latest version it knows about. +// +// When new callbacks are appended to the struct in a future release: +// 1. Bump this constant. +// 2. Document the new version (and the callback it added) in the +// version history block below. +// 3. Keep all existing fields in place and in the same order. +// 4. The runtime reads callbacks by offset; older callers that were +// compiled against version N still work on a runtime at version N+M +// so long as the runtime only touches offsets <= N. +// +// Version history: +// v1 - initial schema: file_*, http_*, extract_*, secure_*, time, +// random_bytes, etc. (~13 callbacks) +// ============================================================================= +#define RAC_PLATFORM_ADAPTER_VERSION ((uint32_t)1) + // ============================================================================= // CALLBACK TYPES (defined outside struct for C compatibility) // ============================================================================= @@ -59,9 +83,30 @@ typedef void (*rac_extract_progress_callback_fn)(int32_t files_extracted, int32_ * Platform adapter structure. * * Implements platform-specific operations via callbacks. - * The SDK layer (Swift/Kotlin) provides these implementations. + * The SDK layer (Swift/Kotlin/RN/Flutter/WASM) provides these + * implementations. + * + * ABI stability: + * The first field is a `version` set by the caller to + * RAC_PLATFORM_ADAPTER_VERSION. rac_init() validates this; an adapter + * whose version is 0 or greater than what the runtime knows about is + * rejected with a clear error instead of being silently misinterpreted. + * + * When new callbacks are appended here, bump RAC_PLATFORM_ADAPTER_VERSION + * and bump RAC_PLATFORM_ADAPTER_VERSION_LATEST. Older consumers continue + * to work by setting their `version` to the version they were compiled + * against; the runtime only reads callbacks through that version. + * + * The trailing `_reserved[4]` is zero-initialized padding so adding + * small fields in a future version doesn't change sizeof() - callers + * using `= {0}` or `= RAC_PLATFORM_ADAPTER_DEFAULTS` stay correct. */ typedef struct rac_platform_adapter { + // ------------------------------------------------------------------------- + // Version (set by caller; validated by rac_init) + // ------------------------------------------------------------------------- + uint32_t version; // Must equal RAC_PLATFORM_ADAPTER_VERSION. + // ------------------------------------------------------------------------- // File System Operations // ------------------------------------------------------------------------- @@ -257,7 +302,7 @@ typedef struct rac_platform_adapter { * @param adapter Platform adapter (must not be NULL) * @return RAC_SUCCESS on success, error code on failure */ -RAC_API rac_result_t rac_set_platform_adapter(const rac_platform_adapter_t* adapter); +RAC_API RAC_NODISCARD rac_result_t rac_set_platform_adapter(const rac_platform_adapter_t* adapter); /** * Gets the current platform adapter. @@ -296,7 +341,7 @@ RAC_API int64_t rac_get_current_time_ms(void); * @param out_task_id Output: Task ID (owned, must be freed) * @return RAC_SUCCESS if started, error code otherwise */ -RAC_API rac_result_t rac_http_download(const char* url, const char* destination_path, +RAC_API RAC_NODISCARD rac_result_t rac_http_download(const char* url, const char* destination_path, rac_http_progress_callback_fn progress_callback, rac_http_complete_callback_fn complete_callback, void* callback_user_data, char** out_task_id); @@ -308,7 +353,7 @@ RAC_API rac_result_t rac_http_download(const char* url, const char* destination_ * @param task_id Task ID to cancel * @return RAC_SUCCESS if cancelled, error code otherwise */ -RAC_API rac_result_t rac_http_download_cancel(const char* task_id); +RAC_API RAC_NODISCARD rac_result_t rac_http_download_cancel(const char* task_id); /** * Extract an archive using the platform adapter. @@ -320,7 +365,7 @@ RAC_API rac_result_t rac_http_download_cancel(const char* task_id); * @param callback_user_data User data for callback * @return RAC_SUCCESS if extracted, error code otherwise */ -RAC_API rac_result_t rac_extract_archive(const char* archive_path, const char* destination_dir, +RAC_API RAC_NODISCARD rac_result_t rac_extract_archive(const char* archive_path, const char* destination_dir, rac_extract_progress_callback_fn progress_callback, void* callback_user_data); diff --git a/sdk/runanywhere-commons/include/rac/core/rac_sdk_state.h b/sdk/runanywhere-commons/include/rac/core/rac_sdk_state.h index 97c82c3bc..8d00c3ce0 100644 --- a/sdk/runanywhere-commons/include/rac/core/rac_sdk_state.h +++ b/sdk/runanywhere-commons/include/rac/core/rac_sdk_state.h @@ -95,7 +95,7 @@ RAC_API rac_sdk_state_handle_t rac_state_get_instance(void); * @param device_id The persistent device ID (copied internally) * @return RAC_SUCCESS on success */ -RAC_API rac_result_t rac_state_initialize(rac_environment_t env, const char* api_key, +RAC_API RAC_NODISCARD rac_result_t rac_state_initialize(rac_environment_t env, const char* api_key, const char* base_url, const char* device_id); /** @@ -160,7 +160,7 @@ RAC_API const char* rac_state_get_device_id(void); * @param auth The auth data to set * @return RAC_SUCCESS on success */ -RAC_API rac_result_t rac_state_set_auth(const rac_auth_data_t* auth); +RAC_API RAC_NODISCARD rac_result_t rac_state_set_auth(const rac_auth_data_t* auth); /** * @brief Get current access token diff --git a/sdk/runanywhere-commons/include/rac/core/rac_structured_error.h b/sdk/runanywhere-commons/include/rac/core/rac_structured_error.h index 233413531..a52f5e278 100644 --- a/sdk/runanywhere-commons/include/rac/core/rac_structured_error.h +++ b/sdk/runanywhere-commons/include/rac/core/rac_structured_error.h @@ -427,7 +427,7 @@ RAC_API void rac_clear_last_error(void); * @param message Error message * @return The error code (for easy return statements) */ -RAC_API rac_result_t rac_set_error(rac_result_t code, rac_error_category_t category, +RAC_API RAC_NODISCARD rac_result_t rac_set_error(rac_result_t code, rac_error_category_t category, const char* message); /** @@ -457,7 +457,7 @@ RAC_API rac_result_t rac_set_error(rac_result_t code, rac_error_category_t categ * @param function Function name (__func__) * @return The error code (for easy return statements) */ -RAC_API rac_result_t rac_error_log_and_track(rac_result_t code, rac_error_category_t category, +RAC_API RAC_NODISCARD rac_result_t rac_error_log_and_track(rac_result_t code, rac_error_category_t category, const char* message, const char* file, int32_t line, const char* function); @@ -476,7 +476,7 @@ RAC_API rac_result_t rac_error_log_and_track(rac_result_t code, rac_error_catego * @param function Function name * @return The error code */ -RAC_API rac_result_t rac_error_log_and_track_model(rac_result_t code, rac_error_category_t category, +RAC_API RAC_NODISCARD rac_result_t rac_error_log_and_track_model(rac_result_t code, rac_error_category_t category, const char* message, const char* model_id, const char* framework, const char* file, int32_t line, const char* function); diff --git a/sdk/runanywhere-commons/include/rac/core/rac_types.h b/sdk/runanywhere-commons/include/rac/core/rac_types.h index 793371d5d..50edbb1e1 100644 --- a/sdk/runanywhere-commons/include/rac/core/rac_types.h +++ b/sdk/runanywhere-commons/include/rac/core/rac_types.h @@ -13,6 +13,11 @@ #include #include +/* Pull in portable attribute macros (RAC_NODISCARD, RAC_NONNULL, RAC_DEPRECATED, + * RAC_NORETURN, RAC_ATTR_PRINTF, RAC_PURE) - every RAC public header gets + * these via its rac_types.h include. */ +#include "rac_attrs.h" + /** * Null pointer macro for use in static initializers. * Uses nullptr in C++ (preferred by clang-tidy modernize-use-nullptr) diff --git a/sdk/runanywhere-commons/include/rac/features/diffusion/rac_diffusion_component.h b/sdk/runanywhere-commons/include/rac/features/diffusion/rac_diffusion_component.h index 55e004f11..72254637b 100644 --- a/sdk/runanywhere-commons/include/rac/features/diffusion/rac_diffusion_component.h +++ b/sdk/runanywhere-commons/include/rac/features/diffusion/rac_diffusion_component.h @@ -33,7 +33,7 @@ extern "C" { * @param out_handle Output: Handle to the component * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_diffusion_component_create(rac_handle_t* out_handle); +RAC_API RAC_NODISCARD rac_result_t rac_diffusion_component_create(rac_handle_t* out_handle); /** * @brief Configure the diffusion component @@ -42,7 +42,7 @@ RAC_API rac_result_t rac_diffusion_component_create(rac_handle_t* out_handle); * @param config Configuration * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_diffusion_component_configure(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_diffusion_component_configure(rac_handle_t handle, const rac_diffusion_config_t* config); /** @@ -70,7 +70,7 @@ RAC_API const char* rac_diffusion_component_get_model_id(rac_handle_t handle); * @param model_name Human-readable model name * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_diffusion_component_load_model(rac_handle_t handle, const char* model_path, +RAC_API RAC_NODISCARD rac_result_t rac_diffusion_component_load_model(rac_handle_t handle, const char* model_path, const char* model_id, const char* model_name); @@ -80,7 +80,7 @@ RAC_API rac_result_t rac_diffusion_component_load_model(rac_handle_t handle, con * @param handle Component handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_diffusion_component_unload(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_diffusion_component_unload(rac_handle_t handle); /** * @brief Cleanup and reset the component @@ -88,7 +88,7 @@ RAC_API rac_result_t rac_diffusion_component_unload(rac_handle_t handle); * @param handle Component handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_diffusion_component_cleanup(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_diffusion_component_cleanup(rac_handle_t handle); /** * @brief Cancel ongoing generation @@ -98,7 +98,7 @@ RAC_API rac_result_t rac_diffusion_component_cleanup(rac_handle_t handle); * @param handle Component handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_diffusion_component_cancel(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_diffusion_component_cancel(rac_handle_t handle); /** * @brief Generate an image (non-streaming) @@ -110,7 +110,7 @@ RAC_API rac_result_t rac_diffusion_component_cancel(rac_handle_t handle); * @param out_result Output: Generation result * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_diffusion_component_generate(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_diffusion_component_generate(rac_handle_t handle, const rac_diffusion_options_t* options, rac_diffusion_result_t* out_result); @@ -127,7 +127,7 @@ RAC_API rac_result_t rac_diffusion_component_generate(rac_handle_t handle, * @param user_data User context passed to callbacks * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_diffusion_component_generate_with_callbacks( +RAC_API RAC_NODISCARD rac_result_t rac_diffusion_component_generate_with_callbacks( rac_handle_t handle, const rac_diffusion_options_t* options, rac_diffusion_progress_callback_fn progress_callback, rac_diffusion_complete_callback_fn complete_callback, @@ -154,7 +154,7 @@ RAC_API rac_result_t rac_diffusion_component_generate_with_callbacks( * @param config_json JSON string * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_diffusion_component_configure_json(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_diffusion_component_configure_json(rac_handle_t handle, const char* config_json); /** @@ -185,7 +185,7 @@ RAC_API rac_result_t rac_diffusion_component_configure_json(rac_handle_t handle, * @param out_json Output JSON (caller must free with rac_free) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_diffusion_component_generate_json( +RAC_API RAC_NODISCARD rac_result_t rac_diffusion_component_generate_json( rac_handle_t handle, const char* options_json, const uint8_t* input_image_data, size_t input_image_size, const uint8_t* mask_data, size_t mask_size, char** out_json); @@ -209,7 +209,7 @@ RAC_API rac_result_t rac_diffusion_component_generate_json( * @param out_json Output JSON (caller must free with rac_free) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_diffusion_component_get_info_json(rac_handle_t handle, char** out_json); +RAC_API RAC_NODISCARD rac_result_t rac_diffusion_component_get_info_json(rac_handle_t handle, char** out_json); /** * @brief Get supported capabilities @@ -228,7 +228,7 @@ RAC_API uint32_t rac_diffusion_component_get_capabilities(rac_handle_t handle); * @param out_info Output: Service information * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_diffusion_component_get_info(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_diffusion_component_get_info(rac_handle_t handle, rac_diffusion_info_t* out_info); /** @@ -246,7 +246,7 @@ RAC_API rac_lifecycle_state_t rac_diffusion_component_get_state(rac_handle_t han * @param out_metrics Output: Lifecycle metrics * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_diffusion_component_get_metrics(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_diffusion_component_get_metrics(rac_handle_t handle, rac_lifecycle_metrics_t* out_metrics); /** diff --git a/sdk/runanywhere-commons/include/rac/features/diffusion/rac_diffusion_model_registry.h b/sdk/runanywhere-commons/include/rac/features/diffusion/rac_diffusion_model_registry.h index 6a200a5cb..343fc0fd5 100644 --- a/sdk/runanywhere-commons/include/rac/features/diffusion/rac_diffusion_model_registry.h +++ b/sdk/runanywhere-commons/include/rac/features/diffusion/rac_diffusion_model_registry.h @@ -267,7 +267,7 @@ RAC_API void rac_diffusion_model_registry_cleanup(void); * @param strategy Strategy to register (caller retains ownership) * @return RAC_SUCCESS on success, RAC_ERROR_ALREADY_EXISTS if name taken */ -RAC_API rac_result_t rac_diffusion_model_registry_register( +RAC_API RAC_NODISCARD rac_result_t rac_diffusion_model_registry_register( const rac_diffusion_model_strategy_t* strategy); /** @@ -276,7 +276,7 @@ RAC_API rac_result_t rac_diffusion_model_registry_register( * @param name Strategy name to unregister * @return RAC_SUCCESS on success, RAC_ERROR_NOT_FOUND if not registered */ -RAC_API rac_result_t rac_diffusion_model_registry_unregister(const char* name); +RAC_API RAC_NODISCARD rac_result_t rac_diffusion_model_registry_unregister(const char* name); /** * @brief Get model definition by ID @@ -285,7 +285,7 @@ RAC_API rac_result_t rac_diffusion_model_registry_unregister(const char* name); * @param out_def Output model definition (filled on success) * @return RAC_SUCCESS if found, RAC_ERROR_NOT_FOUND otherwise */ -RAC_API rac_result_t rac_diffusion_model_registry_get( +RAC_API RAC_NODISCARD rac_result_t rac_diffusion_model_registry_get( const char* model_id, rac_diffusion_model_def_t* out_def); @@ -296,7 +296,7 @@ RAC_API rac_result_t rac_diffusion_model_registry_get( * @param out_count Number of models * @return RAC_SUCCESS on success */ -RAC_API rac_result_t rac_diffusion_model_registry_list( +RAC_API RAC_NODISCARD rac_result_t rac_diffusion_model_registry_list( rac_diffusion_model_def_t** out_models, size_t* out_count); @@ -331,7 +331,7 @@ RAC_API rac_bool_t rac_diffusion_model_registry_is_available(const char* model_i * @param out_def Output model definition (filled on success) * @return RAC_SUCCESS if found, RAC_ERROR_NOT_FOUND if no recommendation */ -RAC_API rac_result_t rac_diffusion_model_registry_get_recommended( +RAC_API RAC_NODISCARD rac_result_t rac_diffusion_model_registry_get_recommended( rac_diffusion_model_def_t* out_def); /** diff --git a/sdk/runanywhere-commons/include/rac/features/diffusion/rac_diffusion_service.h b/sdk/runanywhere-commons/include/rac/features/diffusion/rac_diffusion_service.h index 24ffc1f41..8e3e91d3c 100644 --- a/sdk/runanywhere-commons/include/rac/features/diffusion/rac_diffusion_service.h +++ b/sdk/runanywhere-commons/include/rac/features/diffusion/rac_diffusion_service.h @@ -83,7 +83,7 @@ typedef struct rac_diffusion_service { * @param out_handle Output: Handle to the created service * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_diffusion_create(const char* model_id, rac_handle_t* out_handle); +RAC_API RAC_NODISCARD rac_result_t rac_diffusion_create(const char* model_id, rac_handle_t* out_handle); /** * @brief Create a diffusion service with configuration @@ -96,7 +96,7 @@ RAC_API rac_result_t rac_diffusion_create(const char* model_id, rac_handle_t* ou * @param out_handle Output: Handle to the created service * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_diffusion_create_with_config(const char* model_id, +RAC_API RAC_NODISCARD rac_result_t rac_diffusion_create_with_config(const char* model_id, const rac_diffusion_config_t* config, rac_handle_t* out_handle); @@ -108,7 +108,7 @@ RAC_API rac_result_t rac_diffusion_create_with_config(const char* model_id, * @param config Configuration (can be NULL for defaults) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_diffusion_initialize(rac_handle_t handle, const char* model_path, +RAC_API RAC_NODISCARD rac_result_t rac_diffusion_initialize(rac_handle_t handle, const char* model_path, const rac_diffusion_config_t* config); /** @@ -121,7 +121,7 @@ RAC_API rac_result_t rac_diffusion_initialize(rac_handle_t handle, const char* m * @param out_result Output: Generation result (caller must free with rac_diffusion_result_free) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_diffusion_generate(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_diffusion_generate(rac_handle_t handle, const rac_diffusion_options_t* options, rac_diffusion_result_t* out_result); @@ -135,7 +135,7 @@ RAC_API rac_result_t rac_diffusion_generate(rac_handle_t handle, * @param out_result Output: Generation result (caller must free with rac_diffusion_result_free) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_diffusion_generate_with_progress( +RAC_API RAC_NODISCARD rac_result_t rac_diffusion_generate_with_progress( rac_handle_t handle, const rac_diffusion_options_t* options, rac_diffusion_progress_callback_fn progress_callback, void* user_data, rac_diffusion_result_t* out_result); @@ -147,7 +147,7 @@ RAC_API rac_result_t rac_diffusion_generate_with_progress( * @param out_info Output: Service information * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_diffusion_get_info(rac_handle_t handle, rac_diffusion_info_t* out_info); +RAC_API RAC_NODISCARD rac_result_t rac_diffusion_get_info(rac_handle_t handle, rac_diffusion_info_t* out_info); /** * @brief Get supported capabilities as bitmask @@ -163,7 +163,7 @@ RAC_API uint32_t rac_diffusion_get_capabilities(rac_handle_t handle); * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_diffusion_cancel(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_diffusion_cancel(rac_handle_t handle); /** * @brief Cleanup and release model resources @@ -171,7 +171,7 @@ RAC_API rac_result_t rac_diffusion_cancel(rac_handle_t handle); * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_diffusion_cleanup(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_diffusion_cleanup(rac_handle_t handle); /** * @brief Destroy a diffusion service instance diff --git a/sdk/runanywhere-commons/include/rac/features/diffusion/rac_diffusion_tokenizer.h b/sdk/runanywhere-commons/include/rac/features/diffusion/rac_diffusion_tokenizer.h index 6b405258b..3a94becc0 100644 --- a/sdk/runanywhere-commons/include/rac/features/diffusion/rac_diffusion_tokenizer.h +++ b/sdk/runanywhere-commons/include/rac/features/diffusion/rac_diffusion_tokenizer.h @@ -82,7 +82,7 @@ RAC_API const char* rac_diffusion_tokenizer_get_base_url(rac_diffusion_tokenizer * // url = "https://huggingface.co/runwayml/stable-diffusion-v1-5/resolve/main/tokenizer/vocab.json" * @endcode */ -RAC_API rac_result_t rac_diffusion_tokenizer_get_file_url(rac_diffusion_tokenizer_source_t source, +RAC_API RAC_NODISCARD rac_result_t rac_diffusion_tokenizer_get_file_url(rac_diffusion_tokenizer_source_t source, const char* custom_url, const char* filename, char* out_url, size_t out_url_size); @@ -99,7 +99,7 @@ RAC_API rac_result_t rac_diffusion_tokenizer_get_file_url(rac_diffusion_tokenize * @param out_has_merges Output: true if merges.txt exists * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_diffusion_tokenizer_check_files(const char* model_dir, +RAC_API RAC_NODISCARD rac_result_t rac_diffusion_tokenizer_check_files(const char* model_dir, rac_bool_t* out_has_vocab, rac_bool_t* out_has_merges); @@ -140,7 +140,7 @@ rac_diffusion_tokenizer_ensure_files(const char* model_dir, * @param output_path Full path where the file should be saved * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_diffusion_tokenizer_download_file(rac_diffusion_tokenizer_source_t source, +RAC_API RAC_NODISCARD rac_result_t rac_diffusion_tokenizer_download_file(rac_diffusion_tokenizer_source_t source, const char* custom_url, const char* filename, const char* output_path); diff --git a/sdk/runanywhere-commons/include/rac/features/embeddings/rac_embeddings_component.h b/sdk/runanywhere-commons/include/rac/features/embeddings/rac_embeddings_component.h index 86f5bf818..29ad64cb8 100644 --- a/sdk/runanywhere-commons/include/rac/features/embeddings/rac_embeddings_component.h +++ b/sdk/runanywhere-commons/include/rac/features/embeddings/rac_embeddings_component.h @@ -25,12 +25,12 @@ extern "C" { /** * @brief Create an embeddings component */ -RAC_API rac_result_t rac_embeddings_component_create(rac_handle_t* out_handle); +RAC_API RAC_NODISCARD rac_result_t rac_embeddings_component_create(rac_handle_t* out_handle); /** * @brief Configure the embeddings component */ -RAC_API rac_result_t rac_embeddings_component_configure(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_embeddings_component_configure(rac_handle_t handle, const rac_embeddings_config_t* config); /** @@ -46,7 +46,7 @@ RAC_API const char* rac_embeddings_component_get_model_id(rac_handle_t handle); /** * @brief Load an embedding model */ -RAC_API rac_result_t rac_embeddings_component_load_model(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_embeddings_component_load_model(rac_handle_t handle, const char* model_path, const char* model_id, const char* model_name); @@ -54,17 +54,17 @@ RAC_API rac_result_t rac_embeddings_component_load_model(rac_handle_t handle, /** * @brief Unload the current model */ -RAC_API rac_result_t rac_embeddings_component_unload(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_embeddings_component_unload(rac_handle_t handle); /** * @brief Cleanup and reset the component */ -RAC_API rac_result_t rac_embeddings_component_cleanup(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_embeddings_component_cleanup(rac_handle_t handle); /** * @brief Generate embedding for a single text */ -RAC_API rac_result_t rac_embeddings_component_embed(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_embeddings_component_embed(rac_handle_t handle, const char* text, const rac_embeddings_options_t* options, rac_embeddings_result_t* out_result); @@ -72,7 +72,7 @@ RAC_API rac_result_t rac_embeddings_component_embed(rac_handle_t handle, /** * @brief Generate embeddings for a batch of texts */ -RAC_API rac_result_t rac_embeddings_component_embed_batch(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_embeddings_component_embed_batch(rac_handle_t handle, const char* const* texts, size_t num_texts, const rac_embeddings_options_t* options, @@ -86,7 +86,7 @@ RAC_API rac_lifecycle_state_t rac_embeddings_component_get_state(rac_handle_t ha /** * @brief Get lifecycle metrics */ -RAC_API rac_result_t rac_embeddings_component_get_metrics(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_embeddings_component_get_metrics(rac_handle_t handle, rac_lifecycle_metrics_t* out_metrics); /** diff --git a/sdk/runanywhere-commons/include/rac/features/embeddings/rac_embeddings_service.h b/sdk/runanywhere-commons/include/rac/features/embeddings/rac_embeddings_service.h index 6dccc7dc3..585af7228 100644 --- a/sdk/runanywhere-commons/include/rac/features/embeddings/rac_embeddings_service.h +++ b/sdk/runanywhere-commons/include/rac/features/embeddings/rac_embeddings_service.h @@ -72,7 +72,7 @@ typedef struct rac_embeddings_service { * @param out_handle Output: Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_embeddings_create(const char* model_id, rac_handle_t* out_handle); +RAC_API RAC_NODISCARD rac_result_t rac_embeddings_create(const char* model_id, rac_handle_t* out_handle); /** * @brief Create an embeddings service with additional configuration JSON. @@ -85,7 +85,7 @@ RAC_API rac_result_t rac_embeddings_create(const char* model_id, rac_handle_t* o * @param out_handle Output: Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_embeddings_create_with_config(const char* model_id, +RAC_API RAC_NODISCARD rac_result_t rac_embeddings_create_with_config(const char* model_id, const char* config_json, rac_handle_t* out_handle); @@ -96,7 +96,7 @@ RAC_API rac_result_t rac_embeddings_create_with_config(const char* model_id, * @param model_path Path to the embedding model * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_embeddings_initialize(rac_handle_t handle, const char* model_path); +RAC_API RAC_NODISCARD rac_result_t rac_embeddings_initialize(rac_handle_t handle, const char* model_path); /** * @brief Generate embedding for a single text @@ -107,7 +107,7 @@ RAC_API rac_result_t rac_embeddings_initialize(rac_handle_t handle, const char* * @param out_result Output: Embedding result * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_embeddings_embed(rac_handle_t handle, const char* text, +RAC_API RAC_NODISCARD rac_result_t rac_embeddings_embed(rac_handle_t handle, const char* text, const rac_embeddings_options_t* options, rac_embeddings_result_t* out_result); @@ -121,7 +121,7 @@ RAC_API rac_result_t rac_embeddings_embed(rac_handle_t handle, const char* text, * @param out_result Output: Embedding results * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_embeddings_embed_batch(rac_handle_t handle, const char* const* texts, +RAC_API RAC_NODISCARD rac_result_t rac_embeddings_embed_batch(rac_handle_t handle, const char* const* texts, size_t num_texts, const rac_embeddings_options_t* options, rac_embeddings_result_t* out_result); @@ -133,7 +133,7 @@ RAC_API rac_result_t rac_embeddings_embed_batch(rac_handle_t handle, const char* * @param out_info Output: Service info * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_embeddings_get_info(rac_handle_t handle, rac_embeddings_info_t* out_info); +RAC_API RAC_NODISCARD rac_result_t rac_embeddings_get_info(rac_handle_t handle, rac_embeddings_info_t* out_info); /** * @brief Cleanup service resources @@ -141,7 +141,7 @@ RAC_API rac_result_t rac_embeddings_get_info(rac_handle_t handle, rac_embeddings * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_embeddings_cleanup(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_embeddings_cleanup(rac_handle_t handle); /** * @brief Destroy the embeddings service diff --git a/sdk/runanywhere-commons/include/rac/features/llm/rac_llm_analytics.h b/sdk/runanywhere-commons/include/rac/features/llm/rac_llm_analytics.h index 392587a34..f84e3e3db 100644 --- a/sdk/runanywhere-commons/include/rac/features/llm/rac_llm_analytics.h +++ b/sdk/runanywhere-commons/include/rac/features/llm/rac_llm_analytics.h @@ -44,7 +44,7 @@ typedef struct rac_llm_analytics_s* rac_llm_analytics_handle_t; * @param out_handle Output: Handle to the created service * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_analytics_create(rac_llm_analytics_handle_t* out_handle); +RAC_API RAC_NODISCARD rac_result_t rac_llm_analytics_create(rac_llm_analytics_handle_t* out_handle); /** * @brief Destroy an LLM analytics service instance @@ -71,7 +71,7 @@ RAC_API void rac_llm_analytics_destroy(rac_llm_analytics_handle_t handle); * @param out_generation_id Output: Generated unique ID (owned, must be freed with rac_free) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_analytics_start_generation( +RAC_API RAC_NODISCARD rac_result_t rac_llm_analytics_start_generation( rac_llm_analytics_handle_t handle, const char* model_id, rac_inference_framework_t framework, const float* temperature, const int32_t* max_tokens, const int32_t* context_length, char** out_generation_id); @@ -90,7 +90,7 @@ RAC_API rac_result_t rac_llm_analytics_start_generation( * @param out_generation_id Output: Generated unique ID (owned, must be freed with rac_free) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_analytics_start_streaming_generation( +RAC_API RAC_NODISCARD rac_result_t rac_llm_analytics_start_streaming_generation( rac_llm_analytics_handle_t handle, const char* model_id, rac_inference_framework_t framework, const float* temperature, const int32_t* max_tokens, const int32_t* context_length, char** out_generation_id); @@ -104,7 +104,7 @@ RAC_API rac_result_t rac_llm_analytics_start_streaming_generation( * @param generation_id The generation ID from start_streaming_generation * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_analytics_track_first_token(rac_llm_analytics_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_llm_analytics_track_first_token(rac_llm_analytics_handle_t handle, const char* generation_id); /** @@ -117,7 +117,7 @@ RAC_API rac_result_t rac_llm_analytics_track_first_token(rac_llm_analytics_handl * @param tokens_generated Number of tokens generated so far * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_analytics_track_streaming_update(rac_llm_analytics_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_llm_analytics_track_streaming_update(rac_llm_analytics_handle_t handle, const char* generation_id, int32_t tokens_generated); @@ -131,7 +131,7 @@ RAC_API rac_result_t rac_llm_analytics_track_streaming_update(rac_llm_analytics_ * @param model_id The model ID used * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_analytics_complete_generation(rac_llm_analytics_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_llm_analytics_complete_generation(rac_llm_analytics_handle_t handle, const char* generation_id, int32_t input_tokens, int32_t output_tokens, @@ -146,7 +146,7 @@ RAC_API rac_result_t rac_llm_analytics_complete_generation(rac_llm_analytics_han * @param error_message Error message * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_analytics_track_generation_failed(rac_llm_analytics_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_llm_analytics_track_generation_failed(rac_llm_analytics_handle_t handle, const char* generation_id, rac_result_t error_code, const char* error_message); @@ -162,7 +162,7 @@ RAC_API rac_result_t rac_llm_analytics_track_generation_failed(rac_llm_analytics * @param generation_id Generation ID (can be NULL) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_analytics_track_error(rac_llm_analytics_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_llm_analytics_track_error(rac_llm_analytics_handle_t handle, rac_result_t error_code, const char* error_message, const char* operation, const char* model_id, const char* generation_id); @@ -178,7 +178,7 @@ RAC_API rac_result_t rac_llm_analytics_track_error(rac_llm_analytics_handle_t ha * @param out_metrics Output: Metrics structure * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_analytics_get_metrics(rac_llm_analytics_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_llm_analytics_get_metrics(rac_llm_analytics_handle_t handle, rac_generation_metrics_t* out_metrics); #ifdef __cplusplus diff --git a/sdk/runanywhere-commons/include/rac/features/llm/rac_llm_component.h b/sdk/runanywhere-commons/include/rac/features/llm/rac_llm_component.h index 963a3d205..43628a95e 100644 --- a/sdk/runanywhere-commons/include/rac/features/llm/rac_llm_component.h +++ b/sdk/runanywhere-commons/include/rac/features/llm/rac_llm_component.h @@ -71,7 +71,7 @@ typedef void (*rac_llm_component_error_callback_fn)(rac_result_t error_code, * @param out_handle Output: Handle to the component * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_component_create(rac_handle_t* out_handle); +RAC_API RAC_NODISCARD rac_result_t rac_llm_component_create(rac_handle_t* out_handle); /** * @brief Configure the LLM component @@ -82,7 +82,7 @@ RAC_API rac_result_t rac_llm_component_create(rac_handle_t* out_handle); * @param config Configuration * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_component_configure(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_llm_component_configure(rac_handle_t handle, const rac_llm_config_t* config); /** @@ -118,7 +118,7 @@ RAC_API const char* rac_llm_component_get_model_id(rac_handle_t handle); * Optional: if NULL, defaults to model_id * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_component_load_model(rac_handle_t handle, const char* model_path, +RAC_API RAC_NODISCARD rac_result_t rac_llm_component_load_model(rac_handle_t handle, const char* model_path, const char* model_id, const char* model_name); /** @@ -129,7 +129,7 @@ RAC_API rac_result_t rac_llm_component_load_model(rac_handle_t handle, const cha * @param handle Component handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_component_unload(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_llm_component_unload(rac_handle_t handle); /** * @brief Cleanup and reset the component @@ -139,7 +139,7 @@ RAC_API rac_result_t rac_llm_component_unload(rac_handle_t handle); * @param handle Component handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_component_cleanup(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_llm_component_cleanup(rac_handle_t handle); /** * @brief Cancel ongoing generation @@ -150,7 +150,7 @@ RAC_API rac_result_t rac_llm_component_cleanup(rac_handle_t handle); * @param handle Component handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_component_cancel(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_llm_component_cancel(rac_handle_t handle); /** * @brief Generate text (non-streaming) @@ -163,7 +163,7 @@ RAC_API rac_result_t rac_llm_component_cancel(rac_handle_t handle); * @param out_result Output: Generation result * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_component_generate(rac_handle_t handle, const char* prompt, +RAC_API RAC_NODISCARD rac_result_t rac_llm_component_generate(rac_handle_t handle, const char* prompt, const rac_llm_options_t* options, rac_llm_result_t* out_result); @@ -191,7 +191,7 @@ RAC_API rac_bool_t rac_llm_component_supports_streaming(rac_handle_t handle); * @param user_data User context passed to callbacks * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_component_generate_stream( +RAC_API RAC_NODISCARD rac_result_t rac_llm_component_generate_stream( rac_handle_t handle, const char* prompt, const rac_llm_options_t* options, rac_llm_component_token_callback_fn token_callback, rac_llm_component_complete_callback_fn complete_callback, @@ -226,7 +226,7 @@ RAC_API rac_result_t rac_llm_component_generate_stream( * Pass NULL to skip timing (zero overhead). * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_component_generate_stream_with_timing( +RAC_API RAC_NODISCARD rac_result_t rac_llm_component_generate_stream_with_timing( rac_handle_t handle, const char* prompt, const rac_llm_options_t* options, rac_llm_component_token_callback_fn token_callback, rac_llm_component_complete_callback_fn complete_callback, @@ -248,7 +248,7 @@ RAC_API rac_lifecycle_state_t rac_llm_component_get_state(rac_handle_t handle); * @param out_metrics Output: Lifecycle metrics * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_component_get_metrics(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_llm_component_get_metrics(rac_handle_t handle, rac_lifecycle_metrics_t* out_metrics); // ============================================================================= @@ -266,7 +266,7 @@ RAC_API rac_result_t rac_llm_component_get_metrics(rac_handle_t handle, * @param scale Adapter scale factor (0.0-1.0, default 1.0) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_component_load_lora(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_llm_component_load_lora(rac_handle_t handle, const char* adapter_path, float scale); @@ -277,7 +277,7 @@ RAC_API rac_result_t rac_llm_component_load_lora(rac_handle_t handle, * @param adapter_path Path used when loading the adapter * @return RAC_SUCCESS or RAC_ERROR_NOT_FOUND */ -RAC_API rac_result_t rac_llm_component_remove_lora(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_llm_component_remove_lora(rac_handle_t handle, const char* adapter_path); /** @@ -286,7 +286,7 @@ RAC_API rac_result_t rac_llm_component_remove_lora(rac_handle_t handle, * @param handle Component handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_component_clear_lora(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_llm_component_clear_lora(rac_handle_t handle); /** * @brief Get loaded LoRA adapters info as JSON @@ -297,7 +297,7 @@ RAC_API rac_result_t rac_llm_component_clear_lora(rac_handle_t handle); * @param out_json Output: JSON string (caller must free with rac_free) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_component_get_lora_info(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_llm_component_get_lora_info(rac_handle_t handle, char** out_json); /** @@ -311,7 +311,7 @@ RAC_API rac_result_t rac_llm_component_get_lora_info(rac_handle_t handle, * @param out_error Output: error message if incompatible (caller must free with rac_free), NULL if compatible * @return RAC_SUCCESS if the backend supports LoRA, error code otherwise */ -RAC_API rac_result_t rac_llm_component_check_lora_compat(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_llm_component_check_lora_compat(rac_handle_t handle, const char* adapter_path, char** out_error); diff --git a/sdk/runanywhere-commons/include/rac/features/llm/rac_llm_events.h b/sdk/runanywhere-commons/include/rac/features/llm/rac_llm_events.h index 74cb5b197..0782312ac 100644 --- a/sdk/runanywhere-commons/include/rac/features/llm/rac_llm_events.h +++ b/sdk/runanywhere-commons/include/rac/features/llm/rac_llm_events.h @@ -94,7 +94,7 @@ typedef struct rac_llm_generation_event { * @param framework Inference framework * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_event_model_load_started(const char* model_id, +RAC_API RAC_NODISCARD rac_result_t rac_llm_event_model_load_started(const char* model_id, int64_t model_size_bytes, rac_inference_framework_t framework); @@ -107,7 +107,7 @@ RAC_API rac_result_t rac_llm_event_model_load_started(const char* model_id, * @param framework Inference framework * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_event_model_load_completed(const char* model_id, double duration_ms, +RAC_API RAC_NODISCARD rac_result_t rac_llm_event_model_load_completed(const char* model_id, double duration_ms, int64_t model_size_bytes, rac_inference_framework_t framework); @@ -120,7 +120,7 @@ RAC_API rac_result_t rac_llm_event_model_load_completed(const char* model_id, do * @param framework Inference framework * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_event_model_load_failed(const char* model_id, rac_result_t error_code, +RAC_API RAC_NODISCARD rac_result_t rac_llm_event_model_load_failed(const char* model_id, rac_result_t error_code, const char* error_message, rac_inference_framework_t framework); @@ -130,7 +130,7 @@ RAC_API rac_result_t rac_llm_event_model_load_failed(const char* model_id, rac_r * @param model_id Model identifier * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_event_model_unloaded(const char* model_id); +RAC_API RAC_NODISCARD rac_result_t rac_llm_event_model_unloaded(const char* model_id); /** * @brief Publish a generation started event @@ -141,7 +141,7 @@ RAC_API rac_result_t rac_llm_event_model_unloaded(const char* model_id); * @param framework Inference framework * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_event_generation_started(const char* generation_id, +RAC_API RAC_NODISCARD rac_result_t rac_llm_event_generation_started(const char* generation_id, const char* model_id, rac_bool_t is_streaming, rac_inference_framework_t framework); @@ -154,7 +154,7 @@ RAC_API rac_result_t rac_llm_event_generation_started(const char* generation_id, * @param framework Inference framework * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_event_first_token(const char* generation_id, const char* model_id, +RAC_API RAC_NODISCARD rac_result_t rac_llm_event_first_token(const char* generation_id, const char* model_id, double time_to_first_token_ms, rac_inference_framework_t framework); @@ -165,7 +165,7 @@ RAC_API rac_result_t rac_llm_event_first_token(const char* generation_id, const * @param tokens_generated Number of tokens generated so far * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_event_streaming_update(const char* generation_id, +RAC_API RAC_NODISCARD rac_result_t rac_llm_event_streaming_update(const char* generation_id, int32_t tokens_generated); /** @@ -174,7 +174,7 @@ RAC_API rac_result_t rac_llm_event_streaming_update(const char* generation_id, * @param event Generation event data * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_event_generation_completed(const rac_llm_generation_event_t* event); +RAC_API RAC_NODISCARD rac_result_t rac_llm_event_generation_completed(const rac_llm_generation_event_t* event); /** * @brief Publish a generation failed event @@ -184,7 +184,7 @@ RAC_API rac_result_t rac_llm_event_generation_completed(const rac_llm_generation * @param error_message Error message * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_event_generation_failed(const char* generation_id, +RAC_API RAC_NODISCARD rac_result_t rac_llm_event_generation_failed(const char* generation_id, rac_result_t error_code, const char* error_message); diff --git a/sdk/runanywhere-commons/include/rac/features/llm/rac_llm_metrics.h b/sdk/runanywhere-commons/include/rac/features/llm/rac_llm_metrics.h index 11abcd850..4210a2a77 100644 --- a/sdk/runanywhere-commons/include/rac/features/llm/rac_llm_metrics.h +++ b/sdk/runanywhere-commons/include/rac/features/llm/rac_llm_metrics.h @@ -148,7 +148,7 @@ typedef struct rac_generation_analytics* rac_generation_analytics_handle_t; * @param out_handle Output: Handle to the created collector * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_streaming_metrics_create(const char* model_id, const char* generation_id, +RAC_API RAC_NODISCARD rac_result_t rac_streaming_metrics_create(const char* model_id, const char* generation_id, int32_t prompt_length, rac_streaming_metrics_handle_t* out_handle); @@ -167,7 +167,7 @@ RAC_API void rac_streaming_metrics_destroy(rac_streaming_metrics_handle_t handle * @param handle Collector handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_streaming_metrics_mark_start(rac_streaming_metrics_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_streaming_metrics_mark_start(rac_streaming_metrics_handle_t handle); /** * @brief Record a token received during streaming. @@ -179,7 +179,7 @@ RAC_API rac_result_t rac_streaming_metrics_mark_start(rac_streaming_metrics_hand * @param token Token string received * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_streaming_metrics_record_token(rac_streaming_metrics_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_streaming_metrics_record_token(rac_streaming_metrics_handle_t handle, const char* token); /** @@ -190,7 +190,7 @@ RAC_API rac_result_t rac_streaming_metrics_record_token(rac_streaming_metrics_ha * @param handle Collector handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_streaming_metrics_mark_complete(rac_streaming_metrics_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_streaming_metrics_mark_complete(rac_streaming_metrics_handle_t handle); /** * @brief Mark generation as failed. @@ -201,7 +201,7 @@ RAC_API rac_result_t rac_streaming_metrics_mark_complete(rac_streaming_metrics_h * @param error_code Error code * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_streaming_metrics_mark_failed(rac_streaming_metrics_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_streaming_metrics_mark_failed(rac_streaming_metrics_handle_t handle, rac_result_t error_code); /** @@ -214,7 +214,7 @@ RAC_API rac_result_t rac_streaming_metrics_mark_failed(rac_streaming_metrics_han * @param out_result Output: Streaming result (must be freed with rac_streaming_result_free) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_streaming_metrics_get_result(rac_streaming_metrics_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_streaming_metrics_get_result(rac_streaming_metrics_handle_t handle, rac_streaming_result_t* out_result); /** @@ -224,7 +224,7 @@ RAC_API rac_result_t rac_streaming_metrics_get_result(rac_streaming_metrics_hand * @param out_ttft_ms Output: TTFT in ms (0 if first token not yet received) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_streaming_metrics_get_ttft(rac_streaming_metrics_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_streaming_metrics_get_ttft(rac_streaming_metrics_handle_t handle, double* out_ttft_ms); /** @@ -234,7 +234,7 @@ RAC_API rac_result_t rac_streaming_metrics_get_ttft(rac_streaming_metrics_handle * @param out_token_count Output: Number of tokens recorded * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_streaming_metrics_get_token_count(rac_streaming_metrics_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_streaming_metrics_get_token_count(rac_streaming_metrics_handle_t handle, int32_t* out_token_count); /** @@ -244,7 +244,7 @@ RAC_API rac_result_t rac_streaming_metrics_get_token_count(rac_streaming_metrics * @param out_text Output: Accumulated text (owned, must be freed) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_streaming_metrics_get_text(rac_streaming_metrics_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_streaming_metrics_get_text(rac_streaming_metrics_handle_t handle, char** out_text); /** @@ -258,7 +258,7 @@ RAC_API rac_result_t rac_streaming_metrics_get_text(rac_streaming_metrics_handle * @param output_tokens Actual output/completion token count (0 to use estimation) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_streaming_metrics_set_token_counts(rac_streaming_metrics_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_streaming_metrics_set_token_counts(rac_streaming_metrics_handle_t handle, int32_t input_tokens, int32_t output_tokens); @@ -272,7 +272,7 @@ RAC_API rac_result_t rac_streaming_metrics_set_token_counts(rac_streaming_metric * @param out_handle Output: Handle to the created service * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_generation_analytics_create(rac_generation_analytics_handle_t* out_handle); +RAC_API RAC_NODISCARD rac_result_t rac_generation_analytics_create(rac_generation_analytics_handle_t* out_handle); /** * @brief Destroy a generation analytics service. @@ -291,7 +291,7 @@ RAC_API void rac_generation_analytics_destroy(rac_generation_analytics_handle_t * @param model_id Model ID * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_generation_analytics_start(rac_generation_analytics_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_generation_analytics_start(rac_generation_analytics_handle_t handle, const char* generation_id, const char* model_id); @@ -305,7 +305,7 @@ RAC_API rac_result_t rac_generation_analytics_start(rac_generation_analytics_han * @param model_id Model ID * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_generation_analytics_start_streaming( +RAC_API RAC_NODISCARD rac_result_t rac_generation_analytics_start_streaming( rac_generation_analytics_handle_t handle, const char* generation_id, const char* model_id); /** @@ -317,7 +317,7 @@ RAC_API rac_result_t rac_generation_analytics_start_streaming( * @param generation_id Generation identifier * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_generation_analytics_track_first_token( +RAC_API RAC_NODISCARD rac_result_t rac_generation_analytics_track_first_token( rac_generation_analytics_handle_t handle, const char* generation_id); /** @@ -330,7 +330,7 @@ RAC_API rac_result_t rac_generation_analytics_track_first_token( * @param tokens_generated Number of tokens generated so far * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_generation_analytics_track_streaming_update( +RAC_API RAC_NODISCARD rac_result_t rac_generation_analytics_track_streaming_update( rac_generation_analytics_handle_t handle, const char* generation_id, int32_t tokens_generated); /** @@ -345,7 +345,7 @@ RAC_API rac_result_t rac_generation_analytics_track_streaming_update( * @param model_id Model ID used * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_generation_analytics_complete(rac_generation_analytics_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_generation_analytics_complete(rac_generation_analytics_handle_t handle, const char* generation_id, int32_t input_tokens, int32_t output_tokens, const char* model_id); @@ -360,7 +360,7 @@ RAC_API rac_result_t rac_generation_analytics_complete(rac_generation_analytics_ * @param error_code Error code * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_generation_analytics_track_failed(rac_generation_analytics_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_generation_analytics_track_failed(rac_generation_analytics_handle_t handle, const char* generation_id, rac_result_t error_code); @@ -373,7 +373,7 @@ RAC_API rac_result_t rac_generation_analytics_track_failed(rac_generation_analyt * @param out_metrics Output: Generation metrics * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_generation_analytics_get_metrics(rac_generation_analytics_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_generation_analytics_get_metrics(rac_generation_analytics_handle_t handle, rac_generation_metrics_t* out_metrics); /** @@ -382,7 +382,7 @@ RAC_API rac_result_t rac_generation_analytics_get_metrics(rac_generation_analyti * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_generation_analytics_reset(rac_generation_analytics_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_generation_analytics_reset(rac_generation_analytics_handle_t handle); // ============================================================================= // MEMORY MANAGEMENT diff --git a/sdk/runanywhere-commons/include/rac/features/llm/rac_llm_service.h b/sdk/runanywhere-commons/include/rac/features/llm/rac_llm_service.h index 9d89479a3..6365f8522 100644 --- a/sdk/runanywhere-commons/include/rac/features/llm/rac_llm_service.h +++ b/sdk/runanywhere-commons/include/rac/features/llm/rac_llm_service.h @@ -124,7 +124,7 @@ typedef struct rac_llm_service { * @param out_handle Output: Handle to the created service * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_create(const char* model_id, rac_handle_t* out_handle); +RAC_API RAC_NODISCARD rac_result_t rac_llm_create(const char* model_id, rac_handle_t* out_handle); /** * @brief Initialize an LLM service @@ -133,7 +133,7 @@ RAC_API rac_result_t rac_llm_create(const char* model_id, rac_handle_t* out_hand * @param model_path Path to the model file (can be NULL) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_initialize(rac_handle_t handle, const char* model_path); +RAC_API RAC_NODISCARD rac_result_t rac_llm_initialize(rac_handle_t handle, const char* model_path); /** * @brief Generate text from prompt @@ -144,7 +144,7 @@ RAC_API rac_result_t rac_llm_initialize(rac_handle_t handle, const char* model_p * @param out_result Output: Generation result (caller must free with rac_llm_result_free) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_generate(rac_handle_t handle, const char* prompt, +RAC_API RAC_NODISCARD rac_result_t rac_llm_generate(rac_handle_t handle, const char* prompt, const rac_llm_options_t* options, rac_llm_result_t* out_result); @@ -158,7 +158,7 @@ RAC_API rac_result_t rac_llm_generate(rac_handle_t handle, const char* prompt, * @param user_data User context passed to callback * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_generate_stream(rac_handle_t handle, const char* prompt, +RAC_API RAC_NODISCARD rac_result_t rac_llm_generate_stream(rac_handle_t handle, const char* prompt, const rac_llm_options_t* options, rac_llm_stream_callback_fn callback, void* user_data); @@ -182,7 +182,7 @@ RAC_API rac_result_t rac_llm_generate_stream(rac_handle_t handle, const char* pr * @param timing_out Output: Benchmark timing (can be NULL for no timing) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_generate_stream_with_timing(rac_handle_t handle, const char* prompt, +RAC_API RAC_NODISCARD rac_result_t rac_llm_generate_stream_with_timing(rac_handle_t handle, const char* prompt, const rac_llm_options_t* options, rac_llm_stream_callback_fn callback, void* user_data, @@ -195,7 +195,7 @@ RAC_API rac_result_t rac_llm_generate_stream_with_timing(rac_handle_t handle, co * @param out_info Output: Service information * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_get_info(rac_handle_t handle, rac_llm_info_t* out_info); +RAC_API RAC_NODISCARD rac_result_t rac_llm_get_info(rac_handle_t handle, rac_llm_info_t* out_info); /** * @brief Cancel ongoing generation @@ -203,7 +203,7 @@ RAC_API rac_result_t rac_llm_get_info(rac_handle_t handle, rac_llm_info_t* out_i * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_cancel(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_llm_cancel(rac_handle_t handle); /** * @brief Cleanup and release model resources @@ -211,7 +211,7 @@ RAC_API rac_result_t rac_llm_cancel(rac_handle_t handle); * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_cleanup(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_llm_cleanup(rac_handle_t handle); /** * @brief Destroy an LLM service instance @@ -241,7 +241,7 @@ RAC_API void rac_llm_result_free(rac_llm_result_t* result); * @param prompt System prompt text * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_inject_system_prompt(rac_handle_t handle, const char* prompt); +RAC_API RAC_NODISCARD rac_result_t rac_llm_inject_system_prompt(rac_handle_t handle, const char* prompt); /** * @brief Append text to the LLM's KV cache after current content @@ -253,7 +253,7 @@ RAC_API rac_result_t rac_llm_inject_system_prompt(rac_handle_t handle, const cha * @param text Text to append * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_append_context(rac_handle_t handle, const char* text); +RAC_API RAC_NODISCARD rac_result_t rac_llm_append_context(rac_handle_t handle, const char* text); /** * @brief Generate a response from accumulated KV cache state @@ -268,7 +268,7 @@ RAC_API rac_result_t rac_llm_append_context(rac_handle_t handle, const char* tex * @param out_result Output: Generation result * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_generate_from_context(rac_handle_t handle, const char* query, +RAC_API RAC_NODISCARD rac_result_t rac_llm_generate_from_context(rac_handle_t handle, const char* query, const rac_llm_options_t* options, rac_llm_result_t* out_result); @@ -281,7 +281,7 @@ RAC_API rac_result_t rac_llm_generate_from_context(rac_handle_t handle, const ch * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_llm_clear_context(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_llm_clear_context(rac_handle_t handle); #ifdef __cplusplus } diff --git a/sdk/runanywhere-commons/include/rac/features/llm/rac_llm_structured_output.h b/sdk/runanywhere-commons/include/rac/features/llm/rac_llm_structured_output.h index b50958275..2c0776a5c 100644 --- a/sdk/runanywhere-commons/include/rac/features/llm/rac_llm_structured_output.h +++ b/sdk/runanywhere-commons/include/rac/features/llm/rac_llm_structured_output.h @@ -39,7 +39,7 @@ extern "C" { * @param out_length Output: Length of extracted JSON string (can be NULL) * @return RAC_SUCCESS if JSON found and extracted, error code otherwise */ -RAC_API rac_result_t rac_structured_output_extract_json(const char* text, char** out_json, +RAC_API RAC_NODISCARD rac_result_t rac_structured_output_extract_json(const char* text, char** out_json, size_t* out_length); /** @@ -96,7 +96,7 @@ RAC_API rac_bool_t rac_structured_output_find_matching_bracket(const char* text, * @param out_prompt Output: Allocated prepared prompt (caller must free with rac_free) * @return RAC_SUCCESS on success, error code otherwise */ -RAC_API rac_result_t rac_structured_output_prepare_prompt( +RAC_API RAC_NODISCARD rac_result_t rac_structured_output_prepare_prompt( const char* original_prompt, const rac_structured_output_config_t* config, char** out_prompt); /** @@ -110,7 +110,7 @@ RAC_API rac_result_t rac_structured_output_prepare_prompt( * @param out_prompt Output: Allocated system prompt (caller must free with rac_free) * @return RAC_SUCCESS on success, error code otherwise */ -RAC_API rac_result_t rac_structured_output_get_system_prompt(const char* json_schema, +RAC_API RAC_NODISCARD rac_result_t rac_structured_output_get_system_prompt(const char* json_schema, char** out_prompt); /** diff --git a/sdk/runanywhere-commons/include/rac/features/llm/rac_tool_calling.h b/sdk/runanywhere-commons/include/rac/features/llm/rac_tool_calling.h index a6819e069..933573c36 100644 --- a/sdk/runanywhere-commons/include/rac/features/llm/rac_tool_calling.h +++ b/sdk/runanywhere-commons/include/rac/features/llm/rac_tool_calling.h @@ -156,7 +156,7 @@ typedef struct rac_tool_calling_options { * @param out_result Output: Parsed result (caller must free with rac_tool_call_free) * @return RAC_SUCCESS on success, error code otherwise */ -RAC_API rac_result_t rac_tool_call_parse(const char* llm_output, rac_tool_call_t* out_result); +RAC_API RAC_NODISCARD rac_result_t rac_tool_call_parse(const char* llm_output, rac_tool_call_t* out_result); /** * @brief Parse LLM output for tool calls with specified format @@ -172,7 +172,7 @@ RAC_API rac_result_t rac_tool_call_parse(const char* llm_output, rac_tool_call_t * @param out_result Output: Parsed result (caller must free with rac_tool_call_free) * @return RAC_SUCCESS on success, error code otherwise */ -RAC_API rac_result_t rac_tool_call_parse_with_format(const char* llm_output, +RAC_API RAC_NODISCARD rac_result_t rac_tool_call_parse_with_format(const char* llm_output, rac_tool_call_format_t format, rac_tool_call_t* out_result); @@ -229,7 +229,7 @@ RAC_API rac_tool_call_format_t rac_tool_call_format_from_name(const char* name); * @param out_prompt Output: Allocated prompt string (caller must free with rac_free) * @return RAC_SUCCESS on success, error code otherwise */ -RAC_API rac_result_t rac_tool_call_format_prompt(const rac_tool_definition_t* definitions, +RAC_API RAC_NODISCARD rac_result_t rac_tool_call_format_prompt(const rac_tool_definition_t* definitions, size_t num_definitions, char** out_prompt); /** @@ -244,7 +244,7 @@ RAC_API rac_result_t rac_tool_call_format_prompt(const rac_tool_definition_t* de * @param out_prompt Output: Allocated prompt string (caller must free with rac_free) * @return RAC_SUCCESS on success, error code otherwise */ -RAC_API rac_result_t rac_tool_call_format_prompt_with_format(const rac_tool_definition_t* definitions, +RAC_API RAC_NODISCARD rac_result_t rac_tool_call_format_prompt_with_format(const rac_tool_definition_t* definitions, size_t num_definitions, rac_tool_call_format_t format, char** out_prompt); @@ -258,7 +258,7 @@ RAC_API rac_result_t rac_tool_call_format_prompt_with_format(const rac_tool_defi * @param out_prompt Output: Allocated prompt string (caller must free with rac_free) * @return RAC_SUCCESS on success, error code otherwise */ -RAC_API rac_result_t rac_tool_call_format_prompt_json(const char* tools_json, char** out_prompt); +RAC_API RAC_NODISCARD rac_result_t rac_tool_call_format_prompt_json(const char* tools_json, char** out_prompt); /** * @brief Format tools from JSON array string with specified format @@ -268,7 +268,7 @@ RAC_API rac_result_t rac_tool_call_format_prompt_json(const char* tools_json, ch * @param out_prompt Output: Allocated prompt string (caller must free with rac_free) * @return RAC_SUCCESS on success, error code otherwise */ -RAC_API rac_result_t rac_tool_call_format_prompt_json_with_format(const char* tools_json, +RAC_API RAC_NODISCARD rac_result_t rac_tool_call_format_prompt_json_with_format(const char* tools_json, rac_tool_call_format_t format, char** out_prompt); @@ -285,7 +285,7 @@ RAC_API rac_result_t rac_tool_call_format_prompt_json_with_format(const char* to * @param out_prompt Output: Allocated prompt string (caller must free with rac_free) * @return RAC_SUCCESS on success, error code otherwise */ -RAC_API rac_result_t rac_tool_call_format_prompt_json_with_format_name(const char* tools_json, +RAC_API RAC_NODISCARD rac_result_t rac_tool_call_format_prompt_json_with_format_name(const char* tools_json, const char* format_name, char** out_prompt); @@ -300,7 +300,7 @@ RAC_API rac_result_t rac_tool_call_format_prompt_json_with_format_name(const cha * @param out_prompt Output: Complete formatted prompt (caller must free with rac_free) * @return RAC_SUCCESS on success, error code otherwise */ -RAC_API rac_result_t rac_tool_call_build_initial_prompt(const char* user_prompt, +RAC_API RAC_NODISCARD rac_result_t rac_tool_call_build_initial_prompt(const char* user_prompt, const char* tools_json, const rac_tool_calling_options_t* options, char** out_prompt); @@ -319,7 +319,7 @@ RAC_API rac_result_t rac_tool_call_build_initial_prompt(const char* user_prompt, * @param out_prompt Output: Follow-up prompt (caller must free with rac_free) * @return RAC_SUCCESS on success, error code otherwise */ -RAC_API rac_result_t rac_tool_call_build_followup_prompt(const char* original_user_prompt, +RAC_API RAC_NODISCARD rac_result_t rac_tool_call_build_followup_prompt(const char* original_user_prompt, const char* tools_prompt, const char* tool_name, const char* tool_result_json, @@ -339,7 +339,7 @@ RAC_API rac_result_t rac_tool_call_build_followup_prompt(const char* original_us * @param out_normalized Output: Normalized JSON (caller must free with rac_free) * @return RAC_SUCCESS on success, error code otherwise */ -RAC_API rac_result_t rac_tool_call_normalize_json(const char* json_str, char** out_normalized); +RAC_API RAC_NODISCARD rac_result_t rac_tool_call_normalize_json(const char* json_str, char** out_normalized); /** * @brief Serialize tool definitions to JSON array @@ -349,7 +349,7 @@ RAC_API rac_result_t rac_tool_call_normalize_json(const char* json_str, char** o * @param out_json Output: JSON array string (caller must free with rac_free) * @return RAC_SUCCESS on success, error code otherwise */ -RAC_API rac_result_t rac_tool_call_definitions_to_json(const rac_tool_definition_t* definitions, +RAC_API RAC_NODISCARD rac_result_t rac_tool_call_definitions_to_json(const rac_tool_definition_t* definitions, size_t num_definitions, char** out_json); /** @@ -362,7 +362,7 @@ RAC_API rac_result_t rac_tool_call_definitions_to_json(const rac_tool_definition * @param out_json Output: JSON string (caller must free with rac_free) * @return RAC_SUCCESS on success, error code otherwise */ -RAC_API rac_result_t rac_tool_call_result_to_json(const char* tool_name, rac_bool_t success, +RAC_API RAC_NODISCARD rac_result_t rac_tool_call_result_to_json(const char* tool_name, rac_bool_t success, const char* result_json, const char* error_message, char** out_json); diff --git a/sdk/runanywhere-commons/include/rac/features/platform/rac_diffusion_platform.h b/sdk/runanywhere-commons/include/rac/features/platform/rac_diffusion_platform.h index 075b8fa76..11f5fc29a 100644 --- a/sdk/runanywhere-commons/include/rac/features/platform/rac_diffusion_platform.h +++ b/sdk/runanywhere-commons/include/rac/features/platform/rac_diffusion_platform.h @@ -216,7 +216,7 @@ typedef struct rac_platform_diffusion_callbacks { * @param callbacks Callback functions (copied internally) * @return RAC_SUCCESS on success */ -RAC_API rac_result_t rac_platform_diffusion_set_callbacks( +RAC_API RAC_NODISCARD rac_result_t rac_platform_diffusion_set_callbacks( const rac_platform_diffusion_callbacks_t* callbacks); /** @@ -245,7 +245,7 @@ RAC_API rac_bool_t rac_platform_diffusion_is_available(void); * @param out_handle Output: Service handle * @return RAC_SUCCESS on success, or error code */ -RAC_API rac_result_t rac_diffusion_platform_create(const char* model_path, +RAC_API RAC_NODISCARD rac_result_t rac_diffusion_platform_create(const char* model_path, const rac_diffusion_platform_config_t* config, rac_diffusion_platform_handle_t* out_handle); @@ -264,7 +264,7 @@ RAC_API void rac_diffusion_platform_destroy(rac_diffusion_platform_handle_t hand * @param out_result Output: Generated image * @return RAC_SUCCESS on success, or error code */ -RAC_API rac_result_t rac_diffusion_platform_generate( +RAC_API RAC_NODISCARD rac_result_t rac_diffusion_platform_generate( rac_diffusion_platform_handle_t handle, const rac_diffusion_platform_options_t* options, rac_diffusion_platform_result_t* out_result); @@ -278,7 +278,7 @@ RAC_API rac_result_t rac_diffusion_platform_generate( * @param out_result Output: Generated image * @return RAC_SUCCESS on success, or error code */ -RAC_API rac_result_t rac_diffusion_platform_generate_with_progress( +RAC_API RAC_NODISCARD rac_result_t rac_diffusion_platform_generate_with_progress( rac_diffusion_platform_handle_t handle, const rac_diffusion_platform_options_t* options, rac_platform_diffusion_progress_fn progress_callback, void* progress_user_data, rac_diffusion_platform_result_t* out_result); @@ -289,7 +289,7 @@ RAC_API rac_result_t rac_diffusion_platform_generate_with_progress( * @param handle Service handle * @return RAC_SUCCESS on success, or error code */ -RAC_API rac_result_t rac_diffusion_platform_cancel(rac_diffusion_platform_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_diffusion_platform_cancel(rac_diffusion_platform_handle_t handle); /** * Frees a platform diffusion result. diff --git a/sdk/runanywhere-commons/include/rac/features/platform/rac_llm_platform.h b/sdk/runanywhere-commons/include/rac/features/platform/rac_llm_platform.h index c1f9d5bd2..d8a37a241 100644 --- a/sdk/runanywhere-commons/include/rac/features/platform/rac_llm_platform.h +++ b/sdk/runanywhere-commons/include/rac/features/platform/rac_llm_platform.h @@ -121,7 +121,7 @@ typedef struct rac_platform_llm_callbacks { * @param callbacks Callback functions (copied internally) * @return RAC_SUCCESS on success */ -RAC_API rac_result_t rac_platform_llm_set_callbacks(const rac_platform_llm_callbacks_t* callbacks); +RAC_API RAC_NODISCARD rac_result_t rac_platform_llm_set_callbacks(const rac_platform_llm_callbacks_t* callbacks); /** * Gets the current Swift callbacks. @@ -149,7 +149,7 @@ RAC_API rac_bool_t rac_platform_llm_is_available(void); * @param out_handle Output: Service handle * @return RAC_SUCCESS on success, or error code */ -RAC_API rac_result_t rac_llm_platform_create(const char* model_path, +RAC_API RAC_NODISCARD rac_result_t rac_llm_platform_create(const char* model_path, const rac_llm_platform_config_t* config, rac_llm_platform_handle_t* out_handle); @@ -169,7 +169,7 @@ RAC_API void rac_llm_platform_destroy(rac_llm_platform_handle_t handle); * @param out_response Output: Generated text (caller must free with free()) * @return RAC_SUCCESS on success, or error code */ -RAC_API rac_result_t rac_llm_platform_generate(rac_llm_platform_handle_t handle, const char* prompt, +RAC_API RAC_NODISCARD rac_result_t rac_llm_platform_generate(rac_llm_platform_handle_t handle, const char* prompt, const rac_llm_platform_options_t* options, char** out_response); @@ -188,14 +188,14 @@ RAC_API rac_result_t rac_llm_platform_generate(rac_llm_platform_handle_t handle, * * @return RAC_SUCCESS on success, or an error code */ -RAC_API rac_result_t rac_backend_platform_register(void); +RAC_API RAC_NODISCARD rac_result_t rac_backend_platform_register(void); /** * Unregisters the Platform backend. * * @return RAC_SUCCESS on success, or an error code */ -RAC_API rac_result_t rac_backend_platform_unregister(void); +RAC_API RAC_NODISCARD rac_result_t rac_backend_platform_unregister(void); #ifdef __cplusplus } diff --git a/sdk/runanywhere-commons/include/rac/features/platform/rac_tts_platform.h b/sdk/runanywhere-commons/include/rac/features/platform/rac_tts_platform.h index 5cfad832f..426c7fe80 100644 --- a/sdk/runanywhere-commons/include/rac/features/platform/rac_tts_platform.h +++ b/sdk/runanywhere-commons/include/rac/features/platform/rac_tts_platform.h @@ -135,7 +135,7 @@ typedef struct rac_platform_tts_callbacks { * @param callbacks Callback functions (copied internally) * @return RAC_SUCCESS on success */ -RAC_API rac_result_t rac_platform_tts_set_callbacks(const rac_platform_tts_callbacks_t* callbacks); +RAC_API RAC_NODISCARD rac_result_t rac_platform_tts_set_callbacks(const rac_platform_tts_callbacks_t* callbacks); /** * Gets the current Swift callbacks. @@ -162,7 +162,7 @@ RAC_API rac_bool_t rac_platform_tts_is_available(void); * @param out_handle Output: Service handle * @return RAC_SUCCESS on success, or error code */ -RAC_API rac_result_t rac_tts_platform_create(const rac_tts_platform_config_t* config, +RAC_API RAC_NODISCARD rac_result_t rac_tts_platform_create(const rac_tts_platform_config_t* config, rac_tts_platform_handle_t* out_handle); /** @@ -180,7 +180,7 @@ RAC_API void rac_tts_platform_destroy(rac_tts_platform_handle_t handle); * @param options Synthesis options (can be NULL for defaults) * @return RAC_SUCCESS on success, or error code */ -RAC_API rac_result_t rac_tts_platform_synthesize(rac_tts_platform_handle_t handle, const char* text, +RAC_API RAC_NODISCARD rac_result_t rac_tts_platform_synthesize(rac_tts_platform_handle_t handle, const char* text, const rac_tts_platform_options_t* options); /** diff --git a/sdk/runanywhere-commons/include/rac/features/rag/rac_rag.h b/sdk/runanywhere-commons/include/rac/features/rag/rac_rag.h index d0906366f..058e17c95 100644 --- a/sdk/runanywhere-commons/include/rac/features/rag/rac_rag.h +++ b/sdk/runanywhere-commons/include/rac/features/rag/rac_rag.h @@ -23,14 +23,14 @@ extern "C" { * * @return RAC_SUCCESS on success, error code otherwise */ -RAC_API rac_result_t rac_backend_rag_register(void); +RAC_API RAC_NODISCARD rac_result_t rac_backend_rag_register(void); /** * @brief Unregister the RAG pipeline module * * @return RAC_SUCCESS on success, error code otherwise */ -RAC_API rac_result_t rac_backend_rag_unregister(void); +RAC_API RAC_NODISCARD rac_result_t rac_backend_rag_unregister(void); #ifdef __cplusplus } diff --git a/sdk/runanywhere-commons/include/rac/features/rag/rac_rag_pipeline.h b/sdk/runanywhere-commons/include/rac/features/rag/rac_rag_pipeline.h index c6841b3f7..80337a70c 100644 --- a/sdk/runanywhere-commons/include/rac/features/rag/rac_rag_pipeline.h +++ b/sdk/runanywhere-commons/include/rac/features/rag/rac_rag_pipeline.h @@ -176,7 +176,7 @@ typedef struct rac_rag_result { * @param out_pipeline Pointer to receive pipeline handle * @return RAC_SUCCESS on success, error code otherwise */ -RAC_API rac_result_t rac_rag_pipeline_create( +RAC_API RAC_NODISCARD rac_result_t rac_rag_pipeline_create( rac_handle_t llm_service, rac_handle_t embeddings_service, const rac_rag_pipeline_config_t* config, @@ -194,7 +194,7 @@ RAC_API rac_result_t rac_rag_pipeline_create( * @param out_pipeline Pointer to receive pipeline handle * @return RAC_SUCCESS on success, error code otherwise */ -RAC_API rac_result_t rac_rag_pipeline_create_standalone( +RAC_API RAC_NODISCARD rac_result_t rac_rag_pipeline_create_standalone( const rac_rag_config_t* config, rac_rag_pipeline_t** out_pipeline ); @@ -209,7 +209,7 @@ RAC_API rac_result_t rac_rag_pipeline_create_standalone( * @param metadata_json Optional JSON metadata * @return RAC_SUCCESS on success, error code otherwise */ -RAC_API rac_result_t rac_rag_add_document( +RAC_API RAC_NODISCARD rac_result_t rac_rag_add_document( rac_rag_pipeline_t* pipeline, const char* document_text, const char* metadata_json @@ -226,7 +226,7 @@ RAC_API rac_result_t rac_rag_add_document( * @param count Number of documents * @return RAC_SUCCESS on success, error code otherwise */ -RAC_API rac_result_t rac_rag_add_documents_batch( +RAC_API RAC_NODISCARD rac_result_t rac_rag_add_documents_batch( rac_rag_pipeline_t* pipeline, const char** documents, const char** metadata_array, @@ -243,7 +243,7 @@ RAC_API rac_result_t rac_rag_add_documents_batch( * @param out_result Pointer to receive result (caller must free with rac_rag_result_free) * @return RAC_SUCCESS on success, error code otherwise */ -RAC_API rac_result_t rac_rag_query( +RAC_API RAC_NODISCARD rac_result_t rac_rag_query( rac_rag_pipeline_t* pipeline, const rac_rag_query_t* query, rac_rag_result_t* out_result @@ -255,7 +255,7 @@ RAC_API rac_result_t rac_rag_query( * @param pipeline RAG pipeline handle * @return RAC_SUCCESS on success, error code otherwise */ -RAC_API rac_result_t rac_rag_clear_documents(rac_rag_pipeline_t* pipeline); +RAC_API RAC_NODISCARD rac_result_t rac_rag_clear_documents(rac_rag_pipeline_t* pipeline); /** * @brief Get number of indexed documents @@ -272,7 +272,7 @@ RAC_API size_t rac_rag_get_document_count(rac_rag_pipeline_t* pipeline); * @param out_stats_json Pointer to receive JSON stats string (caller must free) * @return RAC_SUCCESS on success, error code otherwise */ -RAC_API rac_result_t rac_rag_get_statistics( +RAC_API RAC_NODISCARD rac_result_t rac_rag_get_statistics( rac_rag_pipeline_t* pipeline, char** out_stats_json ); diff --git a/sdk/runanywhere-commons/include/rac/features/stt/rac_stt_analytics.h b/sdk/runanywhere-commons/include/rac/features/stt/rac_stt_analytics.h index 191dfc3ee..576e46995 100644 --- a/sdk/runanywhere-commons/include/rac/features/stt/rac_stt_analytics.h +++ b/sdk/runanywhere-commons/include/rac/features/stt/rac_stt_analytics.h @@ -74,7 +74,7 @@ typedef struct rac_stt_metrics { * @param out_handle Output: Handle to the created service * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_stt_analytics_create(rac_stt_analytics_handle_t* out_handle); +RAC_API RAC_NODISCARD rac_result_t rac_stt_analytics_create(rac_stt_analytics_handle_t* out_handle); /** * @brief Destroy an STT analytics service instance @@ -101,7 +101,7 @@ RAC_API void rac_stt_analytics_destroy(rac_stt_analytics_handle_t handle); * @param out_transcription_id Output: Generated unique ID (owned, must be freed with rac_free) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_stt_analytics_start_transcription( +RAC_API RAC_NODISCARD rac_result_t rac_stt_analytics_start_transcription( rac_stt_analytics_handle_t handle, const char* model_id, double audio_length_ms, int32_t audio_size_bytes, const char* language, rac_bool_t is_streaming, int32_t sample_rate, rac_inference_framework_t framework, char** out_transcription_id); @@ -113,7 +113,7 @@ RAC_API rac_result_t rac_stt_analytics_start_transcription( * @param text Partial transcript text * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_stt_analytics_track_partial_transcript(rac_stt_analytics_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_stt_analytics_track_partial_transcript(rac_stt_analytics_handle_t handle, const char* text); /** @@ -124,7 +124,7 @@ RAC_API rac_result_t rac_stt_analytics_track_partial_transcript(rac_stt_analytic * @param confidence Confidence score (0.0 to 1.0) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_stt_analytics_track_final_transcript(rac_stt_analytics_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_stt_analytics_track_final_transcript(rac_stt_analytics_handle_t handle, const char* text, float confidence); /** @@ -136,7 +136,7 @@ RAC_API rac_result_t rac_stt_analytics_track_final_transcript(rac_stt_analytics_ * @param confidence Confidence score (0.0 to 1.0) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_stt_analytics_complete_transcription(rac_stt_analytics_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_stt_analytics_complete_transcription(rac_stt_analytics_handle_t handle, const char* transcription_id, const char* text, float confidence); @@ -149,7 +149,7 @@ RAC_API rac_result_t rac_stt_analytics_complete_transcription(rac_stt_analytics_ * @param error_message Error message * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_stt_analytics_track_transcription_failed(rac_stt_analytics_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_stt_analytics_track_transcription_failed(rac_stt_analytics_handle_t handle, const char* transcription_id, rac_result_t error_code, const char* error_message); @@ -162,7 +162,7 @@ RAC_API rac_result_t rac_stt_analytics_track_transcription_failed(rac_stt_analyt * @param confidence Detection confidence (0.0 to 1.0) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_stt_analytics_track_language_detection(rac_stt_analytics_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_stt_analytics_track_language_detection(rac_stt_analytics_handle_t handle, const char* language, float confidence); @@ -177,7 +177,7 @@ RAC_API rac_result_t rac_stt_analytics_track_language_detection(rac_stt_analytic * @param transcription_id Transcription ID (can be NULL) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_stt_analytics_track_error(rac_stt_analytics_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_stt_analytics_track_error(rac_stt_analytics_handle_t handle, rac_result_t error_code, const char* error_message, const char* operation, const char* model_id, @@ -194,7 +194,7 @@ RAC_API rac_result_t rac_stt_analytics_track_error(rac_stt_analytics_handle_t ha * @param out_metrics Output: Metrics structure * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_stt_analytics_get_metrics(rac_stt_analytics_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_stt_analytics_get_metrics(rac_stt_analytics_handle_t handle, rac_stt_metrics_t* out_metrics); #ifdef __cplusplus diff --git a/sdk/runanywhere-commons/include/rac/features/stt/rac_stt_component.h b/sdk/runanywhere-commons/include/rac/features/stt/rac_stt_component.h index b3bd3d4e2..d0b259c8c 100644 --- a/sdk/runanywhere-commons/include/rac/features/stt/rac_stt_component.h +++ b/sdk/runanywhere-commons/include/rac/features/stt/rac_stt_component.h @@ -32,7 +32,7 @@ extern "C" { * @param out_handle Output: Handle to the component * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_stt_component_create(rac_handle_t* out_handle); +RAC_API RAC_NODISCARD rac_result_t rac_stt_component_create(rac_handle_t* out_handle); /** * @brief Configure the STT component @@ -41,7 +41,7 @@ RAC_API rac_result_t rac_stt_component_create(rac_handle_t* out_handle); * @param config Configuration * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_stt_component_configure(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_stt_component_configure(rac_handle_t handle, const rac_stt_config_t* config); /** @@ -71,7 +71,7 @@ RAC_API const char* rac_stt_component_get_model_id(rac_handle_t handle); * Optional: if NULL, defaults to model_id * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_stt_component_load_model(rac_handle_t handle, const char* model_path, +RAC_API RAC_NODISCARD rac_result_t rac_stt_component_load_model(rac_handle_t handle, const char* model_path, const char* model_id, const char* model_name); /** @@ -80,7 +80,7 @@ RAC_API rac_result_t rac_stt_component_load_model(rac_handle_t handle, const cha * @param handle Component handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_stt_component_unload(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_stt_component_unload(rac_handle_t handle); /** * @brief Cleanup and reset the component @@ -88,7 +88,7 @@ RAC_API rac_result_t rac_stt_component_unload(rac_handle_t handle); * @param handle Component handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_stt_component_cleanup(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_stt_component_cleanup(rac_handle_t handle); /** * @brief Transcribe audio data (batch mode) @@ -100,7 +100,7 @@ RAC_API rac_result_t rac_stt_component_cleanup(rac_handle_t handle); * @param out_result Output: Transcription result * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_stt_component_transcribe(rac_handle_t handle, const void* audio_data, +RAC_API RAC_NODISCARD rac_result_t rac_stt_component_transcribe(rac_handle_t handle, const void* audio_data, size_t audio_size, const rac_stt_options_t* options, rac_stt_result_t* out_result); @@ -124,7 +124,7 @@ RAC_API rac_bool_t rac_stt_component_supports_streaming(rac_handle_t handle); * @param user_data User context passed to callback * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_stt_component_transcribe_stream(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_stt_component_transcribe_stream(rac_handle_t handle, const void* audio_data, size_t audio_size, const rac_stt_options_t* options, rac_stt_stream_callback_t callback, @@ -145,7 +145,7 @@ RAC_API rac_lifecycle_state_t rac_stt_component_get_state(rac_handle_t handle); * @param out_metrics Output: Lifecycle metrics * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_stt_component_get_metrics(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_stt_component_get_metrics(rac_handle_t handle, rac_lifecycle_metrics_t* out_metrics); /** diff --git a/sdk/runanywhere-commons/include/rac/features/stt/rac_stt_events.h b/sdk/runanywhere-commons/include/rac/features/stt/rac_stt_events.h index 680d8123a..96442147d 100644 --- a/sdk/runanywhere-commons/include/rac/features/stt/rac_stt_events.h +++ b/sdk/runanywhere-commons/include/rac/features/stt/rac_stt_events.h @@ -32,26 +32,26 @@ typedef enum rac_stt_event_type { // EVENT PUBLISHING FUNCTIONS // ============================================================================= -RAC_API rac_result_t rac_stt_event_transcription_started( +RAC_API RAC_NODISCARD rac_result_t rac_stt_event_transcription_started( const char* transcription_id, const char* model_id, double audio_length_ms, int32_t audio_size_bytes, const char* language, rac_bool_t is_streaming, rac_inference_framework_t framework); -RAC_API rac_result_t rac_stt_event_partial_transcript(const char* text, int32_t word_count); +RAC_API RAC_NODISCARD rac_result_t rac_stt_event_partial_transcript(const char* text, int32_t word_count); -RAC_API rac_result_t rac_stt_event_final_transcript(const char* text, float confidence); +RAC_API RAC_NODISCARD rac_result_t rac_stt_event_final_transcript(const char* text, float confidence); -RAC_API rac_result_t rac_stt_event_transcription_completed( +RAC_API RAC_NODISCARD rac_result_t rac_stt_event_transcription_completed( const char* transcription_id, const char* model_id, const char* text, float confidence, double duration_ms, double audio_length_ms, int32_t word_count, double real_time_factor, const char* language, rac_bool_t is_streaming, rac_inference_framework_t framework); -RAC_API rac_result_t rac_stt_event_transcription_failed(const char* transcription_id, +RAC_API RAC_NODISCARD rac_result_t rac_stt_event_transcription_failed(const char* transcription_id, const char* model_id, rac_result_t error_code, const char* error_message); -RAC_API rac_result_t rac_stt_event_language_detected(const char* language, float confidence); +RAC_API RAC_NODISCARD rac_result_t rac_stt_event_language_detected(const char* language, float confidence); RAC_API const char* rac_stt_event_type_string(rac_stt_event_type_t event_type); diff --git a/sdk/runanywhere-commons/include/rac/features/stt/rac_stt_service.h b/sdk/runanywhere-commons/include/rac/features/stt/rac_stt_service.h index 521d5dfad..cea43bf9f 100644 --- a/sdk/runanywhere-commons/include/rac/features/stt/rac_stt_service.h +++ b/sdk/runanywhere-commons/include/rac/features/stt/rac_stt_service.h @@ -76,7 +76,7 @@ typedef struct rac_stt_service { * @param out_handle Output: Handle to the created service * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_stt_create(const char* model_path, rac_handle_t* out_handle); +RAC_API RAC_NODISCARD rac_result_t rac_stt_create(const char* model_path, rac_handle_t* out_handle); /** * @brief Initialize an STT service @@ -85,7 +85,7 @@ RAC_API rac_result_t rac_stt_create(const char* model_path, rac_handle_t* out_ha * @param model_path Path to the model file (can be NULL) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_stt_initialize(rac_handle_t handle, const char* model_path); +RAC_API RAC_NODISCARD rac_result_t rac_stt_initialize(rac_handle_t handle, const char* model_path); /** * @brief Transcribe audio data (batch mode) @@ -97,7 +97,7 @@ RAC_API rac_result_t rac_stt_initialize(rac_handle_t handle, const char* model_p * @param out_result Output: Transcription result (caller must free with rac_stt_result_free) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_stt_transcribe(rac_handle_t handle, const void* audio_data, +RAC_API RAC_NODISCARD rac_result_t rac_stt_transcribe(rac_handle_t handle, const void* audio_data, size_t audio_size, const rac_stt_options_t* options, rac_stt_result_t* out_result); @@ -112,7 +112,7 @@ RAC_API rac_result_t rac_stt_transcribe(rac_handle_t handle, const void* audio_d * @param user_data User context passed to callback * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_stt_transcribe_stream(rac_handle_t handle, const void* audio_data, +RAC_API RAC_NODISCARD rac_result_t rac_stt_transcribe_stream(rac_handle_t handle, const void* audio_data, size_t audio_size, const rac_stt_options_t* options, rac_stt_stream_callback_t callback, void* user_data); @@ -123,7 +123,7 @@ RAC_API rac_result_t rac_stt_transcribe_stream(rac_handle_t handle, const void* * @param out_info Output: Service information * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_stt_get_info(rac_handle_t handle, rac_stt_info_t* out_info); +RAC_API RAC_NODISCARD rac_result_t rac_stt_get_info(rac_handle_t handle, rac_stt_info_t* out_info); /** * @brief Cleanup and release resources @@ -131,7 +131,7 @@ RAC_API rac_result_t rac_stt_get_info(rac_handle_t handle, rac_stt_info_t* out_i * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_stt_cleanup(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_stt_cleanup(rac_handle_t handle); /** * @brief Destroy an STT service instance diff --git a/sdk/runanywhere-commons/include/rac/features/tts/rac_tts_analytics.h b/sdk/runanywhere-commons/include/rac/features/tts/rac_tts_analytics.h index 83b0d0305..5c4ce3fbd 100644 --- a/sdk/runanywhere-commons/include/rac/features/tts/rac_tts_analytics.h +++ b/sdk/runanywhere-commons/include/rac/features/tts/rac_tts_analytics.h @@ -74,7 +74,7 @@ typedef struct rac_tts_metrics { * @param out_handle Output: Handle to the created service * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_tts_analytics_create(rac_tts_analytics_handle_t* out_handle); +RAC_API RAC_NODISCARD rac_result_t rac_tts_analytics_create(rac_tts_analytics_handle_t* out_handle); /** * @brief Destroy a TTS analytics service instance @@ -98,7 +98,7 @@ RAC_API void rac_tts_analytics_destroy(rac_tts_analytics_handle_t handle); * @param out_synthesis_id Output: Generated unique ID (owned, must be freed with rac_free) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_tts_analytics_start_synthesis(rac_tts_analytics_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_tts_analytics_start_synthesis(rac_tts_analytics_handle_t handle, const char* text, const char* voice, int32_t sample_rate, rac_inference_framework_t framework, @@ -112,7 +112,7 @@ RAC_API rac_result_t rac_tts_analytics_start_synthesis(rac_tts_analytics_handle_ * @param chunk_size Size of the chunk in bytes * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_tts_analytics_track_synthesis_chunk(rac_tts_analytics_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_tts_analytics_track_synthesis_chunk(rac_tts_analytics_handle_t handle, const char* synthesis_id, int32_t chunk_size); @@ -125,7 +125,7 @@ RAC_API rac_result_t rac_tts_analytics_track_synthesis_chunk(rac_tts_analytics_h * @param audio_size_bytes Size of the generated audio in bytes * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_tts_analytics_complete_synthesis(rac_tts_analytics_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_tts_analytics_complete_synthesis(rac_tts_analytics_handle_t handle, const char* synthesis_id, double audio_duration_ms, int32_t audio_size_bytes); @@ -139,7 +139,7 @@ RAC_API rac_result_t rac_tts_analytics_complete_synthesis(rac_tts_analytics_hand * @param error_message Error message * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_tts_analytics_track_synthesis_failed(rac_tts_analytics_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_tts_analytics_track_synthesis_failed(rac_tts_analytics_handle_t handle, const char* synthesis_id, rac_result_t error_code, const char* error_message); @@ -155,7 +155,7 @@ RAC_API rac_result_t rac_tts_analytics_track_synthesis_failed(rac_tts_analytics_ * @param synthesis_id Synthesis ID (can be NULL) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_tts_analytics_track_error(rac_tts_analytics_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_tts_analytics_track_error(rac_tts_analytics_handle_t handle, rac_result_t error_code, const char* error_message, const char* operation, const char* model_id, const char* synthesis_id); @@ -171,7 +171,7 @@ RAC_API rac_result_t rac_tts_analytics_track_error(rac_tts_analytics_handle_t ha * @param out_metrics Output: Metrics structure * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_tts_analytics_get_metrics(rac_tts_analytics_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_tts_analytics_get_metrics(rac_tts_analytics_handle_t handle, rac_tts_metrics_t* out_metrics); #ifdef __cplusplus diff --git a/sdk/runanywhere-commons/include/rac/features/tts/rac_tts_component.h b/sdk/runanywhere-commons/include/rac/features/tts/rac_tts_component.h index 6bff4c5a0..856786eda 100644 --- a/sdk/runanywhere-commons/include/rac/features/tts/rac_tts_component.h +++ b/sdk/runanywhere-commons/include/rac/features/tts/rac_tts_component.h @@ -32,7 +32,7 @@ extern "C" { * @param out_handle Output: Handle to the component * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_tts_component_create(rac_handle_t* out_handle); +RAC_API RAC_NODISCARD rac_result_t rac_tts_component_create(rac_handle_t* out_handle); /** * @brief Configure the TTS component @@ -41,7 +41,7 @@ RAC_API rac_result_t rac_tts_component_create(rac_handle_t* out_handle); * @param config Configuration * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_tts_component_configure(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_tts_component_configure(rac_handle_t handle, const rac_tts_config_t* config); /** @@ -71,7 +71,7 @@ RAC_API const char* rac_tts_component_get_voice_id(rac_handle_t handle); * Optional: if NULL, defaults to voice_id * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_tts_component_load_voice(rac_handle_t handle, const char* voice_path, +RAC_API RAC_NODISCARD rac_result_t rac_tts_component_load_voice(rac_handle_t handle, const char* voice_path, const char* voice_id, const char* voice_name); /** @@ -80,7 +80,7 @@ RAC_API rac_result_t rac_tts_component_load_voice(rac_handle_t handle, const cha * @param handle Component handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_tts_component_unload(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_tts_component_unload(rac_handle_t handle); /** * @brief Cleanup and reset the component @@ -88,7 +88,7 @@ RAC_API rac_result_t rac_tts_component_unload(rac_handle_t handle); * @param handle Component handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_tts_component_cleanup(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_tts_component_cleanup(rac_handle_t handle); /** * @brief Stop current synthesis @@ -96,7 +96,7 @@ RAC_API rac_result_t rac_tts_component_cleanup(rac_handle_t handle); * @param handle Component handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_tts_component_stop(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_tts_component_stop(rac_handle_t handle); /** * @brief Synthesize text to audio @@ -107,7 +107,7 @@ RAC_API rac_result_t rac_tts_component_stop(rac_handle_t handle); * @param out_result Output: Synthesis result * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_tts_component_synthesize(rac_handle_t handle, const char* text, +RAC_API RAC_NODISCARD rac_result_t rac_tts_component_synthesize(rac_handle_t handle, const char* text, const rac_tts_options_t* options, rac_tts_result_t* out_result); @@ -121,7 +121,7 @@ RAC_API rac_result_t rac_tts_component_synthesize(rac_handle_t handle, const cha * @param user_data User context passed to callback * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_tts_component_synthesize_stream(rac_handle_t handle, const char* text, +RAC_API RAC_NODISCARD rac_result_t rac_tts_component_synthesize_stream(rac_handle_t handle, const char* text, const rac_tts_options_t* options, rac_tts_stream_callback_t callback, void* user_data); @@ -141,7 +141,7 @@ RAC_API rac_lifecycle_state_t rac_tts_component_get_state(rac_handle_t handle); * @param out_metrics Output: Lifecycle metrics * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_tts_component_get_metrics(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_tts_component_get_metrics(rac_handle_t handle, rac_lifecycle_metrics_t* out_metrics); /** diff --git a/sdk/runanywhere-commons/include/rac/features/tts/rac_tts_events.h b/sdk/runanywhere-commons/include/rac/features/tts/rac_tts_events.h index fbf985904..fd337cf6a 100644 --- a/sdk/runanywhere-commons/include/rac/features/tts/rac_tts_events.h +++ b/sdk/runanywhere-commons/include/rac/features/tts/rac_tts_events.h @@ -30,18 +30,18 @@ typedef enum rac_tts_event_type { // EVENT PUBLISHING FUNCTIONS // ============================================================================= -RAC_API rac_result_t rac_tts_event_synthesis_started(const char* synthesis_id, const char* model_id, +RAC_API RAC_NODISCARD rac_result_t rac_tts_event_synthesis_started(const char* synthesis_id, const char* model_id, int32_t character_count, int32_t sample_rate, rac_inference_framework_t framework); -RAC_API rac_result_t rac_tts_event_synthesis_chunk(const char* synthesis_id, int32_t chunk_size); +RAC_API RAC_NODISCARD rac_result_t rac_tts_event_synthesis_chunk(const char* synthesis_id, int32_t chunk_size); -RAC_API rac_result_t rac_tts_event_synthesis_completed( +RAC_API RAC_NODISCARD rac_result_t rac_tts_event_synthesis_completed( const char* synthesis_id, const char* model_id, int32_t character_count, double audio_duration_ms, int32_t audio_size_bytes, double processing_duration_ms, double characters_per_second, int32_t sample_rate, rac_inference_framework_t framework); -RAC_API rac_result_t rac_tts_event_synthesis_failed(const char* synthesis_id, const char* model_id, +RAC_API RAC_NODISCARD rac_result_t rac_tts_event_synthesis_failed(const char* synthesis_id, const char* model_id, rac_result_t error_code, const char* error_message); diff --git a/sdk/runanywhere-commons/include/rac/features/tts/rac_tts_service.h b/sdk/runanywhere-commons/include/rac/features/tts/rac_tts_service.h index 30d3e22b5..af0272adf 100644 --- a/sdk/runanywhere-commons/include/rac/features/tts/rac_tts_service.h +++ b/sdk/runanywhere-commons/include/rac/features/tts/rac_tts_service.h @@ -79,7 +79,7 @@ typedef struct rac_tts_service { * @param out_handle Output: Handle to the created service * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_tts_create(const char* voice_id, rac_handle_t* out_handle); +RAC_API RAC_NODISCARD rac_result_t rac_tts_create(const char* voice_id, rac_handle_t* out_handle); /** * @brief Initialize a TTS service @@ -87,7 +87,7 @@ RAC_API rac_result_t rac_tts_create(const char* voice_id, rac_handle_t* out_hand * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_tts_initialize(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_tts_initialize(rac_handle_t handle); /** * @brief Synthesize text to audio @@ -98,7 +98,7 @@ RAC_API rac_result_t rac_tts_initialize(rac_handle_t handle); * @param out_result Output: Synthesis result (caller must free with rac_tts_result_free) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_tts_synthesize(rac_handle_t handle, const char* text, +RAC_API RAC_NODISCARD rac_result_t rac_tts_synthesize(rac_handle_t handle, const char* text, const rac_tts_options_t* options, rac_tts_result_t* out_result); @@ -112,7 +112,7 @@ RAC_API rac_result_t rac_tts_synthesize(rac_handle_t handle, const char* text, * @param user_data User context passed to callback * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_tts_synthesize_stream(rac_handle_t handle, const char* text, +RAC_API RAC_NODISCARD rac_result_t rac_tts_synthesize_stream(rac_handle_t handle, const char* text, const rac_tts_options_t* options, rac_tts_stream_callback_t callback, void* user_data); @@ -122,7 +122,7 @@ RAC_API rac_result_t rac_tts_synthesize_stream(rac_handle_t handle, const char* * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_tts_stop(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_tts_stop(rac_handle_t handle); /** * @brief Get service information @@ -131,7 +131,7 @@ RAC_API rac_result_t rac_tts_stop(rac_handle_t handle); * @param out_info Output: Service information * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_tts_get_info(rac_handle_t handle, rac_tts_info_t* out_info); +RAC_API RAC_NODISCARD rac_result_t rac_tts_get_info(rac_handle_t handle, rac_tts_info_t* out_info); /** * @brief Cleanup and release resources @@ -139,7 +139,7 @@ RAC_API rac_result_t rac_tts_get_info(rac_handle_t handle, rac_tts_info_t* out_i * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_tts_cleanup(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_tts_cleanup(rac_handle_t handle); /** * @brief Destroy a TTS service instance diff --git a/sdk/runanywhere-commons/include/rac/features/vad/rac_vad_analytics.h b/sdk/runanywhere-commons/include/rac/features/vad/rac_vad_analytics.h index ac4490e3b..b061553f5 100644 --- a/sdk/runanywhere-commons/include/rac/features/vad/rac_vad_analytics.h +++ b/sdk/runanywhere-commons/include/rac/features/vad/rac_vad_analytics.h @@ -64,7 +64,7 @@ typedef struct rac_vad_metrics { * @param out_handle Output: Handle to the created service * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_analytics_create(rac_vad_analytics_handle_t* out_handle); +RAC_API RAC_NODISCARD rac_result_t rac_vad_analytics_create(rac_vad_analytics_handle_t* out_handle); /** * @brief Destroy a VAD analytics service instance @@ -84,7 +84,7 @@ RAC_API void rac_vad_analytics_destroy(rac_vad_analytics_handle_t handle); * @param framework The inference framework being used * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_analytics_track_initialized(rac_vad_analytics_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_vad_analytics_track_initialized(rac_vad_analytics_handle_t handle, rac_inference_framework_t framework); /** @@ -96,7 +96,7 @@ RAC_API rac_result_t rac_vad_analytics_track_initialized(rac_vad_analytics_handl * @param framework The inference framework * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_analytics_track_initialization_failed( +RAC_API RAC_NODISCARD rac_result_t rac_vad_analytics_track_initialization_failed( rac_vad_analytics_handle_t handle, rac_result_t error_code, const char* error_message, rac_inference_framework_t framework); @@ -106,7 +106,7 @@ RAC_API rac_result_t rac_vad_analytics_track_initialization_failed( * @param handle Analytics service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_analytics_track_cleaned_up(rac_vad_analytics_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_vad_analytics_track_cleaned_up(rac_vad_analytics_handle_t handle); // ============================================================================= // DETECTION TRACKING @@ -118,7 +118,7 @@ RAC_API rac_result_t rac_vad_analytics_track_cleaned_up(rac_vad_analytics_handle * @param handle Analytics service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_analytics_track_started(rac_vad_analytics_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_vad_analytics_track_started(rac_vad_analytics_handle_t handle); /** * @brief Track VAD stopped @@ -126,7 +126,7 @@ RAC_API rac_result_t rac_vad_analytics_track_started(rac_vad_analytics_handle_t * @param handle Analytics service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_analytics_track_stopped(rac_vad_analytics_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_vad_analytics_track_stopped(rac_vad_analytics_handle_t handle); /** * @brief Track speech detected (start of speech/voice activity) @@ -134,7 +134,7 @@ RAC_API rac_result_t rac_vad_analytics_track_stopped(rac_vad_analytics_handle_t * @param handle Analytics service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_analytics_track_speech_start(rac_vad_analytics_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_vad_analytics_track_speech_start(rac_vad_analytics_handle_t handle); /** * @brief Track speech ended (silence detected after speech) @@ -142,7 +142,7 @@ RAC_API rac_result_t rac_vad_analytics_track_speech_start(rac_vad_analytics_hand * @param handle Analytics service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_analytics_track_speech_end(rac_vad_analytics_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_vad_analytics_track_speech_end(rac_vad_analytics_handle_t handle); /** * @brief Track VAD paused @@ -150,7 +150,7 @@ RAC_API rac_result_t rac_vad_analytics_track_speech_end(rac_vad_analytics_handle * @param handle Analytics service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_analytics_track_paused(rac_vad_analytics_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_vad_analytics_track_paused(rac_vad_analytics_handle_t handle); /** * @brief Track VAD resumed @@ -158,7 +158,7 @@ RAC_API rac_result_t rac_vad_analytics_track_paused(rac_vad_analytics_handle_t h * @param handle Analytics service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_analytics_track_resumed(rac_vad_analytics_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_vad_analytics_track_resumed(rac_vad_analytics_handle_t handle); // ============================================================================= // MODEL LIFECYCLE (for model-based VAD) @@ -173,7 +173,7 @@ RAC_API rac_result_t rac_vad_analytics_track_resumed(rac_vad_analytics_handle_t * @param framework The inference framework * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_analytics_track_model_load_started( +RAC_API RAC_NODISCARD rac_result_t rac_vad_analytics_track_model_load_started( rac_vad_analytics_handle_t handle, const char* model_id, int64_t model_size_bytes, rac_inference_framework_t framework); @@ -186,7 +186,7 @@ RAC_API rac_result_t rac_vad_analytics_track_model_load_started( * @param model_size_bytes Size of the model in bytes * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_analytics_track_model_load_completed(rac_vad_analytics_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_vad_analytics_track_model_load_completed(rac_vad_analytics_handle_t handle, const char* model_id, double duration_ms, int64_t model_size_bytes); @@ -200,7 +200,7 @@ RAC_API rac_result_t rac_vad_analytics_track_model_load_completed(rac_vad_analyt * @param error_message Error message * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_analytics_track_model_load_failed(rac_vad_analytics_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_vad_analytics_track_model_load_failed(rac_vad_analytics_handle_t handle, const char* model_id, rac_result_t error_code, const char* error_message); @@ -212,7 +212,7 @@ RAC_API rac_result_t rac_vad_analytics_track_model_load_failed(rac_vad_analytics * @param model_id The model identifier * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_analytics_track_model_unloaded(rac_vad_analytics_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_vad_analytics_track_model_unloaded(rac_vad_analytics_handle_t handle, const char* model_id); // ============================================================================= @@ -226,7 +226,7 @@ RAC_API rac_result_t rac_vad_analytics_track_model_unloaded(rac_vad_analytics_ha * @param out_metrics Output: Metrics structure * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_analytics_get_metrics(rac_vad_analytics_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_vad_analytics_get_metrics(rac_vad_analytics_handle_t handle, rac_vad_metrics_t* out_metrics); #ifdef __cplusplus diff --git a/sdk/runanywhere-commons/include/rac/features/vad/rac_vad_component.h b/sdk/runanywhere-commons/include/rac/features/vad/rac_vad_component.h index f4e2bcbe3..a396b7ba4 100644 --- a/sdk/runanywhere-commons/include/rac/features/vad/rac_vad_component.h +++ b/sdk/runanywhere-commons/include/rac/features/vad/rac_vad_component.h @@ -32,7 +32,7 @@ extern "C" { * @param out_handle Output: Handle to the component * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_component_create(rac_handle_t* out_handle); +RAC_API RAC_NODISCARD rac_result_t rac_vad_component_create(rac_handle_t* out_handle); /** * @brief Configure the VAD component @@ -41,7 +41,7 @@ RAC_API rac_result_t rac_vad_component_create(rac_handle_t* out_handle); * @param config Configuration * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_component_configure(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_vad_component_configure(rac_handle_t handle, const rac_vad_config_t* config); /** @@ -58,7 +58,7 @@ RAC_API rac_bool_t rac_vad_component_is_initialized(rac_handle_t handle); * @param handle Component handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_component_initialize(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_vad_component_initialize(rac_handle_t handle); /** * @brief Cleanup and reset the component @@ -66,7 +66,7 @@ RAC_API rac_result_t rac_vad_component_initialize(rac_handle_t handle); * @param handle Component handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_component_cleanup(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_vad_component_cleanup(rac_handle_t handle); /** * @brief Set speech activity callback @@ -76,7 +76,7 @@ RAC_API rac_result_t rac_vad_component_cleanup(rac_handle_t handle); * @param user_data User context passed to callback * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_component_set_activity_callback(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_vad_component_set_activity_callback(rac_handle_t handle, rac_vad_activity_callback_fn callback, void* user_data); @@ -88,7 +88,7 @@ RAC_API rac_result_t rac_vad_component_set_activity_callback(rac_handle_t handle * @param user_data User context passed to callback * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_component_set_audio_callback(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_vad_component_set_audio_callback(rac_handle_t handle, rac_vad_audio_callback_fn callback, void* user_data); @@ -98,7 +98,7 @@ RAC_API rac_result_t rac_vad_component_set_audio_callback(rac_handle_t handle, * @param handle Component handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_component_start(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_vad_component_start(rac_handle_t handle); /** * @brief Stop VAD processing @@ -106,7 +106,7 @@ RAC_API rac_result_t rac_vad_component_start(rac_handle_t handle); * @param handle Component handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_component_stop(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_vad_component_stop(rac_handle_t handle); /** * @brief Reset VAD state @@ -114,7 +114,7 @@ RAC_API rac_result_t rac_vad_component_stop(rac_handle_t handle); * @param handle Component handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_component_reset(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_vad_component_reset(rac_handle_t handle); /** * @brief Process audio samples @@ -125,7 +125,7 @@ RAC_API rac_result_t rac_vad_component_reset(rac_handle_t handle); * @param out_is_speech Output: Whether speech is detected * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_component_process(rac_handle_t handle, const float* samples, +RAC_API RAC_NODISCARD rac_result_t rac_vad_component_process(rac_handle_t handle, const float* samples, size_t num_samples, rac_bool_t* out_is_speech); /** @@ -151,7 +151,7 @@ RAC_API float rac_vad_component_get_energy_threshold(rac_handle_t handle); * @param threshold New threshold (0.0 to 1.0) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_component_set_energy_threshold(rac_handle_t handle, float threshold); +RAC_API RAC_NODISCARD rac_result_t rac_vad_component_set_energy_threshold(rac_handle_t handle, float threshold); /** * @brief Load a VAD model via the service registry. @@ -166,7 +166,7 @@ RAC_API rac_result_t rac_vad_component_set_energy_threshold(rac_handle_t handle, * @param model_name Human-readable model name * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_component_load_model(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_vad_component_load_model(rac_handle_t handle, const char* model_path, const char* model_id, const char* model_name); @@ -187,7 +187,7 @@ RAC_API rac_bool_t rac_vad_component_is_loaded(rac_handle_t handle); * @param handle Component handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_component_unload(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_vad_component_unload(rac_handle_t handle); /** * @brief Get lifecycle state @@ -204,7 +204,7 @@ RAC_API rac_lifecycle_state_t rac_vad_component_get_state(rac_handle_t handle); * @param out_metrics Output: Lifecycle metrics * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_component_get_metrics(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_vad_component_get_metrics(rac_handle_t handle, rac_lifecycle_metrics_t* out_metrics); /** diff --git a/sdk/runanywhere-commons/include/rac/features/vad/rac_vad_energy.h b/sdk/runanywhere-commons/include/rac/features/vad/rac_vad_energy.h index 8f146da5f..15cb28b61 100644 --- a/sdk/runanywhere-commons/include/rac/features/vad/rac_vad_energy.h +++ b/sdk/runanywhere-commons/include/rac/features/vad/rac_vad_energy.h @@ -153,7 +153,7 @@ typedef void (*rac_audio_buffer_callback_fn)(const void* audio_data, size_t audi * @param out_handle Output: Handle to the created service * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_energy_vad_create(const rac_energy_vad_config_t* config, +RAC_API RAC_NODISCARD rac_result_t rac_energy_vad_create(const rac_energy_vad_config_t* config, rac_energy_vad_handle_t* out_handle); /** @@ -172,7 +172,7 @@ RAC_API void rac_energy_vad_destroy(rac_energy_vad_handle_t handle); * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_energy_vad_initialize(rac_energy_vad_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_energy_vad_initialize(rac_energy_vad_handle_t handle); /** * @brief Start voice activity detection. @@ -182,7 +182,7 @@ RAC_API rac_result_t rac_energy_vad_initialize(rac_energy_vad_handle_t handle); * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_energy_vad_start(rac_energy_vad_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_energy_vad_start(rac_energy_vad_handle_t handle); /** * @brief Stop voice activity detection. @@ -192,7 +192,7 @@ RAC_API rac_result_t rac_energy_vad_start(rac_energy_vad_handle_t handle); * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_energy_vad_stop(rac_energy_vad_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_energy_vad_stop(rac_energy_vad_handle_t handle); /** * @brief Reset the VAD state. @@ -202,7 +202,7 @@ RAC_API rac_result_t rac_energy_vad_stop(rac_energy_vad_handle_t handle); * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_energy_vad_reset(rac_energy_vad_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_energy_vad_reset(rac_energy_vad_handle_t handle); // ============================================================================= // PROCESSING API @@ -219,7 +219,7 @@ RAC_API rac_result_t rac_energy_vad_reset(rac_energy_vad_handle_t handle); * @param out_has_voice Output: Whether voice was detected * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_energy_vad_process_audio(rac_energy_vad_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_energy_vad_process_audio(rac_energy_vad_handle_t handle, const float* audio_data, size_t sample_count, rac_bool_t* out_has_voice); @@ -246,7 +246,7 @@ RAC_API float rac_energy_vad_calculate_rms(const float* __restrict audio_data,si * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_energy_vad_pause(rac_energy_vad_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_energy_vad_pause(rac_energy_vad_handle_t handle); /** * @brief Resume VAD processing. @@ -256,7 +256,7 @@ RAC_API rac_result_t rac_energy_vad_pause(rac_energy_vad_handle_t handle); * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_energy_vad_resume(rac_energy_vad_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_energy_vad_resume(rac_energy_vad_handle_t handle); // ============================================================================= // CALIBRATION API @@ -271,7 +271,7 @@ RAC_API rac_result_t rac_energy_vad_resume(rac_energy_vad_handle_t handle); * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_energy_vad_start_calibration(rac_energy_vad_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_energy_vad_start_calibration(rac_energy_vad_handle_t handle); /** * @brief Check if calibration is in progress. @@ -280,7 +280,7 @@ RAC_API rac_result_t rac_energy_vad_start_calibration(rac_energy_vad_handle_t ha * @param out_is_calibrating Output: RAC_TRUE if calibrating * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_energy_vad_is_calibrating(rac_energy_vad_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_energy_vad_is_calibrating(rac_energy_vad_handle_t handle, rac_bool_t* out_is_calibrating); /** @@ -292,7 +292,7 @@ RAC_API rac_result_t rac_energy_vad_is_calibrating(rac_energy_vad_handle_t handl * @param multiplier Calibration multiplier (clamped to 1.5-4.0) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_energy_vad_set_calibration_multiplier(rac_energy_vad_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_energy_vad_set_calibration_multiplier(rac_energy_vad_handle_t handle, float multiplier); // ============================================================================= @@ -308,7 +308,7 @@ RAC_API rac_result_t rac_energy_vad_set_calibration_multiplier(rac_energy_vad_ha * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_energy_vad_notify_tts_start(rac_energy_vad_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_energy_vad_notify_tts_start(rac_energy_vad_handle_t handle); /** * @brief Notify VAD that TTS has finished playing. @@ -319,7 +319,7 @@ RAC_API rac_result_t rac_energy_vad_notify_tts_start(rac_energy_vad_handle_t han * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_energy_vad_notify_tts_finish(rac_energy_vad_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_energy_vad_notify_tts_finish(rac_energy_vad_handle_t handle); /** * @brief Set TTS threshold multiplier. @@ -330,7 +330,7 @@ RAC_API rac_result_t rac_energy_vad_notify_tts_finish(rac_energy_vad_handle_t ha * @param multiplier TTS threshold multiplier (clamped to 2.0-5.0) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_energy_vad_set_tts_multiplier(rac_energy_vad_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_energy_vad_set_tts_multiplier(rac_energy_vad_handle_t handle, float multiplier); // ============================================================================= @@ -346,7 +346,7 @@ RAC_API rac_result_t rac_energy_vad_set_tts_multiplier(rac_energy_vad_handle_t h * @param out_is_active Output: RAC_TRUE if speech is active * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_energy_vad_is_speech_active(rac_energy_vad_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_energy_vad_is_speech_active(rac_energy_vad_handle_t handle, rac_bool_t* out_is_active); /** @@ -356,7 +356,7 @@ RAC_API rac_result_t rac_energy_vad_is_speech_active(rac_energy_vad_handle_t han * @param out_threshold Output: Current threshold value * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_energy_vad_get_threshold(rac_energy_vad_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_energy_vad_get_threshold(rac_energy_vad_handle_t handle, float* out_threshold); /** @@ -366,7 +366,7 @@ RAC_API rac_result_t rac_energy_vad_get_threshold(rac_energy_vad_handle_t handle * @param threshold New threshold value * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_energy_vad_set_threshold(rac_energy_vad_handle_t handle, float threshold); +RAC_API RAC_NODISCARD rac_result_t rac_energy_vad_set_threshold(rac_energy_vad_handle_t handle, float threshold); /** * @brief Get VAD statistics for debugging. @@ -377,7 +377,7 @@ RAC_API rac_result_t rac_energy_vad_set_threshold(rac_energy_vad_handle_t handle * @param out_stats Output: VAD statistics * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_energy_vad_get_statistics(rac_energy_vad_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_energy_vad_get_statistics(rac_energy_vad_handle_t handle, rac_energy_vad_stats_t* out_stats); /** @@ -389,7 +389,7 @@ RAC_API rac_result_t rac_energy_vad_get_statistics(rac_energy_vad_handle_t handl * @param out_sample_rate Output: Sample rate * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_energy_vad_get_sample_rate(rac_energy_vad_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_energy_vad_get_sample_rate(rac_energy_vad_handle_t handle, int32_t* out_sample_rate); /** @@ -401,7 +401,7 @@ RAC_API rac_result_t rac_energy_vad_get_sample_rate(rac_energy_vad_handle_t hand * @param out_frame_length Output: Frame length in samples * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_energy_vad_get_frame_length_samples(rac_energy_vad_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_energy_vad_get_frame_length_samples(rac_energy_vad_handle_t handle, int32_t* out_frame_length); // ============================================================================= @@ -418,7 +418,7 @@ RAC_API rac_result_t rac_energy_vad_get_frame_length_samples(rac_energy_vad_hand * @param user_data User-provided context * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_energy_vad_set_speech_callback(rac_energy_vad_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_energy_vad_set_speech_callback(rac_energy_vad_handle_t handle, rac_speech_activity_callback_fn callback, void* user_data); @@ -432,7 +432,7 @@ RAC_API rac_result_t rac_energy_vad_set_speech_callback(rac_energy_vad_handle_t * @param user_data User-provided context * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_energy_vad_set_audio_callback(rac_energy_vad_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_energy_vad_set_audio_callback(rac_energy_vad_handle_t handle, rac_audio_buffer_callback_fn callback, void* user_data); diff --git a/sdk/runanywhere-commons/include/rac/features/vad/rac_vad_events.h b/sdk/runanywhere-commons/include/rac/features/vad/rac_vad_events.h index 9c78e9370..b0754011e 100644 --- a/sdk/runanywhere-commons/include/rac/features/vad/rac_vad_events.h +++ b/sdk/runanywhere-commons/include/rac/features/vad/rac_vad_events.h @@ -39,33 +39,33 @@ typedef enum rac_vad_event_type { // EVENT PUBLISHING FUNCTIONS // ============================================================================= -RAC_API rac_result_t rac_vad_event_initialized(rac_inference_framework_t framework); +RAC_API RAC_NODISCARD rac_result_t rac_vad_event_initialized(rac_inference_framework_t framework); -RAC_API rac_result_t rac_vad_event_initialization_failed(rac_result_t error_code, +RAC_API RAC_NODISCARD rac_result_t rac_vad_event_initialization_failed(rac_result_t error_code, const char* error_message, rac_inference_framework_t framework); -RAC_API rac_result_t rac_vad_event_cleaned_up(void); -RAC_API rac_result_t rac_vad_event_started(void); -RAC_API rac_result_t rac_vad_event_stopped(void); -RAC_API rac_result_t rac_vad_event_speech_started(void); -RAC_API rac_result_t rac_vad_event_speech_ended(double duration_ms); -RAC_API rac_result_t rac_vad_event_paused(void); -RAC_API rac_result_t rac_vad_event_resumed(void); +RAC_API RAC_NODISCARD rac_result_t rac_vad_event_cleaned_up(void); +RAC_API RAC_NODISCARD rac_result_t rac_vad_event_started(void); +RAC_API RAC_NODISCARD rac_result_t rac_vad_event_stopped(void); +RAC_API RAC_NODISCARD rac_result_t rac_vad_event_speech_started(void); +RAC_API RAC_NODISCARD rac_result_t rac_vad_event_speech_ended(double duration_ms); +RAC_API RAC_NODISCARD rac_result_t rac_vad_event_paused(void); +RAC_API RAC_NODISCARD rac_result_t rac_vad_event_resumed(void); -RAC_API rac_result_t rac_vad_event_model_load_started(const char* model_id, +RAC_API RAC_NODISCARD rac_result_t rac_vad_event_model_load_started(const char* model_id, int64_t model_size_bytes, rac_inference_framework_t framework); -RAC_API rac_result_t rac_vad_event_model_load_completed(const char* model_id, double duration_ms, +RAC_API RAC_NODISCARD rac_result_t rac_vad_event_model_load_completed(const char* model_id, double duration_ms, int64_t model_size_bytes, rac_inference_framework_t framework); -RAC_API rac_result_t rac_vad_event_model_load_failed(const char* model_id, rac_result_t error_code, +RAC_API RAC_NODISCARD rac_result_t rac_vad_event_model_load_failed(const char* model_id, rac_result_t error_code, const char* error_message, rac_inference_framework_t framework); -RAC_API rac_result_t rac_vad_event_model_unloaded(const char* model_id); +RAC_API RAC_NODISCARD rac_result_t rac_vad_event_model_unloaded(const char* model_id); RAC_API const char* rac_vad_event_type_string(rac_vad_event_type_t event_type); diff --git a/sdk/runanywhere-commons/include/rac/features/vad/rac_vad_service.h b/sdk/runanywhere-commons/include/rac/features/vad/rac_vad_service.h index a300d8fbb..08f02483a 100644 --- a/sdk/runanywhere-commons/include/rac/features/vad/rac_vad_service.h +++ b/sdk/runanywhere-commons/include/rac/features/vad/rac_vad_service.h @@ -77,7 +77,7 @@ typedef struct rac_vad_service { * @param out_handle Output: Handle to the created service * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_create(rac_handle_t* out_handle); +RAC_API RAC_NODISCARD rac_result_t rac_vad_create(rac_handle_t* out_handle); /** * @brief Initialize the VAD service @@ -87,7 +87,7 @@ RAC_API rac_result_t rac_vad_create(rac_handle_t* out_handle); * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_initialize(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_vad_initialize(rac_handle_t handle); /** * @brief Set speech activity callback @@ -99,7 +99,7 @@ RAC_API rac_result_t rac_vad_initialize(rac_handle_t handle); * @param user_data User context passed to callback * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_set_activity_callback(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_vad_set_activity_callback(rac_handle_t handle, rac_vad_activity_callback_fn callback, void* user_data); @@ -113,7 +113,7 @@ RAC_API rac_result_t rac_vad_set_activity_callback(rac_handle_t handle, * @param user_data User context passed to callback * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_set_audio_callback(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_vad_set_audio_callback(rac_handle_t handle, rac_vad_audio_callback_fn callback, void* user_data); @@ -125,7 +125,7 @@ RAC_API rac_result_t rac_vad_set_audio_callback(rac_handle_t handle, * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_start(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_vad_start(rac_handle_t handle); /** * @brief Stop VAD processing @@ -135,7 +135,7 @@ RAC_API rac_result_t rac_vad_start(rac_handle_t handle); * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_stop(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_vad_stop(rac_handle_t handle); /** * @brief Reset VAD state @@ -145,7 +145,7 @@ RAC_API rac_result_t rac_vad_stop(rac_handle_t handle); * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_reset(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_vad_reset(rac_handle_t handle); /** * @brief Pause VAD processing @@ -155,7 +155,7 @@ RAC_API rac_result_t rac_vad_reset(rac_handle_t handle); * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_pause(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_vad_pause(rac_handle_t handle); /** * @brief Resume VAD processing @@ -165,7 +165,7 @@ RAC_API rac_result_t rac_vad_pause(rac_handle_t handle); * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_resume(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_vad_resume(rac_handle_t handle); /** * @brief Process audio samples @@ -178,7 +178,7 @@ RAC_API rac_result_t rac_vad_resume(rac_handle_t handle); * @param out_is_speech Output: Whether speech is detected * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_process_samples(rac_handle_t handle, const float* samples, +RAC_API RAC_NODISCARD rac_result_t rac_vad_process_samples(rac_handle_t handle, const float* samples, size_t num_samples, rac_bool_t* out_is_speech); /** @@ -190,7 +190,7 @@ RAC_API rac_result_t rac_vad_process_samples(rac_handle_t handle, const float* s * @param threshold New threshold (0.0 to 1.0) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_set_energy_threshold(rac_handle_t handle, float threshold); +RAC_API RAC_NODISCARD rac_result_t rac_vad_set_energy_threshold(rac_handle_t handle, float threshold); /** * @brief Get service information @@ -199,7 +199,7 @@ RAC_API rac_result_t rac_vad_set_energy_threshold(rac_handle_t handle, float thr * @param out_info Output: Service information * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vad_get_info(rac_handle_t handle, rac_vad_info_t* out_info); +RAC_API RAC_NODISCARD rac_result_t rac_vad_get_info(rac_handle_t handle, rac_vad_info_t* out_info); /** * @brief Destroy a VAD service instance diff --git a/sdk/runanywhere-commons/include/rac/features/vlm/rac_vlm_component.h b/sdk/runanywhere-commons/include/rac/features/vlm/rac_vlm_component.h index 279442410..7640ca870 100644 --- a/sdk/runanywhere-commons/include/rac/features/vlm/rac_vlm_component.h +++ b/sdk/runanywhere-commons/include/rac/features/vlm/rac_vlm_component.h @@ -27,7 +27,7 @@ extern "C" { * @param out_handle Output: Handle to the component * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vlm_component_create(rac_handle_t* out_handle); +RAC_API RAC_NODISCARD rac_result_t rac_vlm_component_create(rac_handle_t* out_handle); /** * @brief Configure the VLM component @@ -36,7 +36,7 @@ RAC_API rac_result_t rac_vlm_component_create(rac_handle_t* out_handle); * @param config Configuration * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vlm_component_configure(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_vlm_component_configure(rac_handle_t handle, const rac_vlm_config_t* config); /** @@ -65,7 +65,7 @@ RAC_API const char* rac_vlm_component_get_model_id(rac_handle_t handle); * @param model_name Human-readable model name (optional: if NULL, defaults to model_id) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vlm_component_load_model(rac_handle_t handle, const char* model_path, +RAC_API RAC_NODISCARD rac_result_t rac_vlm_component_load_model(rac_handle_t handle, const char* model_path, const char* mmproj_path, const char* model_id, const char* model_name); @@ -80,7 +80,7 @@ RAC_API rac_result_t rac_vlm_component_load_model(rac_handle_t handle, const cha * @param model_id Model identifier (must be registered in the global registry) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vlm_component_load_model_by_id(rac_handle_t handle, const char* model_id); +RAC_API RAC_NODISCARD rac_result_t rac_vlm_component_load_model_by_id(rac_handle_t handle, const char* model_id); /** * @brief Resolve VLM model files within a directory @@ -104,7 +104,7 @@ RAC_API rac_result_t rac_vlm_component_load_model_by_id(rac_handle_t handle, con * @param mmproj_path_size Size of the mmproj path output buffer * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vlm_resolve_model_files(const char* model_dir, char* out_model_path, +RAC_API RAC_NODISCARD rac_result_t rac_vlm_resolve_model_files(const char* model_dir, char* out_model_path, size_t model_path_size, char* out_mmproj_path, size_t mmproj_path_size); @@ -114,7 +114,7 @@ RAC_API rac_result_t rac_vlm_resolve_model_files(const char* model_dir, char* ou * @param handle Component handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vlm_component_unload(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_vlm_component_unload(rac_handle_t handle); /** * @brief Cleanup and reset the component @@ -122,7 +122,7 @@ RAC_API rac_result_t rac_vlm_component_unload(rac_handle_t handle); * @param handle Component handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vlm_component_cleanup(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_vlm_component_cleanup(rac_handle_t handle); /** * @brief Cancel ongoing generation @@ -132,7 +132,7 @@ RAC_API rac_result_t rac_vlm_component_cleanup(rac_handle_t handle); * @param handle Component handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vlm_component_cancel(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_vlm_component_cancel(rac_handle_t handle); /** * @brief Process an image with text prompt (non-streaming) @@ -144,7 +144,7 @@ RAC_API rac_result_t rac_vlm_component_cancel(rac_handle_t handle); * @param out_result Output: Generation result * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vlm_component_process(rac_handle_t handle, const rac_vlm_image_t* image, +RAC_API RAC_NODISCARD rac_result_t rac_vlm_component_process(rac_handle_t handle, const rac_vlm_image_t* image, const char* prompt, const rac_vlm_options_t* options, rac_vlm_result_t* out_result); @@ -169,7 +169,7 @@ RAC_API rac_bool_t rac_vlm_component_supports_streaming(rac_handle_t handle); * @param user_data User context passed to callbacks * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vlm_component_process_stream( +RAC_API RAC_NODISCARD rac_result_t rac_vlm_component_process_stream( rac_handle_t handle, const rac_vlm_image_t* image, const char* prompt, const rac_vlm_options_t* options, rac_vlm_component_token_callback_fn token_callback, rac_vlm_component_complete_callback_fn complete_callback, @@ -190,7 +190,7 @@ RAC_API rac_lifecycle_state_t rac_vlm_component_get_state(rac_handle_t handle); * @param out_metrics Output: Lifecycle metrics * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vlm_component_get_metrics(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_vlm_component_get_metrics(rac_handle_t handle, rac_lifecycle_metrics_t* out_metrics); /** diff --git a/sdk/runanywhere-commons/include/rac/features/vlm/rac_vlm_service.h b/sdk/runanywhere-commons/include/rac/features/vlm/rac_vlm_service.h index 5d47adeb3..b968df063 100644 --- a/sdk/runanywhere-commons/include/rac/features/vlm/rac_vlm_service.h +++ b/sdk/runanywhere-commons/include/rac/features/vlm/rac_vlm_service.h @@ -118,7 +118,7 @@ typedef struct rac_vlm_service { * @param out_handle Output: Handle to the created service * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vlm_create(const char* model_id, rac_handle_t* out_handle); +RAC_API RAC_NODISCARD rac_result_t rac_vlm_create(const char* model_id, rac_handle_t* out_handle); /** * @brief Initialize a VLM service with model paths @@ -128,7 +128,7 @@ RAC_API rac_result_t rac_vlm_create(const char* model_id, rac_handle_t* out_hand * @param mmproj_path Path to vision projector (can be NULL for some backends) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vlm_initialize(rac_handle_t handle, const char* model_path, +RAC_API RAC_NODISCARD rac_result_t rac_vlm_initialize(rac_handle_t handle, const char* model_path, const char* mmproj_path); /** @@ -141,7 +141,7 @@ RAC_API rac_result_t rac_vlm_initialize(rac_handle_t handle, const char* model_p * @param out_result Output: Generation result (caller must free with rac_vlm_result_free) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vlm_process(rac_handle_t handle, const rac_vlm_image_t* image, +RAC_API RAC_NODISCARD rac_result_t rac_vlm_process(rac_handle_t handle, const rac_vlm_image_t* image, const char* prompt, const rac_vlm_options_t* options, rac_vlm_result_t* out_result); @@ -156,7 +156,7 @@ RAC_API rac_result_t rac_vlm_process(rac_handle_t handle, const rac_vlm_image_t* * @param user_data User context passed to callback * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vlm_process_stream(rac_handle_t handle, const rac_vlm_image_t* image, +RAC_API RAC_NODISCARD rac_result_t rac_vlm_process_stream(rac_handle_t handle, const rac_vlm_image_t* image, const char* prompt, const rac_vlm_options_t* options, rac_vlm_stream_callback_fn callback, void* user_data); @@ -167,7 +167,7 @@ RAC_API rac_result_t rac_vlm_process_stream(rac_handle_t handle, const rac_vlm_i * @param out_info Output: Service information * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vlm_get_info(rac_handle_t handle, rac_vlm_info_t* out_info); +RAC_API RAC_NODISCARD rac_result_t rac_vlm_get_info(rac_handle_t handle, rac_vlm_info_t* out_info); /** * @brief Cancel ongoing generation @@ -175,7 +175,7 @@ RAC_API rac_result_t rac_vlm_get_info(rac_handle_t handle, rac_vlm_info_t* out_i * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vlm_cancel(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_vlm_cancel(rac_handle_t handle); /** * @brief Cleanup and release model resources @@ -183,7 +183,7 @@ RAC_API rac_result_t rac_vlm_cancel(rac_handle_t handle); * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_vlm_cleanup(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_vlm_cleanup(rac_handle_t handle); /** * @brief Destroy a VLM service instance diff --git a/sdk/runanywhere-commons/include/rac/features/voice_agent/rac_voice_agent.h b/sdk/runanywhere-commons/include/rac/features/voice_agent/rac_voice_agent.h index 454301e33..952e7fffa 100644 --- a/sdk/runanywhere-commons/include/rac/features/voice_agent/rac_voice_agent.h +++ b/sdk/runanywhere-commons/include/rac/features/voice_agent/rac_voice_agent.h @@ -381,7 +381,7 @@ typedef struct rac_voice_agent* rac_voice_agent_handle_t; * @param out_handle Output: Handle to the created voice agent * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_voice_agent_create_standalone(rac_voice_agent_handle_t* out_handle); +RAC_API RAC_NODISCARD rac_result_t rac_voice_agent_create_standalone(rac_voice_agent_handle_t* out_handle); /** * @brief Create a voice agent instance with external component handles. @@ -396,7 +396,7 @@ RAC_API rac_result_t rac_voice_agent_create_standalone(rac_voice_agent_handle_t* * @param out_handle Output: Handle to the created voice agent * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_voice_agent_create(rac_handle_t llm_component_handle, +RAC_API RAC_NODISCARD rac_result_t rac_voice_agent_create(rac_handle_t llm_component_handle, rac_handle_t stt_component_handle, rac_handle_t tts_component_handle, rac_handle_t vad_component_handle, @@ -425,7 +425,7 @@ RAC_API void rac_voice_agent_destroy(rac_voice_agent_handle_t handle); * @param model_name Human-readable model name (e.g., "Whisper Base") * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_voice_agent_load_stt_model(rac_voice_agent_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_voice_agent_load_stt_model(rac_voice_agent_handle_t handle, const char* model_path, const char* model_id, const char* model_name); @@ -438,7 +438,7 @@ RAC_API rac_result_t rac_voice_agent_load_stt_model(rac_voice_agent_handle_t han * @param model_name Human-readable model name (e.g., "Llama 3.2 1B Instruct") * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_voice_agent_load_llm_model(rac_voice_agent_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_voice_agent_load_llm_model(rac_voice_agent_handle_t handle, const char* model_path, const char* model_id, const char* model_name); @@ -451,7 +451,7 @@ RAC_API rac_result_t rac_voice_agent_load_llm_model(rac_voice_agent_handle_t han * @param voice_name Human-readable voice name (e.g., "Piper TTS (British English)") * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_voice_agent_load_tts_voice(rac_voice_agent_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_voice_agent_load_tts_voice(rac_voice_agent_handle_t handle, const char* voice_path, const char* voice_id, const char* voice_name); @@ -462,7 +462,7 @@ RAC_API rac_result_t rac_voice_agent_load_tts_voice(rac_voice_agent_handle_t han * @param out_loaded Output: RAC_TRUE if loaded * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_voice_agent_is_stt_loaded(rac_voice_agent_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_voice_agent_is_stt_loaded(rac_voice_agent_handle_t handle, rac_bool_t* out_loaded); /** @@ -472,7 +472,7 @@ RAC_API rac_result_t rac_voice_agent_is_stt_loaded(rac_voice_agent_handle_t hand * @param out_loaded Output: RAC_TRUE if loaded * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_voice_agent_is_llm_loaded(rac_voice_agent_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_voice_agent_is_llm_loaded(rac_voice_agent_handle_t handle, rac_bool_t* out_loaded); /** @@ -482,7 +482,7 @@ RAC_API rac_result_t rac_voice_agent_is_llm_loaded(rac_voice_agent_handle_t hand * @param out_loaded Output: RAC_TRUE if loaded * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_voice_agent_is_tts_loaded(rac_voice_agent_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_voice_agent_is_tts_loaded(rac_voice_agent_handle_t handle, rac_bool_t* out_loaded); /** @@ -519,7 +519,7 @@ RAC_API const char* rac_voice_agent_get_tts_voice_id(rac_voice_agent_handle_t ha * @param config Configuration (can be NULL for defaults) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_voice_agent_initialize(rac_voice_agent_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_voice_agent_initialize(rac_voice_agent_handle_t handle, const rac_voice_agent_config_t* config); /** @@ -531,7 +531,7 @@ RAC_API rac_result_t rac_voice_agent_initialize(rac_voice_agent_handle_t handle, * @param handle Voice agent handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_voice_agent_initialize_with_loaded_models(rac_voice_agent_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_voice_agent_initialize_with_loaded_models(rac_voice_agent_handle_t handle); /** * @brief Cleanup voice agent resources. @@ -541,7 +541,7 @@ RAC_API rac_result_t rac_voice_agent_initialize_with_loaded_models(rac_voice_age * @param handle Voice agent handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_voice_agent_cleanup(rac_voice_agent_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_voice_agent_cleanup(rac_voice_agent_handle_t handle); /** * @brief Check if voice agent is ready. @@ -552,7 +552,7 @@ RAC_API rac_result_t rac_voice_agent_cleanup(rac_voice_agent_handle_t handle); * @param out_is_ready Output: RAC_TRUE if ready * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_voice_agent_is_ready(rac_voice_agent_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_voice_agent_is_ready(rac_voice_agent_handle_t handle, rac_bool_t* out_is_ready); // ============================================================================= @@ -571,7 +571,7 @@ RAC_API rac_result_t rac_voice_agent_is_ready(rac_voice_agent_handle_t handle, * rac_voice_agent_result_free) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_voice_agent_process_voice_turn(rac_voice_agent_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_voice_agent_process_voice_turn(rac_voice_agent_handle_t handle, const void* audio_data, size_t audio_size, rac_voice_agent_result_t* out_result); @@ -588,7 +588,7 @@ RAC_API rac_result_t rac_voice_agent_process_voice_turn(rac_voice_agent_handle_t * @param user_data User context passed to callback * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_voice_agent_process_stream(rac_voice_agent_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_voice_agent_process_stream(rac_voice_agent_handle_t handle, const void* audio_data, size_t audio_size, rac_voice_agent_event_callback_fn callback, void* user_data); @@ -608,7 +608,7 @@ RAC_API rac_result_t rac_voice_agent_process_stream(rac_voice_agent_handle_t han * @param out_transcription Output: Transcribed text (owned, must be freed with rac_free) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_voice_agent_transcribe(rac_voice_agent_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_voice_agent_transcribe(rac_voice_agent_handle_t handle, const void* audio_data, size_t audio_size, char** out_transcription); @@ -622,7 +622,7 @@ RAC_API rac_result_t rac_voice_agent_transcribe(rac_voice_agent_handle_t handle, * @param out_response Output: Generated response (owned, must be freed with rac_free) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_voice_agent_generate_response(rac_voice_agent_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_voice_agent_generate_response(rac_voice_agent_handle_t handle, const char* prompt, char** out_response); /** @@ -636,7 +636,7 @@ RAC_API rac_result_t rac_voice_agent_generate_response(rac_voice_agent_handle_t * @param out_audio_size Output: Size of audio data in bytes * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_voice_agent_synthesize_speech(rac_voice_agent_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_voice_agent_synthesize_speech(rac_voice_agent_handle_t handle, const char* text, void** out_audio, size_t* out_audio_size); @@ -651,7 +651,7 @@ RAC_API rac_result_t rac_voice_agent_synthesize_speech(rac_voice_agent_handle_t * @param out_speech_detected Output: RAC_TRUE if speech detected * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_voice_agent_detect_speech(rac_voice_agent_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_voice_agent_detect_speech(rac_voice_agent_handle_t handle, const float* samples, size_t sample_count, rac_bool_t* out_speech_detected); diff --git a/sdk/runanywhere-commons/include/rac/features/wakeword/rac_wakeword_service.h b/sdk/runanywhere-commons/include/rac/features/wakeword/rac_wakeword_service.h index 929b59825..6662716ff 100644 --- a/sdk/runanywhere-commons/include/rac/features/wakeword/rac_wakeword_service.h +++ b/sdk/runanywhere-commons/include/rac/features/wakeword/rac_wakeword_service.h @@ -39,7 +39,7 @@ extern "C" { * @param[out] out_handle Output: Handle to the created service * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_wakeword_create(rac_handle_t* out_handle); +RAC_API RAC_NODISCARD rac_result_t rac_wakeword_create(rac_handle_t* out_handle); /** * @brief Initialize the wake word service @@ -51,7 +51,7 @@ RAC_API rac_result_t rac_wakeword_create(rac_handle_t* out_handle); * @param config Configuration (NULL for defaults) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_wakeword_initialize(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_wakeword_initialize(rac_handle_t handle, const rac_wakeword_config_t* config); /** @@ -79,7 +79,7 @@ RAC_API void rac_wakeword_destroy(rac_handle_t handle); * @param wake_word Human-readable wake word phrase (e.g., "Hey Jarvis") * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_wakeword_load_model(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_wakeword_load_model(rac_handle_t handle, const char* model_path, const char* model_id, const char* wake_word); @@ -94,7 +94,7 @@ RAC_API rac_result_t rac_wakeword_load_model(rac_handle_t handle, * @param vad_model_path Path to Silero VAD ONNX model * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_wakeword_load_vad(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_wakeword_load_vad(rac_handle_t handle, const char* vad_model_path); /** @@ -104,7 +104,7 @@ RAC_API rac_result_t rac_wakeword_load_vad(rac_handle_t handle, * @param model_id Model identifier to unload * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_wakeword_unload_model(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_wakeword_unload_model(rac_handle_t handle, const char* model_id); /** @@ -115,7 +115,7 @@ RAC_API rac_result_t rac_wakeword_unload_model(rac_handle_t handle, * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_wakeword_unload_all(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_wakeword_unload_all(rac_handle_t handle); /** * @brief Get list of loaded models @@ -125,7 +125,7 @@ RAC_API rac_result_t rac_wakeword_unload_all(rac_handle_t handle); * @param[out] out_count Output: Number of models * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_wakeword_get_models(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_wakeword_get_models(rac_handle_t handle, const rac_wakeword_model_info_t** out_models, int32_t* out_count); @@ -144,7 +144,7 @@ RAC_API rac_result_t rac_wakeword_get_models(rac_handle_t handle, * @param user_data User context passed to callback * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_wakeword_set_callback(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_wakeword_set_callback(rac_handle_t handle, rac_wakeword_callback_fn callback, void* user_data); @@ -156,7 +156,7 @@ RAC_API rac_result_t rac_wakeword_set_callback(rac_handle_t handle, * @param user_data User context passed to callback * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_wakeword_set_vad_callback(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_wakeword_set_vad_callback(rac_handle_t handle, rac_wakeword_vad_callback_fn callback, void* user_data); @@ -173,7 +173,7 @@ RAC_API rac_result_t rac_wakeword_set_vad_callback(rac_handle_t handle, * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_wakeword_start(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_wakeword_start(rac_handle_t handle); /** * @brief Stop listening for wake words @@ -184,7 +184,7 @@ RAC_API rac_result_t rac_wakeword_start(rac_handle_t handle); * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_wakeword_stop(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_wakeword_stop(rac_handle_t handle); /** * @brief Pause detection temporarily @@ -195,7 +195,7 @@ RAC_API rac_result_t rac_wakeword_stop(rac_handle_t handle); * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_wakeword_pause(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_wakeword_pause(rac_handle_t handle); /** * @brief Resume detection after pause @@ -203,7 +203,7 @@ RAC_API rac_result_t rac_wakeword_pause(rac_handle_t handle); * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_wakeword_resume(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_wakeword_resume(rac_handle_t handle); /** * @brief Reset detector state @@ -214,7 +214,7 @@ RAC_API rac_result_t rac_wakeword_resume(rac_handle_t handle); * @param handle Service handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_wakeword_reset(rac_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_wakeword_reset(rac_handle_t handle); // ============================================================================= // AUDIO PROCESSING @@ -232,7 +232,7 @@ RAC_API rac_result_t rac_wakeword_reset(rac_handle_t handle); * @param[out] out_result Optional: Frame processing result * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_wakeword_process(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_wakeword_process(rac_handle_t handle, const float* samples, size_t num_samples, rac_wakeword_frame_result_t* out_result); @@ -248,7 +248,7 @@ RAC_API rac_result_t rac_wakeword_process(rac_handle_t handle, * @param[out] out_result Optional: Frame processing result * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_wakeword_process_int16(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_wakeword_process_int16(rac_handle_t handle, const int16_t* samples, size_t num_samples, rac_wakeword_frame_result_t* out_result); @@ -267,7 +267,7 @@ RAC_API rac_result_t rac_wakeword_process_int16(rac_handle_t handle, * @param threshold New threshold (0.0 - 1.0) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_wakeword_set_threshold(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_wakeword_set_threshold(rac_handle_t handle, float threshold); /** @@ -278,7 +278,7 @@ RAC_API rac_result_t rac_wakeword_set_threshold(rac_handle_t handle, * @param threshold Model threshold (0.0 - 1.0) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_wakeword_set_model_threshold(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_wakeword_set_model_threshold(rac_handle_t handle, const char* model_id, float threshold); @@ -289,7 +289,7 @@ RAC_API rac_result_t rac_wakeword_set_model_threshold(rac_handle_t handle, * @param enabled Whether to enable VAD filtering * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_wakeword_set_vad_enabled(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_wakeword_set_vad_enabled(rac_handle_t handle, rac_bool_t enabled); // ============================================================================= @@ -303,7 +303,7 @@ RAC_API rac_result_t rac_wakeword_set_vad_enabled(rac_handle_t handle, * @param[out] out_info Output: Service information * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_wakeword_get_info(rac_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_wakeword_get_info(rac_handle_t handle, rac_wakeword_info_t* out_info); /** @@ -322,6 +322,128 @@ RAC_API rac_bool_t rac_wakeword_is_ready(rac_handle_t handle); */ RAC_API rac_bool_t rac_wakeword_is_listening(rac_handle_t handle); +// ============================================================================= +// PROVIDER REGISTRATION (backend hook) +// ============================================================================= +// +// The wake-word service layer lives in rac_commons and therefore cannot +// directly link against any concrete inference backend. Instead, any backend +// that wants to serve wake-word detection (today: the ONNX wake-word backend +// in src/backends/onnx/wakeword_onnx.cpp, and any future backends such as +// MetalRT) registers a vtable of function pointers via +// rac_wakeword_provider_set(). The service layer then dispatches every +// model-load / audio-process / reset / destroy call through that vtable. +// +// If no provider has registered, all service operations still succeed +// structurally (the service can be created, started, stopped) but +// rac_wakeword_process() becomes a no-op - detections never fire. Callers +// can detect this via rac_wakeword_has_provider() or by observing zero +// detections on known-positive audio. +// +// Lifetime: the callbacks struct passed to rac_wakeword_provider_set() must +// outlive every wake-word service instance. Typically this is a static +// global inside the provider backend's translation unit. The caller retains +// ownership; the service layer does not copy or free the struct. +// +// Thread-safety: rac_wakeword_provider_set() is expected to be called once +// at SDK startup, before any wake-word services exist. Calling it +// concurrently with live service instances is undefined. +// ============================================================================= + +/** + * @brief Callback vtable a wake-word inference backend must implement to + * serve as the wake-word provider for rac_wakeword_* services. + * + * All function pointers may be NULL if the provider does not support that + * operation; the service layer checks each before calling. The `user_data` + * pointer is stored and passed back into every callback so providers can + * associate each `backend_handle` with their own state. + */ +typedef struct rac_wakeword_provider_ops { + /** Create a backend-specific handle. Called once per wake-word service + * during rac_wakeword_initialize(). Must return a handle suitable for + * passing to every other vtable method (and to `destroy`). */ + rac_result_t (*create)(const rac_wakeword_config_t* config, + rac_handle_t* out_backend_handle, + void* user_data); + + /** Load a wake-word classification model (ONNX, etc.). */ + rac_result_t (*load_model)(rac_handle_t backend_handle, + const char* model_path, + const char* model_id, + const char* wake_word, + void* user_data); + + /** Unload a wake-word model previously loaded with load_model. */ + rac_result_t (*unload_model)(rac_handle_t backend_handle, + const char* model_id, + void* user_data); + + /** Load a VAD pre-filter model (Silero). Optional. */ + rac_result_t (*load_vad)(rac_handle_t backend_handle, + const char* vad_model_path, + void* user_data); + + /** Run one frame of audio through inference + optional VAD. + * out_detected_index is set to >= 0 if a wake word fires (index into + * the provider's loaded-models list) or -1 otherwise. */ + rac_result_t (*process)(rac_handle_t backend_handle, + const float* samples, size_t num_samples, + int32_t* out_detected_index, + float* out_confidence, + rac_bool_t* out_vad_speech, + float* out_vad_confidence, + void* user_data); + + /** Reset internal state (KV cache, sliding window, etc.). */ + rac_result_t (*reset)(rac_handle_t backend_handle, void* user_data); + + /** Adjust detection threshold at runtime. */ + rac_result_t (*set_threshold)(rac_handle_t backend_handle, + float threshold, + void* user_data); + + /** Tear down the backend handle. */ + void (*destroy)(rac_handle_t backend_handle, void* user_data); + + /** Opaque user-data passed through to every callback. */ + void* user_data; +} rac_wakeword_provider_ops_t; + +/** + * @brief Register a wake-word inference provider. + * + * Called once at SDK startup by the concrete backend + * (e.g. rac_backend_wakeword_onnx_register() wires up the ONNX provider). + * + * THREADING CONTRACT: + * - Callers MUST call this exactly once at SDK initialisation, BEFORE + * any wake-word service instance has been created via rac_wakeword_create. + * - `ops` must point to memory that outlives every wake-word service. In + * practice this means the provider struct must be a file-scope `static` + * constant in the backend module (see g_onnx_wakeword_provider_ops in + * wakeword_onnx.cpp for the canonical example). + * - Re-registering (i.e. calling this with different `ops` while services + * are alive) is UNDEFINED BEHAVIOUR. Services retain a raw pointer to + * the ops struct they were created under; swapping the provider while + * the old services are still dispatching is a use-after-free. + * - Passing NULL is only legal after all services have been destroyed. + * It's provided for teardown in tests and shutdown paths. + * + * @param ops Provider vtable. Must outlive all wake-word services. + * Pass NULL only during teardown. + * @return RAC_SUCCESS always. + */ +RAC_API RAC_NODISCARD rac_result_t rac_wakeword_provider_set(const rac_wakeword_provider_ops_t* ops); + +/** + * @brief Query whether a wake-word provider is currently registered. + * + * @return RAC_TRUE if a provider has been set via rac_wakeword_provider_set(), + * RAC_FALSE otherwise. Useful for test code and graceful degradation. + */ +RAC_API rac_bool_t rac_wakeword_has_provider(void); + #ifdef __cplusplus } #endif diff --git a/sdk/runanywhere-commons/include/rac/infrastructure/device/rac_device_manager.h b/sdk/runanywhere-commons/include/rac/infrastructure/device/rac_device_manager.h index 134a090ba..ccff40b65 100644 --- a/sdk/runanywhere-commons/include/rac/infrastructure/device/rac_device_manager.h +++ b/sdk/runanywhere-commons/include/rac/infrastructure/device/rac_device_manager.h @@ -122,7 +122,7 @@ typedef struct rac_device_callbacks { * @param callbacks Callback structure (copied internally) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_device_manager_set_callbacks(const rac_device_callbacks_t* callbacks); +RAC_API RAC_NODISCARD rac_result_t rac_device_manager_set_callbacks(const rac_device_callbacks_t* callbacks); /** * @brief Register device with backend if not already registered @@ -140,7 +140,7 @@ RAC_API rac_result_t rac_device_manager_set_callbacks(const rac_device_callbacks * @param build_token Optional build token for development mode (can be NULL) * @return RAC_SUCCESS on success or if already registered, error code otherwise */ -RAC_API rac_result_t rac_device_manager_register_if_needed(rac_environment_t env, +RAC_API RAC_NODISCARD rac_result_t rac_device_manager_register_if_needed(rac_environment_t env, const char* build_token); /** diff --git a/sdk/runanywhere-commons/include/rac/infrastructure/download/rac_download.h b/sdk/runanywhere-commons/include/rac/infrastructure/download/rac_download.h index 9f092f1e6..0c64b23f7 100644 --- a/sdk/runanywhere-commons/include/rac/infrastructure/download/rac_download.h +++ b/sdk/runanywhere-commons/include/rac/infrastructure/download/rac_download.h @@ -233,7 +233,7 @@ typedef struct rac_download_manager* rac_download_manager_handle_t; * @param out_handle Output: Handle to the created manager * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_download_manager_create(const rac_download_config_t* config, +RAC_API RAC_NODISCARD rac_result_t rac_download_manager_create(const rac_download_config_t* config, rac_download_manager_handle_t* out_handle); /** @@ -264,7 +264,7 @@ RAC_API void rac_download_manager_destroy(rac_download_manager_handle_t handle); * @param out_task_id Output: Task ID for tracking (owned, must be freed with rac_free) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_download_manager_start(rac_download_manager_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_download_manager_start(rac_download_manager_handle_t handle, const char* model_id, const char* url, const char* destination_path, rac_bool_t requires_extraction, @@ -281,7 +281,7 @@ RAC_API rac_result_t rac_download_manager_start(rac_download_manager_handle_t ha * @param task_id Task ID to cancel * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_download_manager_cancel(rac_download_manager_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_download_manager_cancel(rac_download_manager_handle_t handle, const char* task_id); /** @@ -292,7 +292,7 @@ RAC_API rac_result_t rac_download_manager_cancel(rac_download_manager_handle_t h * @param handle Manager handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_download_manager_pause_all(rac_download_manager_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_download_manager_pause_all(rac_download_manager_handle_t handle); /** * @brief Resume all paused downloads. @@ -302,7 +302,7 @@ RAC_API rac_result_t rac_download_manager_pause_all(rac_download_manager_handle_ * @param handle Manager handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_download_manager_resume_all(rac_download_manager_handle_t handle); +RAC_API RAC_NODISCARD rac_result_t rac_download_manager_resume_all(rac_download_manager_handle_t handle); // ============================================================================= // STATUS API @@ -316,7 +316,7 @@ RAC_API rac_result_t rac_download_manager_resume_all(rac_download_manager_handle * @param out_progress Output: Current progress * @return RAC_SUCCESS or error code (RAC_ERROR_NOT_FOUND if task doesn't exist) */ -RAC_API rac_result_t rac_download_manager_get_progress(rac_download_manager_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_download_manager_get_progress(rac_download_manager_handle_t handle, const char* task_id, rac_download_progress_t* out_progress); @@ -328,7 +328,7 @@ RAC_API rac_result_t rac_download_manager_get_progress(rac_download_manager_hand * @param out_count Output: Number of tasks * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_download_manager_get_active_tasks(rac_download_manager_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_download_manager_get_active_tasks(rac_download_manager_handle_t handle, char*** out_task_ids, size_t* out_count); /** @@ -340,7 +340,7 @@ RAC_API rac_result_t rac_download_manager_get_active_tasks(rac_download_manager_ * @param out_is_healthy Output: RAC_TRUE if healthy * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_download_manager_is_healthy(rac_download_manager_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_download_manager_is_healthy(rac_download_manager_handle_t handle, rac_bool_t* out_is_healthy); // ============================================================================= @@ -358,7 +358,7 @@ RAC_API rac_result_t rac_download_manager_is_healthy(rac_download_manager_handle * @param total_bytes Total bytes to download * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_download_manager_update_progress(rac_download_manager_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_download_manager_update_progress(rac_download_manager_handle_t handle, const char* task_id, int64_t bytes_downloaded, int64_t total_bytes); @@ -373,7 +373,7 @@ RAC_API rac_result_t rac_download_manager_update_progress(rac_download_manager_h * @param downloaded_path Path to the downloaded file * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_download_manager_mark_complete(rac_download_manager_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_download_manager_mark_complete(rac_download_manager_handle_t handle, const char* task_id, const char* downloaded_path); @@ -388,7 +388,7 @@ RAC_API rac_result_t rac_download_manager_mark_complete(rac_download_manager_han * @param error_message Error message (can be NULL) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_download_manager_mark_failed(rac_download_manager_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_download_manager_mark_failed(rac_download_manager_handle_t handle, const char* task_id, rac_result_t error_code, const char* error_message); @@ -407,7 +407,7 @@ RAC_API rac_result_t rac_download_manager_mark_failed(rac_download_manager_handl * @param extracted_path Path to the extracted model directory * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_download_manager_mark_extraction_complete( +RAC_API RAC_NODISCARD rac_result_t rac_download_manager_mark_extraction_complete( rac_download_manager_handle_t handle, const char* task_id, const char* extracted_path); /** @@ -421,7 +421,7 @@ RAC_API rac_result_t rac_download_manager_mark_extraction_complete( * @param error_message Error description (can be NULL) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_download_manager_mark_extraction_failed( +RAC_API RAC_NODISCARD rac_result_t rac_download_manager_mark_extraction_failed( rac_download_manager_handle_t handle, const char* task_id, rac_result_t error_code, const char* error_message); diff --git a/sdk/runanywhere-commons/include/rac/infrastructure/download/rac_download_orchestrator.h b/sdk/runanywhere-commons/include/rac/infrastructure/download/rac_download_orchestrator.h index ce957d376..9f20e01fe 100644 --- a/sdk/runanywhere-commons/include/rac/infrastructure/download/rac_download_orchestrator.h +++ b/sdk/runanywhere-commons/include/rac/infrastructure/download/rac_download_orchestrator.h @@ -61,7 +61,7 @@ extern "C" { * @param out_task_id Output: Task ID for tracking/cancellation (owned, free with rac_free) * @return RAC_SUCCESS if download started, error code if failed to start */ -RAC_API rac_result_t rac_download_orchestrate( +RAC_API RAC_NODISCARD rac_result_t rac_download_orchestrate( rac_download_manager_handle_t dm_handle, const char* model_id, const char* download_url, rac_inference_framework_t framework, rac_model_format_t format, rac_archive_structure_t archive_structure, @@ -88,7 +88,7 @@ RAC_API rac_result_t rac_download_orchestrate( * @param out_task_id Output: Task ID for tracking/cancellation (owned, free with rac_free) * @return RAC_SUCCESS if download started, error code if failed to start */ -RAC_API rac_result_t rac_download_orchestrate_multi( +RAC_API RAC_NODISCARD rac_result_t rac_download_orchestrate_multi( rac_download_manager_handle_t dm_handle, const char* model_id, const rac_model_file_descriptor_t* files, size_t file_count, const char* base_download_url, rac_inference_framework_t framework, rac_model_format_t format, @@ -118,7 +118,7 @@ RAC_API rac_result_t rac_download_orchestrate_multi( * @param path_size Size of output buffer * @return RAC_SUCCESS if model path found, RAC_ERROR_NOT_FOUND if no model file found */ -RAC_API rac_result_t rac_find_model_path_after_extraction( +RAC_API RAC_NODISCARD rac_result_t rac_find_model_path_after_extraction( const char* extracted_dir, rac_archive_structure_t structure, rac_inference_framework_t framework, rac_model_format_t format, char* out_path, size_t path_size); @@ -142,7 +142,7 @@ RAC_API rac_result_t rac_find_model_path_after_extraction( * @param out_needs_extraction Output: RAC_TRUE if download needs extraction * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_download_compute_destination(const char* model_id, +RAC_API RAC_NODISCARD rac_result_t rac_download_compute_destination(const char* model_id, const char* download_url, rac_inference_framework_t framework, rac_model_format_t format, char* out_path, diff --git a/sdk/runanywhere-commons/include/rac/infrastructure/events/rac_events.h b/sdk/runanywhere-commons/include/rac/infrastructure/events/rac_events.h index 7a9a56c5b..6e1d05078 100644 --- a/sdk/runanywhere-commons/include/rac/infrastructure/events/rac_events.h +++ b/sdk/runanywhere-commons/include/rac/infrastructure/events/rac_events.h @@ -143,7 +143,7 @@ RAC_API void rac_event_unsubscribe(uint64_t subscription_id); * @param event The event to publish * @return RAC_SUCCESS on success, or an error code on failure */ -RAC_API rac_result_t rac_event_publish(const rac_event_t* event); +RAC_API RAC_NODISCARD rac_result_t rac_event_publish(const rac_event_t* event); /** * Track an event (convenience function matching Swift's EventPublisher.track). @@ -154,7 +154,7 @@ RAC_API rac_result_t rac_event_publish(const rac_event_t* event); * @param properties_json Event properties as JSON (can be NULL) * @return RAC_SUCCESS on success, or an error code on failure */ -RAC_API rac_result_t rac_event_track(const char* type, rac_event_category_t category, +RAC_API RAC_NODISCARD rac_result_t rac_event_track(const char* type, rac_event_category_t category, rac_event_destination_t destination, const char* properties_json); diff --git a/sdk/runanywhere-commons/include/rac/infrastructure/extraction/rac_extraction.h b/sdk/runanywhere-commons/include/rac/infrastructure/extraction/rac_extraction.h index fd9b0ea45..aba275e26 100644 --- a/sdk/runanywhere-commons/include/rac/infrastructure/extraction/rac_extraction.h +++ b/sdk/runanywhere-commons/include/rac/infrastructure/extraction/rac_extraction.h @@ -123,7 +123,7 @@ typedef void (*rac_extraction_progress_fn)(int32_t files_extracted, int32_t tota * - RAC_ERROR_FILE_NOT_FOUND: Archive file does not exist * - RAC_ERROR_NULL_POINTER: archive_path or destination_dir is NULL */ -RAC_API rac_result_t rac_extract_archive_native(const char* archive_path, +RAC_API RAC_NODISCARD rac_result_t rac_extract_archive_native(const char* archive_path, const char* destination_dir, const rac_extraction_options_t* options, rac_extraction_progress_fn progress_callback, diff --git a/sdk/runanywhere-commons/include/rac/infrastructure/file_management/rac_file_manager.h b/sdk/runanywhere-commons/include/rac/infrastructure/file_management/rac_file_manager.h index d77430c3f..24fc4c5ec 100644 --- a/sdk/runanywhere-commons/include/rac/infrastructure/file_management/rac_file_manager.h +++ b/sdk/runanywhere-commons/include/rac/infrastructure/file_management/rac_file_manager.h @@ -157,7 +157,7 @@ typedef struct { * @param cb Platform I/O callbacks * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_file_manager_create_directory_structure(const rac_file_callbacks_t* cb); +RAC_API RAC_NODISCARD rac_result_t rac_file_manager_create_directory_structure(const rac_file_callbacks_t* cb); // ============================================================================= // MODEL FOLDER MANAGEMENT @@ -175,7 +175,7 @@ RAC_API rac_result_t rac_file_manager_create_directory_structure(const rac_file_ * @param path_size Size of output buffer * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_file_manager_create_model_folder(const rac_file_callbacks_t* cb, +RAC_API RAC_NODISCARD rac_result_t rac_file_manager_create_model_folder(const rac_file_callbacks_t* cb, const char* model_id, rac_inference_framework_t framework, char* out_path, size_t path_size); @@ -190,7 +190,7 @@ RAC_API rac_result_t rac_file_manager_create_model_folder(const rac_file_callbac * @param out_has_contents Output: RAC_TRUE if folder has files (can be NULL) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_file_manager_model_folder_exists(const rac_file_callbacks_t* cb, +RAC_API RAC_NODISCARD rac_result_t rac_file_manager_model_folder_exists(const rac_file_callbacks_t* cb, const char* model_id, rac_inference_framework_t framework, rac_bool_t* out_exists, @@ -208,7 +208,7 @@ RAC_API rac_result_t rac_file_manager_model_folder_exists(const rac_file_callbac * @param framework Inference framework * @return RAC_SUCCESS, or RAC_ERROR_FILE_NOT_FOUND if folder doesn't exist */ -RAC_API rac_result_t rac_file_manager_delete_model(const rac_file_callbacks_t* cb, +RAC_API RAC_NODISCARD rac_result_t rac_file_manager_delete_model(const rac_file_callbacks_t* cb, const char* model_id, rac_inference_framework_t framework); @@ -233,7 +233,7 @@ RAC_API rac_result_t rac_file_manager_delete_model(const rac_file_callbacks_t* c * @param out_size Output: Total size in bytes * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_file_manager_calculate_dir_size(const rac_file_callbacks_t* cb, +RAC_API RAC_NODISCARD rac_result_t rac_file_manager_calculate_dir_size(const rac_file_callbacks_t* cb, const char* path, int64_t* out_size); /** @@ -245,7 +245,7 @@ RAC_API rac_result_t rac_file_manager_calculate_dir_size(const rac_file_callback * @param out_size Output: Total models size in bytes * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_file_manager_models_storage_used(const rac_file_callbacks_t* cb, +RAC_API RAC_NODISCARD rac_result_t rac_file_manager_models_storage_used(const rac_file_callbacks_t* cb, int64_t* out_size); // ============================================================================= @@ -266,7 +266,7 @@ RAC_API rac_result_t rac_file_manager_models_storage_used(const rac_file_callbac * @param cb Platform I/O callbacks * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_file_manager_clear_cache(const rac_file_callbacks_t* cb); +RAC_API RAC_NODISCARD rac_result_t rac_file_manager_clear_cache(const rac_file_callbacks_t* cb); /** * @brief Clear the temp directory. @@ -281,7 +281,7 @@ RAC_API rac_result_t rac_file_manager_clear_cache(const rac_file_callbacks_t* cb * @param cb Platform I/O callbacks * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_file_manager_clear_temp(const rac_file_callbacks_t* cb); +RAC_API RAC_NODISCARD rac_result_t rac_file_manager_clear_temp(const rac_file_callbacks_t* cb); /** * @brief Get the cache directory size. @@ -290,7 +290,7 @@ RAC_API rac_result_t rac_file_manager_clear_temp(const rac_file_callbacks_t* cb) * @param out_size Output: Cache size in bytes * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_file_manager_cache_size(const rac_file_callbacks_t* cb, +RAC_API RAC_NODISCARD rac_result_t rac_file_manager_cache_size(const rac_file_callbacks_t* cb, int64_t* out_size); // ============================================================================= @@ -312,7 +312,7 @@ RAC_API rac_result_t rac_file_manager_cache_size(const rac_file_callbacks_t* cb, * @param out_info Output: Storage information * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_file_manager_get_storage_info(const rac_file_callbacks_t* cb, +RAC_API RAC_NODISCARD rac_result_t rac_file_manager_get_storage_info(const rac_file_callbacks_t* cb, rac_file_manager_storage_info_t* out_info); /** @@ -331,7 +331,7 @@ RAC_API rac_result_t rac_file_manager_get_storage_info(const rac_file_callbacks_ * from rac_storage_analyzer.h) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_file_manager_check_storage( +RAC_API RAC_NODISCARD rac_result_t rac_file_manager_check_storage( const rac_file_callbacks_t* cb, int64_t required_bytes, rac_storage_availability_t* out_availability); @@ -349,7 +349,7 @@ RAC_API rac_result_t rac_file_manager_check_storage( * @param path Directory path to clear * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_file_manager_clear_directory(const rac_file_callbacks_t* cb, +RAC_API RAC_NODISCARD rac_result_t rac_file_manager_clear_directory(const rac_file_callbacks_t* cb, const char* path); #ifdef __cplusplus diff --git a/sdk/runanywhere-commons/include/rac/infrastructure/model_management/rac_lora_registry.h b/sdk/runanywhere-commons/include/rac/infrastructure/model_management/rac_lora_registry.h index 38e60ca37..218c7191b 100644 --- a/sdk/runanywhere-commons/include/rac/infrastructure/model_management/rac_lora_registry.h +++ b/sdk/runanywhere-commons/include/rac/infrastructure/model_management/rac_lora_registry.h @@ -47,7 +47,7 @@ typedef struct rac_lora_registry* rac_lora_registry_handle_t; * @return RAC_SUCCESS, RAC_ERROR_INVALID_ARGUMENT (NULL out_handle), * or RAC_ERROR_OUT_OF_MEMORY */ -RAC_API rac_result_t rac_lora_registry_create(rac_lora_registry_handle_t* out_handle); +RAC_API RAC_NODISCARD rac_result_t rac_lora_registry_create(rac_lora_registry_handle_t* out_handle); /** * @brief Destroy a LoRA adapter registry and free all entries @@ -68,7 +68,7 @@ RAC_API void rac_lora_registry_destroy(rac_lora_registry_handle_t handle); * @return RAC_SUCCESS, RAC_ERROR_INVALID_ARGUMENT (NULL handle/entry/id), * or RAC_ERROR_OUT_OF_MEMORY */ -RAC_API rac_result_t rac_lora_registry_register(rac_lora_registry_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_lora_registry_register(rac_lora_registry_handle_t handle, const rac_lora_entry_t* entry); /** @@ -77,7 +77,7 @@ RAC_API rac_result_t rac_lora_registry_register(rac_lora_registry_handle_t handl * @param adapter_id ID of the adapter to remove * @return RAC_SUCCESS or RAC_ERROR_NOT_FOUND */ -RAC_API rac_result_t rac_lora_registry_remove(rac_lora_registry_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_lora_registry_remove(rac_lora_registry_handle_t handle, const char* adapter_id); // QUERIES @@ -90,7 +90,7 @@ RAC_API rac_result_t rac_lora_registry_remove(rac_lora_registry_handle_t handle, * @return RAC_SUCCESS, RAC_ERROR_INVALID_ARGUMENT (NULL params), * or RAC_ERROR_OUT_OF_MEMORY */ -RAC_API rac_result_t rac_lora_registry_get_all(rac_lora_registry_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_lora_registry_get_all(rac_lora_registry_handle_t handle, rac_lora_entry_t*** out_entries, size_t* out_count); @@ -103,7 +103,7 @@ RAC_API rac_result_t rac_lora_registry_get_all(rac_lora_registry_handle_t handle * @return RAC_SUCCESS, RAC_ERROR_INVALID_ARGUMENT (NULL params), * or RAC_ERROR_OUT_OF_MEMORY */ -RAC_API rac_result_t rac_lora_registry_get_for_model(rac_lora_registry_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_lora_registry_get_for_model(rac_lora_registry_handle_t handle, const char* model_id, rac_lora_entry_t*** out_entries, size_t* out_count); @@ -116,7 +116,7 @@ RAC_API rac_result_t rac_lora_registry_get_for_model(rac_lora_registry_handle_t * @return RAC_SUCCESS, RAC_ERROR_INVALID_ARGUMENT (NULL params), * RAC_ERROR_NOT_FOUND, or RAC_ERROR_OUT_OF_MEMORY */ -RAC_API rac_result_t rac_lora_registry_get(rac_lora_registry_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_lora_registry_get(rac_lora_registry_handle_t handle, const char* adapter_id, rac_lora_entry_t** out_entry); diff --git a/sdk/runanywhere-commons/include/rac/infrastructure/model_management/rac_model_assignment.h b/sdk/runanywhere-commons/include/rac/infrastructure/model_management/rac_model_assignment.h index 04036257c..67dacc7ff 100644 --- a/sdk/runanywhere-commons/include/rac/infrastructure/model_management/rac_model_assignment.h +++ b/sdk/runanywhere-commons/include/rac/infrastructure/model_management/rac_model_assignment.h @@ -97,7 +97,7 @@ rac_model_assignment_set_callbacks(const rac_assignment_callbacks_t* callbacks); * @param out_count Number of models returned * @return RAC_SUCCESS on success, error code otherwise */ -RAC_API rac_result_t rac_model_assignment_fetch(rac_bool_t force_refresh, +RAC_API RAC_NODISCARD rac_result_t rac_model_assignment_fetch(rac_bool_t force_refresh, rac_model_info_t*** out_models, size_t* out_count); /** @@ -111,7 +111,7 @@ RAC_API rac_result_t rac_model_assignment_fetch(rac_bool_t force_refresh, * @param out_count Number of models returned * @return RAC_SUCCESS on success, error code otherwise */ -RAC_API rac_result_t rac_model_assignment_get_by_framework(rac_inference_framework_t framework, +RAC_API RAC_NODISCARD rac_result_t rac_model_assignment_get_by_framework(rac_inference_framework_t framework, rac_model_info_t*** out_models, size_t* out_count); @@ -126,7 +126,7 @@ RAC_API rac_result_t rac_model_assignment_get_by_framework(rac_inference_framewo * @param out_count Number of models returned * @return RAC_SUCCESS on success, error code otherwise */ -RAC_API rac_result_t rac_model_assignment_get_by_category(rac_model_category_t category, +RAC_API RAC_NODISCARD rac_result_t rac_model_assignment_get_by_category(rac_model_category_t category, rac_model_info_t*** out_models, size_t* out_count); diff --git a/sdk/runanywhere-commons/include/rac/infrastructure/model_management/rac_model_compatibility.h b/sdk/runanywhere-commons/include/rac/infrastructure/model_management/rac_model_compatibility.h index 85323d5fa..5aee0a7b1 100644 --- a/sdk/runanywhere-commons/include/rac/infrastructure/model_management/rac_model_compatibility.h +++ b/sdk/runanywhere-commons/include/rac/infrastructure/model_management/rac_model_compatibility.h @@ -55,7 +55,7 @@ typedef struct rac_model_compatibility_result { * @param out_result Output: compatibility result * @return RAC_SUCCESS, RAC_ERROR_NOT_FOUND if model not in registry, or other error */ -RAC_API rac_result_t rac_model_check_compatibility( +RAC_API RAC_NODISCARD rac_result_t rac_model_check_compatibility( rac_model_registry_handle_t registry_handle, const char* model_id, int64_t available_ram, diff --git a/sdk/runanywhere-commons/include/rac/infrastructure/model_management/rac_model_paths.h b/sdk/runanywhere-commons/include/rac/infrastructure/model_management/rac_model_paths.h index b759dccae..2e9629931 100644 --- a/sdk/runanywhere-commons/include/rac/infrastructure/model_management/rac_model_paths.h +++ b/sdk/runanywhere-commons/include/rac/infrastructure/model_management/rac_model_paths.h @@ -39,7 +39,7 @@ extern "C" { * "/var/mobile/Containers/Data/Application/.../Documents") * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_model_paths_set_base_dir(const char* base_dir); +RAC_API RAC_NODISCARD rac_result_t rac_model_paths_set_base_dir(const char* base_dir); /** * @brief Get the configured base directory. @@ -60,7 +60,7 @@ RAC_API const char* rac_model_paths_get_base_dir(void); * @param path_size Size of output buffer * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_model_paths_get_base_directory(char* out_path, size_t path_size); +RAC_API RAC_NODISCARD rac_result_t rac_model_paths_get_base_directory(char* out_path, size_t path_size); /** * @brief Get the models directory. @@ -72,7 +72,7 @@ RAC_API rac_result_t rac_model_paths_get_base_directory(char* out_path, size_t p * @param path_size Size of output buffer * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_model_paths_get_models_directory(char* out_path, size_t path_size); +RAC_API RAC_NODISCARD rac_result_t rac_model_paths_get_models_directory(char* out_path, size_t path_size); // ============================================================================= // FRAMEWORK-SPECIFIC PATHS - Mirrors ModelPathUtils framework methods @@ -89,7 +89,7 @@ RAC_API rac_result_t rac_model_paths_get_models_directory(char* out_path, size_t * @param path_size Size of output buffer * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_model_paths_get_framework_directory(rac_inference_framework_t framework, +RAC_API RAC_NODISCARD rac_result_t rac_model_paths_get_framework_directory(rac_inference_framework_t framework, char* out_path, size_t path_size); /** @@ -104,7 +104,7 @@ RAC_API rac_result_t rac_model_paths_get_framework_directory(rac_inference_frame * @param path_size Size of output buffer * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_model_paths_get_model_folder(const char* model_id, +RAC_API RAC_NODISCARD rac_result_t rac_model_paths_get_model_folder(const char* model_id, rac_inference_framework_t framework, char* out_path, size_t path_size); @@ -125,7 +125,7 @@ RAC_API rac_result_t rac_model_paths_get_model_folder(const char* model_id, * @param path_size Size of output buffer * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_model_paths_get_model_file_path(const char* model_id, +RAC_API RAC_NODISCARD rac_result_t rac_model_paths_get_model_file_path(const char* model_id, rac_inference_framework_t framework, rac_model_format_t format, char* out_path, size_t path_size); @@ -144,7 +144,7 @@ RAC_API rac_result_t rac_model_paths_get_model_file_path(const char* model_id, * @param path_size Size of output buffer * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_model_paths_get_expected_model_path(const char* model_id, +RAC_API RAC_NODISCARD rac_result_t rac_model_paths_get_expected_model_path(const char* model_id, rac_inference_framework_t framework, rac_model_format_t format, char* out_path, size_t path_size); @@ -158,7 +158,7 @@ RAC_API rac_result_t rac_model_paths_get_expected_model_path(const char* model_i * @param path_size Size of output buffer * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_model_paths_get_model_path(const rac_model_info_t* model_info, +RAC_API RAC_NODISCARD rac_result_t rac_model_paths_get_model_path(const rac_model_info_t* model_info, char* out_path, size_t path_size); // ============================================================================= @@ -175,7 +175,7 @@ RAC_API rac_result_t rac_model_paths_get_model_path(const rac_model_info_t* mode * @param path_size Size of output buffer * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_model_paths_get_cache_directory(char* out_path, size_t path_size); +RAC_API RAC_NODISCARD rac_result_t rac_model_paths_get_cache_directory(char* out_path, size_t path_size); /** * @brief Get the temporary files directory. @@ -187,7 +187,7 @@ RAC_API rac_result_t rac_model_paths_get_cache_directory(char* out_path, size_t * @param path_size Size of output buffer * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_model_paths_get_temp_directory(char* out_path, size_t path_size); +RAC_API RAC_NODISCARD rac_result_t rac_model_paths_get_temp_directory(char* out_path, size_t path_size); /** * @brief Get the downloads directory. @@ -199,7 +199,7 @@ RAC_API rac_result_t rac_model_paths_get_temp_directory(char* out_path, size_t p * @param path_size Size of output buffer * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_model_paths_get_downloads_directory(char* out_path, size_t path_size); +RAC_API RAC_NODISCARD rac_result_t rac_model_paths_get_downloads_directory(char* out_path, size_t path_size); // ============================================================================= // PATH ANALYSIS - Mirrors ModelPathUtils analysis methods @@ -214,7 +214,7 @@ RAC_API rac_result_t rac_model_paths_get_downloads_directory(char* out_path, siz * @param model_id_size Size of output buffer * @return RAC_SUCCESS if model ID found, RAC_ERROR_NOT_FOUND otherwise */ -RAC_API rac_result_t rac_model_paths_extract_model_id(const char* path, char* out_model_id, +RAC_API RAC_NODISCARD rac_result_t rac_model_paths_extract_model_id(const char* path, char* out_model_id, size_t model_id_size); /** @@ -225,7 +225,7 @@ RAC_API rac_result_t rac_model_paths_extract_model_id(const char* path, char* ou * @param out_framework Output: The framework if found * @return RAC_SUCCESS if framework found, RAC_ERROR_NOT_FOUND otherwise */ -RAC_API rac_result_t rac_model_paths_extract_framework(const char* path, +RAC_API RAC_NODISCARD rac_result_t rac_model_paths_extract_framework(const char* path, rac_inference_framework_t* out_framework); /** diff --git a/sdk/runanywhere-commons/include/rac/infrastructure/model_management/rac_model_registry.h b/sdk/runanywhere-commons/include/rac/infrastructure/model_management/rac_model_registry.h index 5531a7302..64e82cf8a 100644 --- a/sdk/runanywhere-commons/include/rac/infrastructure/model_management/rac_model_registry.h +++ b/sdk/runanywhere-commons/include/rac/infrastructure/model_management/rac_model_registry.h @@ -48,7 +48,7 @@ typedef struct rac_model_registry* rac_model_registry_handle_t; * @param out_handle Output: Handle to the created registry * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_model_registry_create(rac_model_registry_handle_t* out_handle); +RAC_API RAC_NODISCARD rac_result_t rac_model_registry_create(rac_model_registry_handle_t* out_handle); /** * @brief Destroy a model registry instance. @@ -70,7 +70,7 @@ RAC_API void rac_model_registry_destroy(rac_model_registry_handle_t handle); * @param model Model info to save * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_model_registry_save(rac_model_registry_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_model_registry_save(rac_model_registry_handle_t handle, const rac_model_info_t* model); /** @@ -83,7 +83,7 @@ RAC_API rac_result_t rac_model_registry_save(rac_model_registry_handle_t handle, * @param out_model Output: Model info (owned, must be freed with rac_model_info_free) * @return RAC_SUCCESS, RAC_ERROR_NOT_FOUND, or other error code */ -RAC_API rac_result_t rac_model_registry_get(rac_model_registry_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_model_registry_get(rac_model_registry_handle_t handle, const char* model_id, rac_model_info_t** out_model); /** @@ -97,7 +97,7 @@ RAC_API rac_result_t rac_model_registry_get(rac_model_registry_handle_t handle, * @param out_model Output: Model info (owned, must be freed with rac_model_info_free) * @return RAC_SUCCESS, RAC_ERROR_NOT_FOUND, or other error code */ -RAC_API rac_result_t rac_model_registry_get_by_path(rac_model_registry_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_model_registry_get_by_path(rac_model_registry_handle_t handle, const char* local_path, rac_model_info_t** out_model); @@ -111,7 +111,7 @@ RAC_API rac_result_t rac_model_registry_get_by_path(rac_model_registry_handle_t * @param out_count Output: Number of models * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_model_registry_get_all(rac_model_registry_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_model_registry_get_all(rac_model_registry_handle_t handle, rac_model_info_t*** out_models, size_t* out_count); /** @@ -126,7 +126,7 @@ RAC_API rac_result_t rac_model_registry_get_all(rac_model_registry_handle_t hand * @param out_count Output: Number of models * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_model_registry_get_by_frameworks( +RAC_API RAC_NODISCARD rac_result_t rac_model_registry_get_by_frameworks( rac_model_registry_handle_t handle, const rac_inference_framework_t* frameworks, size_t framework_count, rac_model_info_t*** out_models, size_t* out_count); @@ -140,7 +140,7 @@ RAC_API rac_result_t rac_model_registry_get_by_frameworks( * @param model_id Model identifier * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_model_registry_update_last_used(rac_model_registry_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_model_registry_update_last_used(rac_model_registry_handle_t handle, const char* model_id); /** @@ -152,7 +152,7 @@ RAC_API rac_result_t rac_model_registry_update_last_used(rac_model_registry_hand * @param model_id Model identifier * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_model_registry_remove(rac_model_registry_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_model_registry_remove(rac_model_registry_handle_t handle, const char* model_id); /** @@ -165,7 +165,7 @@ RAC_API rac_result_t rac_model_registry_remove(rac_model_registry_handle_t handl * @param out_count Output: Number of models * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_model_registry_get_downloaded(rac_model_registry_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_model_registry_get_downloaded(rac_model_registry_handle_t handle, rac_model_info_t*** out_models, size_t* out_count); @@ -179,7 +179,7 @@ RAC_API rac_result_t rac_model_registry_get_downloaded(rac_model_registry_handle * @param local_path Path to downloaded model (can be NULL to clear) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_model_registry_update_download_status(rac_model_registry_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_model_registry_update_download_status(rac_model_registry_handle_t handle, const char* model_id, const char* local_path); @@ -355,7 +355,7 @@ typedef struct { * @param out_result Output: Discovery result (caller must call rac_discovery_result_free) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_model_registry_discover_downloaded( +RAC_API RAC_NODISCARD rac_result_t rac_model_registry_discover_downloaded( rac_model_registry_handle_t handle, const rac_discovery_callbacks_t* callbacks, rac_discovery_result_t* out_result); diff --git a/sdk/runanywhere-commons/include/rac/infrastructure/model_management/rac_model_strategy.h b/sdk/runanywhere-commons/include/rac/infrastructure/model_management/rac_model_strategy.h index f4835bfe8..a76cacd03 100644 --- a/sdk/runanywhere-commons/include/rac/infrastructure/model_management/rac_model_strategy.h +++ b/sdk/runanywhere-commons/include/rac/infrastructure/model_management/rac_model_strategy.h @@ -245,7 +245,7 @@ typedef struct { * @param strategy Storage strategy callbacks * @return RAC_SUCCESS on success */ -RAC_API rac_result_t rac_storage_strategy_register(rac_inference_framework_t framework, +RAC_API RAC_NODISCARD rac_result_t rac_storage_strategy_register(rac_inference_framework_t framework, const rac_storage_strategy_t* strategy); /** @@ -257,7 +257,7 @@ RAC_API rac_result_t rac_storage_strategy_register(rac_inference_framework_t fra * @param strategy Download strategy callbacks * @return RAC_SUCCESS on success */ -RAC_API rac_result_t rac_download_strategy_register(rac_inference_framework_t framework, +RAC_API RAC_NODISCARD rac_result_t rac_download_strategy_register(rac_inference_framework_t framework, const rac_download_strategy_t* strategy); /** @@ -304,7 +304,7 @@ rac_download_strategy_get(rac_inference_framework_t framework); * @param path_size Size of output buffer * @return RAC_SUCCESS if found */ -RAC_API rac_result_t rac_model_strategy_find_path(rac_inference_framework_t framework, +RAC_API RAC_NODISCARD rac_result_t rac_model_strategy_find_path(rac_inference_framework_t framework, const char* model_id, const char* model_folder, char* out_path, size_t path_size); @@ -316,7 +316,7 @@ RAC_API rac_result_t rac_model_strategy_find_path(rac_inference_framework_t fram * @param out_details Output storage details * @return RAC_SUCCESS if model detected */ -RAC_API rac_result_t rac_model_strategy_detect(rac_inference_framework_t framework, +RAC_API RAC_NODISCARD rac_result_t rac_model_strategy_detect(rac_inference_framework_t framework, const char* model_folder, rac_model_storage_details_t* out_details); @@ -337,7 +337,7 @@ RAC_API rac_bool_t rac_model_strategy_is_valid(rac_inference_framework_t framewo * @param config Download configuration * @return RAC_SUCCESS if ready */ -RAC_API rac_result_t rac_model_strategy_prepare_download(rac_inference_framework_t framework, +RAC_API RAC_NODISCARD rac_result_t rac_model_strategy_prepare_download(rac_inference_framework_t framework, const rac_model_download_config_t* config); /** @@ -349,7 +349,7 @@ RAC_API rac_result_t rac_model_strategy_prepare_download(rac_inference_framework * @param path_size Size of output buffer * @return RAC_SUCCESS on success */ -RAC_API rac_result_t rac_model_strategy_get_download_dest(rac_inference_framework_t framework, +RAC_API RAC_NODISCARD rac_result_t rac_model_strategy_get_download_dest(rac_inference_framework_t framework, const rac_model_download_config_t* config, char* out_path, size_t path_size); @@ -362,7 +362,7 @@ RAC_API rac_result_t rac_model_strategy_get_download_dest(rac_inference_framewor * @param out_result Output result * @return RAC_SUCCESS if successful */ -RAC_API rac_result_t rac_model_strategy_post_process(rac_inference_framework_t framework, +RAC_API RAC_NODISCARD rac_result_t rac_model_strategy_post_process(rac_inference_framework_t framework, const rac_model_download_config_t* config, const char* downloaded_path, rac_download_result_t* out_result); diff --git a/sdk/runanywhere-commons/include/rac/infrastructure/model_management/rac_model_types.h b/sdk/runanywhere-commons/include/rac/infrastructure/model_management/rac_model_types.h index 72101ac13..c29794e40 100644 --- a/sdk/runanywhere-commons/include/rac/infrastructure/model_management/rac_model_types.h +++ b/sdk/runanywhere-commons/include/rac/infrastructure/model_management/rac_model_types.h @@ -337,7 +337,7 @@ RAC_API rac_model_category_t rac_model_category_from_framework(rac_inference_fra * @param out_count Output: Number of formats * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_framework_get_supported_formats(rac_inference_framework_t framework, +RAC_API RAC_NODISCARD rac_result_t rac_framework_get_supported_formats(rac_inference_framework_t framework, rac_model_format_t** out_formats, size_t* out_count); @@ -433,7 +433,7 @@ RAC_API rac_bool_t rac_artifact_requires_download(const rac_model_artifact_info_ * @param out_artifact Output: Inferred artifact info * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_artifact_infer_from_url(const char* url, rac_model_format_t format, +RAC_API RAC_NODISCARD rac_result_t rac_artifact_infer_from_url(const char* url, rac_model_format_t format, rac_model_artifact_info_t* out_artifact); /** diff --git a/sdk/runanywhere-commons/include/rac/infrastructure/storage/rac_storage_analyzer.h b/sdk/runanywhere-commons/include/rac/infrastructure/storage/rac_storage_analyzer.h index 94bc05464..4f7ce9e68 100644 --- a/sdk/runanywhere-commons/include/rac/infrastructure/storage/rac_storage_analyzer.h +++ b/sdk/runanywhere-commons/include/rac/infrastructure/storage/rac_storage_analyzer.h @@ -194,7 +194,7 @@ typedef struct rac_storage_analyzer* rac_storage_analyzer_handle_t; * @param out_handle Output: Created analyzer handle * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_storage_analyzer_create(const rac_storage_callbacks_t* callbacks, +RAC_API RAC_NODISCARD rac_result_t rac_storage_analyzer_create(const rac_storage_callbacks_t* callbacks, rac_storage_analyzer_handle_t* out_handle); /** @@ -218,7 +218,7 @@ RAC_API void rac_storage_analyzer_destroy(rac_storage_analyzer_handle_t handle); * @param out_info Output: Storage info (caller must call rac_storage_info_free) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_storage_analyzer_analyze(rac_storage_analyzer_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_storage_analyzer_analyze(rac_storage_analyzer_handle_t handle, rac_model_registry_handle_t registry_handle, rac_storage_info_t* out_info); @@ -232,7 +232,7 @@ RAC_API rac_result_t rac_storage_analyzer_analyze(rac_storage_analyzer_handle_t * @param out_metrics Output: Model metrics * @return RAC_SUCCESS or RAC_ERROR_NOT_FOUND */ -RAC_API rac_result_t rac_storage_analyzer_get_model_metrics( +RAC_API RAC_NODISCARD rac_result_t rac_storage_analyzer_get_model_metrics( rac_storage_analyzer_handle_t handle, rac_model_registry_handle_t registry_handle, const char* model_id, rac_inference_framework_t framework, rac_model_storage_metrics_t* out_metrics); @@ -246,7 +246,7 @@ RAC_API rac_result_t rac_storage_analyzer_get_model_metrics( * @param out_availability Output: Availability result * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_storage_analyzer_check_available( +RAC_API RAC_NODISCARD rac_result_t rac_storage_analyzer_check_available( rac_storage_analyzer_handle_t handle, int64_t model_size, double safety_margin, rac_storage_availability_t* out_availability); @@ -258,7 +258,7 @@ RAC_API rac_result_t rac_storage_analyzer_check_available( * @param out_size Output: Size in bytes * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_storage_analyzer_calculate_size(rac_storage_analyzer_handle_t handle, +RAC_API RAC_NODISCARD rac_result_t rac_storage_analyzer_calculate_size(rac_storage_analyzer_handle_t handle, const char* path, int64_t* out_size); // ============================================================================= diff --git a/sdk/runanywhere-commons/include/rac/infrastructure/telemetry/rac_telemetry_manager.h b/sdk/runanywhere-commons/include/rac/infrastructure/telemetry/rac_telemetry_manager.h index ab606695c..2accf8c5b 100644 --- a/sdk/runanywhere-commons/include/rac/infrastructure/telemetry/rac_telemetry_manager.h +++ b/sdk/runanywhere-commons/include/rac/infrastructure/telemetry/rac_telemetry_manager.h @@ -115,7 +115,7 @@ RAC_API void rac_telemetry_manager_set_http_callback(rac_telemetry_manager_t* ma * * Queues the payload for batching and sending. */ -RAC_API rac_result_t rac_telemetry_manager_track(rac_telemetry_manager_t* manager, +RAC_API RAC_NODISCARD rac_result_t rac_telemetry_manager_track(rac_telemetry_manager_t* manager, const rac_telemetry_payload_t* payload); /** @@ -123,7 +123,7 @@ RAC_API rac_result_t rac_telemetry_manager_track(rac_telemetry_manager_t* manage * * Converts analytics event to telemetry payload and queues it. */ -RAC_API rac_result_t rac_telemetry_manager_track_analytics(rac_telemetry_manager_t* manager, +RAC_API RAC_NODISCARD rac_result_t rac_telemetry_manager_track_analytics(rac_telemetry_manager_t* manager, rac_event_type_t event_type, const rac_analytics_event_data_t* data); @@ -132,7 +132,7 @@ RAC_API rac_result_t rac_telemetry_manager_track_analytics(rac_telemetry_manager * * Sends all queued events to the backend. */ -RAC_API rac_result_t rac_telemetry_manager_flush(rac_telemetry_manager_t* manager); +RAC_API RAC_NODISCARD rac_result_t rac_telemetry_manager_flush(rac_telemetry_manager_t* manager); // ============================================================================= // JSON SERIALIZATION @@ -147,7 +147,7 @@ RAC_API rac_result_t rac_telemetry_manager_flush(rac_telemetry_manager_t* manage * @param out_length Output: Length of JSON string * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_telemetry_manager_payload_to_json(const rac_telemetry_payload_t* payload, +RAC_API RAC_NODISCARD rac_result_t rac_telemetry_manager_payload_to_json(const rac_telemetry_payload_t* payload, rac_environment_t env, char** out_json, size_t* out_length); @@ -171,7 +171,7 @@ rac_telemetry_manager_batch_to_json(const rac_telemetry_batch_request_t* request * @param out_response Output: Parsed response (caller must free) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_telemetry_manager_parse_response( +RAC_API RAC_NODISCARD rac_result_t rac_telemetry_manager_parse_response( const char* json, rac_telemetry_batch_response_t* out_response); // ============================================================================= diff --git a/sdk/runanywhere-commons/include/rac/server/rac_server.h b/sdk/runanywhere-commons/include/rac/server/rac_server.h index 9edef920a..74e102052 100644 --- a/sdk/runanywhere-commons/include/rac/server/rac_server.h +++ b/sdk/runanywhere-commons/include/rac/server/rac_server.h @@ -153,7 +153,7 @@ typedef struct rac_server_status { * - RAC_ERROR_MODEL_LOAD_FAILED: Failed to load model * - RAC_ERROR_BIND_FAILED: Failed to bind to port */ -RAC_API rac_result_t rac_server_start(const rac_server_config_t* config); +RAC_API RAC_NODISCARD rac_result_t rac_server_start(const rac_server_config_t* config); /** * @brief Stop the HTTP server @@ -163,7 +163,7 @@ RAC_API rac_result_t rac_server_start(const rac_server_config_t* config); * * @return RAC_SUCCESS on success, RAC_ERROR_NOT_RUNNING if not running */ -RAC_API rac_result_t rac_server_stop(void); +RAC_API RAC_NODISCARD rac_result_t rac_server_stop(void); /** * @brief Check if the server is running @@ -178,7 +178,7 @@ RAC_API rac_bool_t rac_server_is_running(void); * @param status Output parameter for status (must not be NULL) * @return RAC_SUCCESS on success */ -RAC_API rac_result_t rac_server_get_status(rac_server_status_t* status); +RAC_API RAC_NODISCARD rac_result_t rac_server_get_status(rac_server_status_t* status); /** * @brief Block until the server stops diff --git a/sdk/runanywhere-commons/include/rac/utils/rac_image_utils.h b/sdk/runanywhere-commons/include/rac/utils/rac_image_utils.h index 2fb55ed59..d81a972d8 100644 --- a/sdk/runanywhere-commons/include/rac/utils/rac_image_utils.h +++ b/sdk/runanywhere-commons/include/rac/utils/rac_image_utils.h @@ -80,7 +80,7 @@ typedef struct rac_image_float { * @param out_image Output: Loaded image data (must be freed with rac_image_free) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_image_load_file(const char* file_path, rac_image_data_t* out_image); +RAC_API RAC_NODISCARD rac_result_t rac_image_load_file(const char* file_path, rac_image_data_t* out_image); /** * @brief Decode a base64-encoded image @@ -93,7 +93,7 @@ RAC_API rac_result_t rac_image_load_file(const char* file_path, rac_image_data_t * @param out_image Output: Loaded image data (must be freed with rac_image_free) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_image_decode_base64(const char* base64_data, size_t data_size, +RAC_API RAC_NODISCARD rac_result_t rac_image_decode_base64(const char* base64_data, size_t data_size, rac_image_data_t* out_image); /** @@ -106,7 +106,7 @@ RAC_API rac_result_t rac_image_decode_base64(const char* base64_data, size_t dat * @param out_image Output: Loaded image data (must be freed with rac_image_free) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_image_decode_bytes(const uint8_t* data, size_t data_size, +RAC_API RAC_NODISCARD rac_result_t rac_image_decode_bytes(const uint8_t* data, size_t data_size, rac_image_data_t* out_image); // ============================================================================= @@ -124,7 +124,7 @@ RAC_API rac_result_t rac_image_decode_bytes(const uint8_t* data, size_t data_siz * @param out_image Output: Resized image (must be freed with rac_image_free) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_image_resize(const rac_image_data_t* image, int32_t new_width, +RAC_API RAC_NODISCARD rac_result_t rac_image_resize(const rac_image_data_t* image, int32_t new_width, int32_t new_height, rac_image_data_t* out_image); /** @@ -138,7 +138,7 @@ RAC_API rac_result_t rac_image_resize(const rac_image_data_t* image, int32_t new * @param out_image Output: Resized image (must be freed with rac_image_free) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_image_resize_max(const rac_image_data_t* image, int32_t max_size, +RAC_API RAC_NODISCARD rac_result_t rac_image_resize_max(const rac_image_data_t* image, int32_t max_size, rac_image_data_t* out_image); /** @@ -155,7 +155,7 @@ RAC_API rac_result_t rac_image_resize_max(const rac_image_data_t* image, int32_t * @param out_float Output: Normalized float image (must be freed with rac_image_float_free) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_image_normalize(const rac_image_data_t* image, const float* mean, +RAC_API RAC_NODISCARD rac_result_t rac_image_normalize(const rac_image_data_t* image, const float* mean, const float* std, rac_image_float_t* out_float); /** @@ -168,7 +168,7 @@ RAC_API rac_result_t rac_image_normalize(const rac_image_data_t* image, const fl * @param out_chw Output: Float image in CHW format (must be freed with rac_image_float_free) * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_image_to_chw(const rac_image_float_t* image, rac_image_float_t* out_chw); +RAC_API RAC_NODISCARD rac_result_t rac_image_to_chw(const rac_image_float_t* image, rac_image_float_t* out_chw); // ============================================================================= // PIXEL FORMAT CONVERSION @@ -188,7 +188,7 @@ RAC_API rac_result_t rac_image_to_chw(const rac_image_float_t* image, rac_image_ * @param out_size Size of the output buffer * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_image_convert_rgba_to_rgb(const uint8_t* rgba_data, uint32_t width, +RAC_API RAC_NODISCARD rac_result_t rac_image_convert_rgba_to_rgb(const uint8_t* rgba_data, uint32_t width, uint32_t height, uint32_t row_stride, uint8_t* out_rgb_data, size_t out_size); @@ -206,7 +206,7 @@ RAC_API rac_result_t rac_image_convert_rgba_to_rgb(const uint8_t* rgba_data, uin * @param out_size Size of the output buffer * @return RAC_SUCCESS or error code */ -RAC_API rac_result_t rac_image_convert_bgra_to_rgb(const uint8_t* bgra_data, uint32_t width, +RAC_API RAC_NODISCARD rac_result_t rac_image_convert_bgra_to_rgb(const uint8_t* bgra_data, uint32_t width, uint32_t height, uint32_t bytes_per_row, uint8_t* out_rgb_data, size_t out_size); diff --git a/sdk/runanywhere-commons/src/backends/backend_interface.h b/sdk/runanywhere-commons/src/backends/backend_interface.h new file mode 100644 index 000000000..84cfacdd3 --- /dev/null +++ b/sdk/runanywhere-commons/src/backends/backend_interface.h @@ -0,0 +1,124 @@ +/** + * @file src/backends/backend_interface.h + * @brief Abstract C++ interface for concrete inference backends. + * + * INTERNAL ONLY. Not exposed to SDK consumers via include/. + * + * Today, each of the five backends (LlamaCPP, ONNX, WhisperCPP, MetalRT, + * WhisperKit CoreML) defines its own top-level C++ class and registers a + * capability-specific vtable (rac_llm_service_ops_t, + * rac_stt_service_ops_t, ...) with the service registry. That works, but + * it produces duplicated registration boilerplate and makes it awkward + * to express "run this model on the first backend that can handle it on + * the current hardware." + * + * IInferenceBackend gathers the cross-capability lifecycle contract + * (load_model, health_check, cancel, unload, primary_capability, + * supports_hardware) into a single abstract class so that: + * + * 1. New backends can `class MyBackend : public IInferenceBackend` and + * get the core contract for free. + * 2. Registration helpers (`register_backend(...)`) can be written + * once in terms of the abstract interface, instead of duplicated + * per-capability. + * 3. Runtime backend selection can consult + * `backend->supports_hardware(hardware_report)` to filter out + * backends whose required hardware is absent (e.g. MetalRT on + * x86 Linux) without each backend's can_handle() having to + * rediscover that on its own. + * + * This header intentionally stays small. Capability-specific operations + * (generate_text, transcribe_audio, synthesize_speech, etc.) still live + * in the per-capability service vtables; the abstract backend is about + * lifecycle + hardware dispatch, not a god-class. + * + * Existing backends don't need to be rewritten to inherit from this + * interface. Phase 8 introduces the interface and new backends are + * expected to use it; retrofit of the five legacy backends is + * incremental and is documented in CPP_LAYER_AUDIT.md as follow-up. + */ + +#ifndef RAC_BACKEND_INTERFACE_H +#define RAC_BACKEND_INTERFACE_H + +#include +#include + +#include "rac/core/rac_error.h" +#include "rac/core/rac_hardware.h" +#include "rac/core/rac_types.h" +#include "rac/infrastructure/model_management/rac_model_types.h" + +namespace rac { +namespace backends { + +// Small result struct returned by health_check(). A plain bool would +// conflate "not ready" with "failing"; this is explicit. +struct BackendHealth { + rac_bool_t is_ready = RAC_FALSE; + rac_bool_t is_degraded = RAC_FALSE; + rac_result_t last_error = RAC_SUCCESS; + std::string diagnostic; // Free-form, for logs only. +}; + +// Lightweight capability advertisement. Each backend declares what it +// can serve and what hardware it expects to have. The service registry +// uses these to prune backends before dispatching. +struct BackendAdvertisement { + rac_capability_t primary_capability = RAC_CAPABILITY_UNKNOWN; + rac_inference_framework_t framework = RAC_FRAMEWORK_UNKNOWN; + int32_t priority = 100; // Higher = preferred on tie-break. + std::string name; + std::string version; +}; + +// Abstract inference backend. Concrete backend classes inherit from this +// to opt into the shared lifecycle + hardware dispatch machinery. +class IInferenceBackend { +public: + virtual ~IInferenceBackend() = default; + + // Deleted because backends own heavyweight state (model memory, + // worker threads, ORT sessions). Copies are never what the caller + // wanted. + IInferenceBackend(const IInferenceBackend&) = delete; + IInferenceBackend& operator=(const IInferenceBackend&) = delete; + + // ---- Identification / discovery -------------------------------------- + + virtual BackendAdvertisement advertise() const noexcept = 0; + + // Return true if this backend can run on the current hardware. Called + // once at registration so backends can refuse to load on the wrong + // device (e.g. MetalRT on non-Apple-Silicon). Default implementation + // accepts any hardware - CPU-only backends can rely on it. + virtual rac_bool_t supports_hardware( + const rac_hardware_report_t& /*report*/) const noexcept { + return RAC_TRUE; + } + + // ---- Lifecycle ------------------------------------------------------- + + // Load a model given an on-disk path plus a JSON-ish config string. + // Backends that need richer config can parse `config_json` themselves + // (see e.g. llamacpp_backend.cpp initialize()). + virtual rac_result_t load_model(std::string_view model_path, + std::string_view config_json) noexcept = 0; + + // Release all resources associated with the currently loaded model. + virtual rac_result_t unload_model() noexcept = 0; + + // Cooperatively cancel any in-flight inference. Idempotent. + virtual rac_result_t cancel() noexcept { return RAC_SUCCESS; } + + // Query backend health. Always safe to call, even pre-load. + virtual BackendHealth health_check() const noexcept = 0; + +protected: + IInferenceBackend() = default; +}; + +} // namespace backends +} // namespace rac + +#endif // RAC_BACKEND_INTERFACE_H diff --git a/sdk/runanywhere-commons/src/backends/onnx/CMakeLists.txt b/sdk/runanywhere-commons/src/backends/onnx/CMakeLists.txt index 1646c9d9e..814591cc3 100644 --- a/sdk/runanywhere-commons/src/backends/onnx/CMakeLists.txt +++ b/sdk/runanywhere-commons/src/backends/onnx/CMakeLists.txt @@ -246,6 +246,13 @@ target_include_directories(rac_backend_onnx PUBLIC ${RAC_COMMONS_ROOT_DIR}/include/rac/backends ) +# Internal-only shared headers (e.g. internal/rac_platform_compat.h used by +# onnx_backend.cpp and wakeword_onnx.cpp). Not PUBLIC - the internal/ dir must +# never leak into consumers' include paths. +target_include_directories(rac_backend_onnx PRIVATE + ${RAC_COMMONS_ROOT_DIR}/src +) + # RAG embedding provider headers (onnx_embedding_provider.h lives in features/rag/) if(RAC_BACKEND_RAG AND EXISTS "${RAG_DIR}/onnx_embedding_provider.h") target_include_directories(rac_backend_onnx PRIVATE ${RAG_DIR}) diff --git a/sdk/runanywhere-commons/src/backends/onnx/onnx_backend.cpp b/sdk/runanywhere-commons/src/backends/onnx/onnx_backend.cpp index 1cd397b03..86e23f5b8 100644 --- a/sdk/runanywhere-commons/src/backends/onnx/onnx_backend.cpp +++ b/sdk/runanywhere-commons/src/backends/onnx/onnx_backend.cpp @@ -14,7 +14,7 @@ #include "onnx_backend.h" -#include "rac/core/rac_platform_compat.h" +#include "internal/rac_platform_compat.h" #ifdef _WIN32 #include // for _mkdir diff --git a/sdk/runanywhere-commons/src/backends/onnx/rac_backend_onnx_register.cpp b/sdk/runanywhere-commons/src/backends/onnx/rac_backend_onnx_register.cpp index 2d667c466..7b0b3ff8a 100644 --- a/sdk/runanywhere-commons/src/backends/onnx/rac_backend_onnx_register.cpp +++ b/sdk/runanywhere-commons/src/backends/onnx/rac_backend_onnx_register.cpp @@ -12,6 +12,7 @@ #include "rac/backends/rac_embeddings_onnx.h" #include +#include // sscanf for sherpa-onnx version parsing #include #include #include @@ -31,6 +32,10 @@ namespace fs = std::filesystem; #include "rac/infrastructure/model_management/rac_model_strategy.h" #include "rac/infrastructure/model_management/rac_model_types.h" +#if SHERPA_ONNX_AVAILABLE +#include "sherpa-onnx/c-api/c-api.h" +#endif + // ============================================================================= // STT VTABLE IMPLEMENTATION // ============================================================================= @@ -544,6 +549,70 @@ rac_result_t rac_backend_onnx_register(void) { return RAC_ERROR_MODULE_ALREADY_REGISTERED; } +#if SHERPA_ONNX_AVAILABLE + // ---- Sherpa-ONNX ABI compatibility check -------------------------------- + // + // The sherpa-onnx C API exposes struct types (SherpaOnnxOnlineRecognizerConfig, + // SherpaOnnxOfflineRecognizerConfig, SherpaOnnxVadModelConfig, ...) whose + // layouts are compiled into our .cpp files at the version of sherpa-onnx + // whose headers are currently on the include path (see VERSIONS file: + // SHERPA_ONNX_VERSION_*). The actual inference happens via prebuilt + // sherpa binaries shipped under third_party/sherpa-onnx-*/. + // + // If the prebuilt binary's struct layout diverges from our compile-time + // header's layout (major-version skew), struct field access -> SIGSEGV. + // + // Defensive check: query the runtime version string, log it loudly, and + // refuse registration if the major.minor does not match "1.12". This + // gives SDK integrators a clear error instead of a mysterious crash. + // + // NOTE: sherpa-onnx has been 1.12.x across all supported VERSIONS targets + // (iOS 1.12.18, macOS 1.12.18, Android 1.12.20, Linux 1.12.23, + // Windows 1.12.23). If we ever bump to 1.13 or 2.x, update the + // expected_major / expected_minor constants below. + { + const char* runtime_version = SherpaOnnxGetVersionStr(); + if (runtime_version == nullptr) { + RAC_LOG_ERROR(LOG_CAT, + "Sherpa-ONNX runtime did not return a version string; " + "refusing to register backend."); + return RAC_ERROR_BACKEND_INIT_FAILED; + } + + constexpr int expected_major = 1; + constexpr int expected_minor = 12; + int got_major = 0; + int got_minor = 0; + int got_patch = 0; + // sscanf returns number of fields successfully scanned. + int scanned = + std::sscanf(runtime_version, "%d.%d.%d", &got_major, &got_minor, &got_patch); + + if (scanned < 2) { + RAC_LOG_ERROR(LOG_CAT, + "Sherpa-ONNX runtime version string '%s' is unparseable; " + "refusing to register backend.", runtime_version); + return RAC_ERROR_BACKEND_INIT_FAILED; + } + + if (got_major != expected_major || got_minor != expected_minor) { + RAC_LOG_ERROR(LOG_CAT, + "Sherpa-ONNX ABI mismatch: runtime reports %d.%d.%d, " + "compile-time headers expect %d.%d.x. " + "Refusing to register backend - struct layouts may differ, " + "which would cause SIGSEGV on first inference. " + "Rebuild third_party/sherpa-onnx-* binaries to match headers, " + "or upgrade the commons SDK to a version aligned with your " + "sherpa-onnx binary.", + got_major, got_minor, got_patch, expected_major, expected_minor); + return RAC_ERROR_BACKEND_INCOMPATIBLE_VERSION; + } + + RAC_LOG_INFO(LOG_CAT, "Sherpa-ONNX runtime version: %s (expected %d.%d.x) - OK", + runtime_version, expected_major, expected_minor); + } +#endif // SHERPA_ONNX_AVAILABLE + // Register module (STT, TTS, VAD only; diffusion is CoreML-only in Swift SDK) rac_module_info_t module_info = {}; module_info.id = MODULE_ID; diff --git a/sdk/runanywhere-commons/src/backends/onnx/wakeword_onnx.cpp b/sdk/runanywhere-commons/src/backends/onnx/wakeword_onnx.cpp index 6f89244f2..48f6e797c 100644 --- a/sdk/runanywhere-commons/src/backends/onnx/wakeword_onnx.cpp +++ b/sdk/runanywhere-commons/src/backends/onnx/wakeword_onnx.cpp @@ -19,7 +19,8 @@ #include "rac/backends/rac_wakeword_onnx.h" #include "rac/backends/rac_vad_onnx.h" #include "rac/core/rac_logger.h" -#include "rac/core/rac_platform_compat.h" +#include "rac/features/wakeword/rac_wakeword_service.h" // provider vtable hook +#include "internal/rac_platform_compat.h" #ifdef RAC_HAS_ONNX #include @@ -1021,6 +1022,97 @@ RAC_ONNX_API void rac_wakeword_onnx_destroy(rac_handle_t handle) { delete backend; } +// ============================================================================= +// WAKE-WORD PROVIDER VTABLE ADAPTERS +// ============================================================================= +// +// The rac_commons wake-word service (include/rac/features/wakeword/ +// rac_wakeword_service.h) dispatches to whichever concrete backend is +// registered via rac_wakeword_provider_set(). These adapter functions +// translate that generic vtable into our ONNX-specific API +// (rac_wakeword_onnx_* at the top of this file), so we can serve wake-word +// detection without the service layer taking a hard link dependency on +// rac_backend_onnx. +// ============================================================================= + +static rac_result_t onnx_wakeword_provider_create(const rac_wakeword_config_t* cfg, + rac_handle_t* out_handle, + void* /*user_data*/) { + rac_wakeword_onnx_config_t onnx_cfg = RAC_WAKEWORD_ONNX_CONFIG_DEFAULT; + if (cfg != nullptr) { + onnx_cfg.sample_rate = cfg->sample_rate; + onnx_cfg.threshold = cfg->threshold; + onnx_cfg.num_threads = cfg->num_threads; + // Convert ms -> samples. Default 80ms -> 1280 samples at 16kHz. + onnx_cfg.frame_length = + (cfg->sample_rate > 0 && cfg->frame_length_ms > 0) + ? (cfg->sample_rate * cfg->frame_length_ms) / 1000 + : 1280; + onnx_cfg.enable_optimization = RAC_TRUE; + // embedding_model_path / melspec_model_path are plumbed separately + // via rac_wakeword_onnx_init_shared_models() today; the generic + // service API doesn't yet expose them. See follow-up work on the + // unified stream config in Phase 9. + } + return rac_wakeword_onnx_create(&onnx_cfg, out_handle); +} + +static rac_result_t onnx_wakeword_provider_load_model(rac_handle_t h, + const char* path, + const char* id, + const char* word, + void* /*user_data*/) { + return rac_wakeword_onnx_load_model(h, path, id, word); +} + +static rac_result_t onnx_wakeword_provider_unload_model(rac_handle_t h, + const char* id, + void* /*user_data*/) { + return rac_wakeword_onnx_unload_model(h, id); +} + +static rac_result_t onnx_wakeword_provider_load_vad(rac_handle_t h, + const char* path, + void* /*user_data*/) { + return rac_wakeword_onnx_load_vad(h, path); +} + +static rac_result_t onnx_wakeword_provider_process( + rac_handle_t h, + const float* samples, size_t n, + int32_t* out_det, float* out_conf, + rac_bool_t* out_vad_speech, float* out_vad_conf, + void* /*user_data*/) { + return rac_wakeword_onnx_process_with_vad( + h, samples, n, out_det, out_conf, out_vad_speech, out_vad_conf); +} + +static rac_result_t onnx_wakeword_provider_reset(rac_handle_t h, void* /*user_data*/) { + return rac_wakeword_onnx_reset(h); +} + +static rac_result_t onnx_wakeword_provider_set_threshold(rac_handle_t h, + float threshold, + void* /*user_data*/) { + return rac_wakeword_onnx_set_threshold(h, threshold); +} + +static void onnx_wakeword_provider_destroy(rac_handle_t h, void* /*user_data*/) { + rac_wakeword_onnx_destroy(h); +} + +static const rac_wakeword_provider_ops_t g_onnx_wakeword_provider_ops = { + onnx_wakeword_provider_create, + onnx_wakeword_provider_load_model, + onnx_wakeword_provider_unload_model, + onnx_wakeword_provider_load_vad, + onnx_wakeword_provider_process, + onnx_wakeword_provider_reset, + onnx_wakeword_provider_set_threshold, + onnx_wakeword_provider_destroy, + /* user_data */ nullptr, +}; + // ============================================================================= // BACKEND REGISTRATION // ============================================================================= @@ -1032,8 +1124,13 @@ RAC_ONNX_API rac_result_t rac_backend_wakeword_onnx_register(void) { return RAC_SUCCESS; } + // Install ourselves as the wake-word provider in rac_commons. From this + // point on, any rac_wakeword_* service call will route through our + // rac_wakeword_onnx_* functions. + (void)rac_wakeword_provider_set(&g_onnx_wakeword_provider_ops); + g_wakeword_onnx_registered = true; - RAC_LOG_INFO(LOG_TAG, "Backend registered"); + RAC_LOG_INFO(LOG_TAG, "Backend registered (wakeword provider installed)"); return RAC_SUCCESS; } @@ -1043,8 +1140,12 @@ RAC_ONNX_API rac_result_t rac_backend_wakeword_onnx_unregister(void) { return RAC_SUCCESS; } + // Clear the provider so subsequent rac_wakeword_initialize calls no + // longer create ONNX backend handles that would outlive us. + (void)rac_wakeword_provider_set(nullptr); + g_wakeword_onnx_registered = false; - RAC_LOG_INFO(LOG_TAG, "Backend unregistered"); + RAC_LOG_INFO(LOG_TAG, "Backend unregistered (wakeword provider cleared)"); return RAC_SUCCESS; } diff --git a/sdk/runanywhere-commons/src/core/rac_core.cpp b/sdk/runanywhere-commons/src/core/rac_core.cpp index 7c91c5ece..764f41201 100644 --- a/sdk/runanywhere-commons/src/core/rac_core.cpp +++ b/sdk/runanywhere-commons/src/core/rac_core.cpp @@ -87,6 +87,20 @@ void rac_log(rac_log_level_t level, const char* category, const char* message) { // INITIALIZATION API // ============================================================================= +// Compile-time layout locks for the versioned ABI. If someone reorders +// rac_platform_adapter_t's fields or changes the first-field type, these +// static_asserts will fail at compile time - catching the mistake before +// it becomes a runtime adapter misalignment across Swift/Kotlin/RN/Flutter/Web. +// +// - offsetof(version) must be 0 so rac_init can read it at a known location +// even if the rest of the struct evolved. +// - sizeof(version) is locked to 4 bytes (the wire format that SDK consumers +// serialize / write into the struct). +static_assert(offsetof(rac_platform_adapter_t, version) == 0, + "rac_platform_adapter_t.version must be the first field"); +static_assert(sizeof(((rac_platform_adapter_t*)0)->version) == sizeof(uint32_t), + "rac_platform_adapter_t.version must be exactly uint32_t (4 bytes)"); + rac_result_t rac_init(const rac_config_t* config) { std::lock_guard lock(s_init_mutex); @@ -103,6 +117,27 @@ rac_result_t rac_init(const rac_config_t* config) { return RAC_ERROR_ADAPTER_NOT_SET; } + // Adapter ABI version check. Every caller MUST set + // `.version = RAC_PLATFORM_ADAPTER_VERSION` before calling rac_init. + // No grace period - an unversioned adapter is a programming error and + // we'd rather fail loudly at init than hit UB later when the runtime + // tries to call a callback at a wrong offset. + const uint32_t adapter_version = config->platform_adapter->version; + if (adapter_version == 0) { + rac_error_set_details( + "Platform adapter .version is 0. Every caller must set " + "`.version = RAC_PLATFORM_ADAPTER_VERSION` before passing the " + "adapter to rac_init(). If you are using {0}-init, add the " + "assignment explicitly."); + return RAC_ERROR_BACKEND_INCOMPATIBLE_VERSION; + } + if (adapter_version > RAC_PLATFORM_ADAPTER_VERSION) { + rac_error_set_details( + "Platform adapter version is newer than this runtime supports. " + "Upgrade runanywhere-commons or set .version to a supported value."); + return RAC_ERROR_BACKEND_INCOMPATIBLE_VERSION; + } + // Store configuration s_platform_adapter = config->platform_adapter; s_log_level = config->log_level; diff --git a/sdk/runanywhere-commons/src/core/rac_hardware.cpp b/sdk/runanywhere-commons/src/core/rac_hardware.cpp new file mode 100644 index 000000000..5c9ec6ea5 --- /dev/null +++ b/sdk/runanywhere-commons/src/core/rac_hardware.cpp @@ -0,0 +1,267 @@ +/** + * @file rac_hardware.cpp + * @brief Hardware capability detection implementation. + * + * Compile-time checks for SIMD feature flags (via __has_include / + * preprocessor macros set by the build system), then a small amount of + * runtime probing for counts (logical cores, GPU memory). + * + * Never throws; if a probe fails (e.g. sysctl on a minimal Linux), the + * corresponding field is left at 0 / RAC_FALSE and the caller gets a + * partial report instead of an error. + */ + +#include "rac/core/rac_hardware.h" + +#include +#include +#include + +#include "rac/core/rac_error.h" +#include "rac/core/rac_logger.h" + +// ---- Platform includes for probing --------------------------------------- +#if defined(__APPLE__) +#include +#include +#include +#endif + +#if defined(__linux__) || defined(__ANDROID__) +#include +#endif + +#if defined(_WIN32) +#include +#include +#endif + +// ---- SIMD detection helpers --------------------------------------------- +// These are compile-time constants by design. Runtime CPUID probing would +// be needed to say "binary supports AVX2 but running CPU doesn't" on x86; +// today we report what the binary was compiled with. + +namespace { + +constexpr rac_bool_t kHasNeon = +#if defined(__ARM_NEON) || defined(__ARM_NEON__) || defined(__aarch64__) + RAC_TRUE; +#else + RAC_FALSE; +#endif + +constexpr rac_bool_t kHasSSE42 = +#if defined(__SSE4_2__) + RAC_TRUE; +#else + RAC_FALSE; +#endif + +constexpr rac_bool_t kHasAVX = +#if defined(__AVX__) + RAC_TRUE; +#else + RAC_FALSE; +#endif + +constexpr rac_bool_t kHasAVX2 = +#if defined(__AVX2__) + RAC_TRUE; +#else + RAC_FALSE; +#endif + +constexpr rac_bool_t kHasAVX512 = +#if defined(__AVX512F__) || defined(__AVX512BW__) || defined(__AVX512VL__) + RAC_TRUE; +#else + RAC_FALSE; +#endif + +constexpr rac_bool_t kHasMetal = +#if defined(__APPLE__) && (defined(GGML_USE_METAL) || defined(RAC_ENABLE_METAL)) + RAC_TRUE; +#else + RAC_FALSE; +#endif + +constexpr rac_bool_t kHasCUDA = +#if defined(GGML_USE_CUDA) || defined(RAC_ENABLE_CUDA) + RAC_TRUE; +#else + RAC_FALSE; +#endif + +constexpr rac_bool_t kHasVulkan = +#if defined(GGML_USE_VULKAN) || defined(RAC_ENABLE_VULKAN) + RAC_TRUE; +#else + RAC_FALSE; +#endif + +constexpr rac_bool_t kHasOpenCL = +#if defined(GGML_USE_OPENCL) || defined(RAC_ENABLE_OPENCL) + RAC_TRUE; +#else + RAC_FALSE; +#endif + +constexpr rac_bool_t kHasWebGPU = +#if defined(__EMSCRIPTEN__) && defined(RAC_WASM_WEBGPU) + RAC_TRUE; +#else + RAC_FALSE; +#endif + +constexpr rac_bool_t kHasANE = +#if defined(__APPLE__) + // Apple Silicon and A-series from A11 onward ship the Neural Engine; + // Core ML dispatches to it automatically. We can't cheaply introspect + // "is ANE actually present" - it effectively is on every modern Apple + // target commons builds for, so we trust the target. + RAC_TRUE; +#else + RAC_FALSE; +#endif + +constexpr rac_bool_t kHasQNN = +#if defined(RAC_ENABLE_QNN) + RAC_TRUE; +#else + RAC_FALSE; +#endif + +constexpr rac_bool_t kHasGenio = +#if defined(RAC_ENABLE_GENIO) + RAC_TRUE; +#else + RAC_FALSE; +#endif + +constexpr rac_bool_t kIsAppleSilicon = +#if defined(__APPLE__) && defined(__aarch64__) + RAC_TRUE; +#else + RAC_FALSE; +#endif + +constexpr rac_bool_t kIsIosSimulator = +#if defined(__APPLE__) && TARGET_OS_SIMULATOR + RAC_TRUE; +#else + RAC_FALSE; +#endif + +int32_t probe_logical_cpus() { +#if defined(_WIN32) + SYSTEM_INFO si; + GetSystemInfo(&si); + return static_cast(si.dwNumberOfProcessors); +#else + long n = sysconf(_SC_NPROCESSORS_ONLN); + return n > 0 ? static_cast(n) : 0; +#endif +} + +int32_t probe_physical_cpus() { +#if defined(__APPLE__) + int32_t count = 0; + size_t size = sizeof(count); + if (sysctlbyname("hw.physicalcpu", &count, &size, nullptr, 0) == 0) { + return count; + } + return 0; +#else + // On Linux the physical count requires parsing /proc/cpuinfo which is + // messy; fall back to logical count. Accurate-enough for our purposes + // (used for thread pool sizing hints). + return probe_logical_cpus(); +#endif +} + +int64_t probe_total_gpu_memory_mb() { + // Cheap probes only; not a goal to be exhaustive. +#if defined(__APPLE__) && defined(__aarch64__) + // Apple Silicon is unified memory: GPU can access up to ~75% of RAM. + // Estimate from hw.memsize. + int64_t ram_bytes = 0; + size_t size = sizeof(ram_bytes); + if (sysctlbyname("hw.memsize", &ram_bytes, &size, nullptr, 0) == 0) { + return (ram_bytes / (1024 * 1024)) * 3 / 4; + } +#endif + return 0; // Unknown / not probed. +} + +} // namespace + +extern "C" { + +rac_result_t rac_hardware_query_capabilities(rac_hardware_report_t* out_report) { + if (out_report == nullptr) { + return RAC_ERROR_INVALID_ARGUMENT; + } + if (out_report->version != RAC_HARDWARE_REPORT_V1) { + RAC_LOG_ERROR("Hardware", + "Report struct version mismatch: caller expects %u, " + "runtime supports %u. Refusing to fill struct.", + out_report->version, (unsigned)RAC_HARDWARE_REPORT_V1); + return RAC_ERROR_BACKEND_INCOMPATIBLE_VERSION; + } + + out_report->has_neon = kHasNeon; + out_report->has_sse42 = kHasSSE42; + out_report->has_avx = kHasAVX; + out_report->has_avx2 = kHasAVX2; + out_report->has_avx512 = kHasAVX512; + out_report->num_logical_cpus = probe_logical_cpus(); + out_report->num_physical_cpus = probe_physical_cpus(); + + out_report->has_metal = kHasMetal; + out_report->has_cuda = kHasCUDA; + out_report->has_vulkan = kHasVulkan; + out_report->has_opencl = kHasOpenCL; + out_report->has_webgpu = kHasWebGPU; + out_report->total_gpu_memory_mb = probe_total_gpu_memory_mb(); + + out_report->has_ane = kHasANE; + out_report->has_qnn = kHasQNN; + out_report->has_genio = kHasGenio; + + out_report->is_apple_silicon = kIsAppleSilicon; + out_report->is_ios_simulator = kIsIosSimulator; + out_report->is_android_emulator = RAC_FALSE; // TODO: detect via ro.kernel.qemu + + std::memset(out_report->_reserved, 0, sizeof(out_report->_reserved)); + return RAC_SUCCESS; +} + +char* rac_hardware_format_report(const rac_hardware_report_t* report) { + if (report == nullptr) { + return rac_strdup("(null hardware report)"); + } + std::string s; + char buf[512]; + + std::snprintf(buf, sizeof(buf), + "Hardware capabilities (version %u):\n" + " CPU: logical=%d physical=%d\n" + " SIMD: neon=%d sse42=%d avx=%d avx2=%d avx512=%d\n" + " GPU: metal=%d cuda=%d vulkan=%d opencl=%d webgpu=%d mem_mb=%lld\n" + " NPU: ane=%d qnn=%d genio=%d\n" + " Platform: apple_silicon=%d ios_simulator=%d android_emulator=%d\n", + report->version, + report->num_logical_cpus, report->num_physical_cpus, + (int)report->has_neon, (int)report->has_sse42, + (int)report->has_avx, (int)report->has_avx2, (int)report->has_avx512, + (int)report->has_metal, (int)report->has_cuda, + (int)report->has_vulkan, (int)report->has_opencl, (int)report->has_webgpu, + (long long)report->total_gpu_memory_mb, + (int)report->has_ane, (int)report->has_qnn, (int)report->has_genio, + (int)report->is_apple_silicon, (int)report->is_ios_simulator, + (int)report->is_android_emulator); + s += buf; + return rac_strdup(s.c_str()); +} + +} // extern "C" diff --git a/sdk/runanywhere-commons/src/features/rag/CMakeLists.txt b/sdk/runanywhere-commons/src/features/rag/CMakeLists.txt index 222bee7dc..82648fc73 100644 --- a/sdk/runanywhere-commons/src/features/rag/CMakeLists.txt +++ b/sdk/runanywhere-commons/src/features/rag/CMakeLists.txt @@ -85,6 +85,11 @@ target_include_directories(rac_backend_rag PUBLIC ${CMAKE_SOURCE_DIR}/include/rac/backends ${usearch_SOURCE_DIR}/include ) +# Internal headers (e.g. internal/rac_platform_compat.h used by +# onnx_embedding_provider.cpp). PRIVATE - never leaks to consumers. +target_include_directories(rac_backend_rag PRIVATE + ${CMAKE_SOURCE_DIR}/src +) # nlohmann_json is used by RAG for config parsing. # It's header-only (INTERFACE lib) so no link-time cost. diff --git a/sdk/runanywhere-commons/src/features/rag/onnx_embedding_provider.cpp b/sdk/runanywhere-commons/src/features/rag/onnx_embedding_provider.cpp index 23f2eef4c..d0cf1ec84 100644 --- a/sdk/runanywhere-commons/src/features/rag/onnx_embedding_provider.cpp +++ b/sdk/runanywhere-commons/src/features/rag/onnx_embedding_provider.cpp @@ -6,7 +6,7 @@ #include "onnx_embedding_provider.h" #include "rac/features/rag/ort_guards.h" #include "rac/core/rac_logger.h" -#include "rac/core/rac_platform_compat.h" +#include "internal/rac_platform_compat.h" #include "../../backends/onnx/onnx_backend.h" #include diff --git a/sdk/runanywhere-commons/src/features/vlm/vlm_component.cpp b/sdk/runanywhere-commons/src/features/vlm/vlm_component.cpp index f9bcfbf07..35af5a304 100644 --- a/sdk/runanywhere-commons/src/features/vlm/vlm_component.cpp +++ b/sdk/runanywhere-commons/src/features/vlm/vlm_component.cpp @@ -12,7 +12,7 @@ #include #include #include -#include "rac/core/rac_platform_compat.h" +#include "internal/rac_platform_compat.h" #include "rac/core/capabilities/rac_lifecycle.h" #include "rac/core/rac_core.h" diff --git a/sdk/runanywhere-commons/src/features/wakeword/wakeword_service.cpp b/sdk/runanywhere-commons/src/features/wakeword/wakeword_service.cpp index 469ba9895..35c931c2c 100644 --- a/sdk/runanywhere-commons/src/features/wakeword/wakeword_service.cpp +++ b/sdk/runanywhere-commons/src/features/wakeword/wakeword_service.cpp @@ -23,6 +23,23 @@ namespace rac { namespace wakeword { +// ============================================================================= +// PROVIDER REGISTRY +// ============================================================================= +// +// Holds the currently-registered provider vtable. Set once at startup by the +// chosen wakeword backend (typically the ONNX backend) via +// rac_wakeword_provider_set(). See rac_wakeword_service.h for design notes. +// +// Reads on the hot path (rac_wakeword_process) are lock-free via atomic; +// writes go through rac_wakeword_provider_set which is expected to run at +// SDK init before any service is live. +// ============================================================================= + +namespace { +std::atomic g_provider{nullptr}; +} // namespace + // ============================================================================= // INTERNAL TYPES // ============================================================================= @@ -141,6 +158,35 @@ RAC_API rac_result_t rac_wakeword_initialize(rac_handle_t handle, // Reserve audio buffer service->audio_buffer.reserve(service->samples_per_frame * 2); + // Create the backend handle via the registered provider, if any. If no + // provider is registered the service still initializes successfully; + // process() will become a no-op until a provider is wired in. + const auto* provider = g_provider.load(std::memory_order_acquire); + if (provider && provider->create) { + rac_handle_t backend_handle = nullptr; + rac_result_t rc = + provider->create(&service->config, &backend_handle, provider->user_data); + if (rc != RAC_SUCCESS) { + RAC_LOG_ERROR("WakeWord", + "Provider create failed: %d. Service will be inert until " + "a new provider is registered or the config is fixed.", + rc); + // Intentionally continue - we still mark initialized so the + // rest of the API (start/stop/load_model-with-no-backend) works + // in a degraded mode. load_model calls will surface the error + // at provider dispatch time. + } else { + service->backend_handle = backend_handle; + RAC_LOG_INFO("WakeWord", "Provider backend handle created"); + } + } else { + RAC_LOG_WARNING("WakeWord", + "No wake-word provider registered. Service will accept " + "load_model() but rac_wakeword_process() will not fire " + "detections until a backend registers via " + "rac_wakeword_provider_set()."); + } + service->initialized = true; service->stream_start_time = get_timestamp_ms(); @@ -159,13 +205,18 @@ RAC_API void rac_wakeword_destroy(rac_handle_t handle) { // Stop if running if (service->listening) { - rac_wakeword_stop(handle); + (void)rac_wakeword_stop(handle); } - // TODO: Destroy backend handle when implemented - // if (service->backend_handle) { - // rac_wakeword_onnx_destroy(service->backend_handle); - // } + // Tear down the backend handle via the registered provider, if any. + if (service->backend_handle) { + const auto* provider = + g_provider.load(std::memory_order_acquire); + if (provider && provider->destroy) { + provider->destroy(service->backend_handle, provider->user_data); + } + service->backend_handle = nullptr; + } delete service; } @@ -202,16 +253,42 @@ RAC_API rac_result_t rac_wakeword_load_model(rac_handle_t handle, } } - // Add model entry + // Dispatch the load to the registered provider backend before we commit + // the model to the in-memory roster. If there's no backend or the backend + // refuses the model, we surface the error; we don't want the service to + // look like it has a loaded model when the backend actually doesn't. + const auto* provider = g_provider.load(std::memory_order_acquire); + bool actually_loaded = false; + if (provider && provider->load_model) { + if (!service->backend_handle) { + RAC_LOG_ERROR("WakeWord", + "load_model: provider is registered but no backend " + "handle exists (create() must have failed at init)."); + return RAC_ERROR_BACKEND_NOT_READY; + } + rac_result_t rc = provider->load_model(service->backend_handle, model_path, + model_id, wake_word, provider->user_data); + if (rc != RAC_SUCCESS) { + RAC_LOG_ERROR("WakeWord", "Provider load_model(%s) failed: %d", + model_id, rc); + return rc; + } + actually_loaded = true; + } + // If no provider is registered we still record the model entry so the + // service layer's bookkeeping remains consistent with the user's API + // calls; the entry will simply be unable to fire detections. + LoadedModel model; model.model_id = model_id; model.wake_word = wake_word; model.model_path = model_path; - model.is_loaded = true; // TODO: Actually load via backend + model.is_loaded = actually_loaded; service->models.push_back(model); - RAC_LOG_INFO("WakeWord", "Loaded model: %s ('%s')", model_id, wake_word); + RAC_LOG_INFO("WakeWord", "Loaded model: %s ('%s'), backend_loaded=%s", + model_id, wake_word, actually_loaded ? "true" : "false"); return RAC_SUCCESS; } @@ -229,10 +306,29 @@ RAC_API rac_result_t rac_wakeword_load_vad(rac_handle_t handle, return RAC_ERROR_WAKEWORD_NOT_INITIALIZED; } + const auto* provider = g_provider.load(std::memory_order_acquire); + if (provider && provider->load_vad) { + if (!service->backend_handle) { + RAC_LOG_ERROR("WakeWord", "load_vad: no backend handle (create failed?)"); + return RAC_ERROR_BACKEND_NOT_READY; + } + rac_result_t rc = provider->load_vad(service->backend_handle, vad_model_path, + provider->user_data); + if (rc != RAC_SUCCESS) { + RAC_LOG_ERROR("WakeWord", "Provider load_vad failed: %d", rc); + return rc; + } + service->vad_loaded = true; + } else { + // No provider or provider doesn't implement VAD: record the path so + // a later rac_wakeword_provider_set() can pick it up if needed, but + // flag not-loaded so the process loop knows not to expect VAD input. + service->vad_loaded = false; + } service->vad_model_path = vad_model_path; - service->vad_loaded = true; // TODO: Actually load via backend - RAC_LOG_INFO("WakeWord", "Loaded VAD model: %s", vad_model_path); + RAC_LOG_INFO("WakeWord", "Loaded VAD model: %s (backend_loaded=%s)", + vad_model_path, service->vad_loaded ? "true" : "false"); return RAC_SUCCESS; } @@ -255,6 +351,21 @@ RAC_API rac_result_t rac_wakeword_unload_model(rac_handle_t handle, return RAC_ERROR_WAKEWORD_MODEL_NOT_FOUND; } + // Let the provider drop its reference to the model first (frees ORT + // session / mem pools). Do this before erasing the model from our + // roster so in-flight diagnostics can still name the model. + const auto* provider = g_provider.load(std::memory_order_acquire); + if (provider && provider->unload_model && service->backend_handle) { + rac_result_t rc = provider->unload_model(service->backend_handle, model_id, + provider->user_data); + if (rc != RAC_SUCCESS) { + RAC_LOG_WARNING("WakeWord", + "Provider unload_model(%s) returned %d (proceeding with " + "service-layer unload anyway).", + model_id, rc); + } + } + service->models.erase(it); RAC_LOG_INFO("WakeWord", "Unloaded model: %s", model_id); @@ -416,7 +527,17 @@ RAC_API rac_result_t rac_wakeword_reset(rac_handle_t handle) { service->audio_buffer.clear(); service->last_detection_time = 0; - // TODO: Reset backend state + // Forward the reset to the provider so sliding windows / KV caches / + // VAD state get cleared too. Failure here is non-fatal for the service + // layer - the audio buffer has already been reset locally. + const auto* provider = g_provider.load(std::memory_order_acquire); + if (provider && provider->reset && service->backend_handle) { + rac_result_t rc = + provider->reset(service->backend_handle, provider->user_data); + if (rc != RAC_SUCCESS) { + RAC_LOG_WARNING("WakeWord", "Provider reset returned %d (non-fatal)", rc); + } + } return RAC_SUCCESS; } @@ -474,28 +595,58 @@ RAC_API rac_result_t rac_wakeword_process(rac_handle_t handle, service->audio_buffer.begin() + service->samples_per_frame ); - // TODO: Process through ONNX backend - // For now, simulate with placeholder + // Defaults: no detection, "speech" = true (so that processing + // proceeds when no VAD is configured). These get overwritten by + // the provider if one is registered. bool detected = false; int32_t keyword_index = -1; float confidence = 0.0f; - bool vad_speech = true; // Assume speech if no VAD + rac_bool_t vad_speech_rac = RAC_TRUE; float vad_prob = 1.0f; - // VAD pre-filtering (would call backend) - if (service->config.use_vad_filter && service->vad_loaded) { - // TODO: Run VAD inference - // vad_speech = rac_wakeword_onnx_vad_process(...) + const auto* provider = g_provider.load(std::memory_order_acquire); + if (provider && provider->process && service->backend_handle) { + int32_t det_idx = -1; + float det_conf = 0.0f; + rac_bool_t vad_flag = RAC_TRUE; + float vad_conf = 1.0f; + rac_result_t rc = provider->process( + service->backend_handle, + frame.data(), frame.size(), + &det_idx, &det_conf, + &vad_flag, &vad_conf, + provider->user_data); + if (rc == RAC_SUCCESS) { + detected = (det_idx >= 0); + keyword_index = det_idx; + confidence = det_conf; + vad_speech_rac = vad_flag; + vad_prob = vad_conf; + } else { + // Rate-limit this warning: a provider that's briefly unhealthy + // shouldn't spam the log once per 80ms frame. Log at debug + // level only; the caller sees the error via reduced detection + // rate. + RAC_LOG_DEBUG("WakeWord", + "Provider process returned %d; dropping frame", rc); + } } + // else: no provider -> the frame is accumulated but inference + // doesn't fire. Callers can detect this via rac_wakeword_has_provider(). + + const bool vad_speech = (vad_speech_rac == RAC_TRUE); // Copy VAD callback under lock to invoke outside lock (avoid deadlock) auto vad_cb = service->vad_callback; auto vad_ud = service->vad_user_data; - // Only run wake word detection if VAD detects speech - if (!service->config.use_vad_filter || vad_speech) { - // TODO: Run wake word inference for each model - // detected = rac_wakeword_onnx_process(...) + // Filtering: if VAD is enabled but no speech detected, suppress + // the wake-word detection. If VAD is disabled or no provider + // supports it, we treat every frame as speech. + if (service->config.use_vad_filter && service->vad_loaded && !vad_speech) { + detected = false; + keyword_index = -1; + confidence = 0.0f; } // Update result @@ -603,6 +754,18 @@ RAC_API rac_result_t rac_wakeword_set_threshold(rac_handle_t handle, float thres service->config.threshold = threshold; + // Propagate to the backend if one is registered. + const auto* provider = g_provider.load(std::memory_order_acquire); + if (provider && provider->set_threshold && service->backend_handle) { + rac_result_t rc = provider->set_threshold(service->backend_handle, threshold, + provider->user_data); + if (rc != RAC_SUCCESS) { + RAC_LOG_WARNING("WakeWord", + "Provider set_threshold returned %d; service-level " + "threshold cache is updated regardless.", rc); + } + } + return RAC_SUCCESS; } @@ -682,6 +845,26 @@ RAC_API rac_bool_t rac_wakeword_is_listening(rac_handle_t handle) { return get_service(handle)->listening ? RAC_TRUE : RAC_FALSE; } +// ============================================================================= +// PROVIDER REGISTRATION +// ============================================================================= + +RAC_API rac_result_t rac_wakeword_provider_set(const rac_wakeword_provider_ops_t* ops) { + // Intentionally memory_order_release: services that later call + // g_provider.load(acquire) will see the fully-initialized ops struct. + g_provider.store(ops, std::memory_order_release); + if (ops) { + RAC_LOG_INFO("WakeWord", "Provider registered"); + } else { + RAC_LOG_INFO("WakeWord", "Provider cleared"); + } + return RAC_SUCCESS; +} + +RAC_API rac_bool_t rac_wakeword_has_provider(void) { + return (g_provider.load(std::memory_order_acquire) != nullptr) ? RAC_TRUE : RAC_FALSE; +} + } // extern "C" } // namespace wakeword diff --git a/sdk/runanywhere-commons/src/infrastructure/download/download_manager.cpp b/sdk/runanywhere-commons/src/infrastructure/download/download_manager.cpp index 9065e7032..aeb470a25 100644 --- a/sdk/runanywhere-commons/src/infrastructure/download/download_manager.cpp +++ b/sdk/runanywhere-commons/src/infrastructure/download/download_manager.cpp @@ -61,9 +61,13 @@ struct rac_download_manager { // Thread safety std::mutex mutex; - // Health state - bool is_healthy; - bool is_paused; + // Health state. These flags are read without the mutex held on the hot + // path (e.g. rac_download_manager_start_download consults is_paused + // before acquiring the mutex), and they may be written by the pause / + // resume / set-healthy entry points on any thread. They MUST be + // std::atomic to avoid torn reads and data races flagged by TSan. + std::atomic is_healthy{true}; + std::atomic is_paused{false}; }; // Note: rac_strdup is declared in rac_types.h and implemented in rac_memory.cpp diff --git a/sdk/runanywhere-commons/src/infrastructure/download/download_orchestrator.cpp b/sdk/runanywhere-commons/src/infrastructure/download/download_orchestrator.cpp index e90266426..cac19aa9a 100644 --- a/sdk/runanywhere-commons/src/infrastructure/download/download_orchestrator.cpp +++ b/sdk/runanywhere-commons/src/infrastructure/download/download_orchestrator.cpp @@ -22,7 +22,7 @@ #include #include #include -#include "rac/core/rac_platform_compat.h" +#include "internal/rac_platform_compat.h" #ifdef _WIN32 #include // for _mkdir diff --git a/sdk/runanywhere-commons/src/infrastructure/extraction/rac_extraction.cpp b/sdk/runanywhere-commons/src/infrastructure/extraction/rac_extraction.cpp index cb6e5fea9..107a26e39 100644 --- a/sdk/runanywhere-commons/src/infrastructure/extraction/rac_extraction.cpp +++ b/sdk/runanywhere-commons/src/infrastructure/extraction/rac_extraction.cpp @@ -15,7 +15,7 @@ #include #include #include -#include "rac/core/rac_platform_compat.h" +#include "internal/rac_platform_compat.h" #ifdef _WIN32 #include // for _mkdir diff --git a/sdk/runanywhere-commons/include/rac/core/rac_platform_compat.h b/sdk/runanywhere-commons/src/internal/rac_platform_compat.h similarity index 64% rename from sdk/runanywhere-commons/include/rac/core/rac_platform_compat.h rename to sdk/runanywhere-commons/src/internal/rac_platform_compat.h index 4af05effe..6a0a213a2 100644 --- a/sdk/runanywhere-commons/include/rac/core/rac_platform_compat.h +++ b/sdk/runanywhere-commons/src/internal/rac_platform_compat.h @@ -1,51 +1,25 @@ /** - * @file rac_platform_compat.h - * @brief RunAnywhere Commons - Platform Compatibility Layer + * @file src/internal/rac_platform_compat.h + * @brief RunAnywhere Commons - Internal Platform Compatibility Layer * * Provides POSIX-like APIs on Windows (MSVC) so that the rest of the codebase * can use dirent.h, S_ISDIR, S_ISREG, etc. without #ifdef clutter. * * On non-Windows platforms this header is a no-op passthrough. * - * ----------------------------------------------------------------------------- - * TODO(future): Move this shim out of the public include path. - * ----------------------------------------------------------------------------- - * Flagged in PR #383 review (coderabbitai): this header currently lives under - * `include/rac/core/` which means any SDK consumer that pulls a commons public - * header transitively inherits *un-prefixed* global names — `DIR`, `dirent`, - * `opendir`, `readdir`, `closedir`, `strcasecmp`, `strncasecmp`, and the - * `S_IS*` / `S_IFLNK` macros. That: - * 1. Breaks the project's "all public symbols must be `rac_` prefixed" rule - * (see `sdk/runanywhere-commons/CLAUDE.md`). - * 2. Can collide with a consumer's own dirent shim or the platform's real - * headers if they include in a different order. - * Impact is Windows-only in practice (POSIX platforms just pass through to - * system headers), but it's still a leaky public contract. + * INTERNAL ONLY. Not installed. Not visible to SDK consumers. * - * Options for the cleanup: - * A) Move the implementation to `src/internal/rac_platform_compat.h` so it's - * never installed / never visible to consumers. All current call sites - * would need their `#include` path updated. This is the preferred fix. - * B) Keep the header public but rename every exposed symbol to `rac_*` - * (`rac_opendir`, `rac_readdir`, `rac_dirent`, `rac_strcasecmp`, …) and - * update every call site. More invasive in source but keeps drop-in - * POSIX-ish semantics; less aligned with the project rule. + * Header history: + * This file used to live under `include/rac/core/rac_platform_compat.h` + * which leaked un-prefixed global names - `DIR`, `dirent`, `opendir`, + * `readdir`, `closedir`, `strcasecmp`, `strncasecmp`, `S_IS*`, `S_IFLNK` - + * into the public namespace on Windows. That violated the project's + * "all public symbols must be `rac_` prefixed" rule (PR #383 review + * comment). Moved to `src/internal/` so consumers never see it. * - * Current call sites to update (option A or B): - * - src/features/vlm/vlm_component.cpp - * - src/features/rag/onnx_embedding_provider.cpp - * - src/features/result_free.cpp - * - src/backends/onnx/onnx_backend.cpp - * - src/backends/onnx/wakeword_onnx.cpp - * - src/infrastructure/download/download_orchestrator.cpp - * - src/infrastructure/extraction/rac_extraction.cpp - * - src/infrastructure/telemetry/telemetry_json.cpp - * - tests/test_extraction.cpp, tests/test_download_orchestrator.cpp, tests/test_common.h - * - Any new Windows-facing file that uses opendir/stat/etc. - * - * Deferred because it's orthogonal to the "make Windows build work" goal. - * Deferring is safe: the pollution only manifests on Windows, and today no - * external consumer builds commons on Windows yet. + * Callers use `#include "internal/rac_platform_compat.h"` because the + * PRIVATE include root is the src/ dir (configured in top-level + * CMakeLists.txt via target_include_directories(rac_commons PRIVATE ...)). */ #ifndef RAC_PLATFORM_COMPAT_H diff --git a/sdk/runanywhere-commons/src/jni/jni_scope.h b/sdk/runanywhere-commons/src/jni/jni_scope.h new file mode 100644 index 000000000..2fc219fb8 --- /dev/null +++ b/sdk/runanywhere-commons/src/jni/jni_scope.h @@ -0,0 +1,255 @@ +/** + * @file src/jni/jni_scope.h + * @brief RAII helpers for safe, exception-aware JNI calls + * + * Problem + * ------- + * Every JNIEnv method that crosses into Java/Kotlin can leave a pending + * exception on the JNIEnv when the Java callee throws (OutOfMemoryError + * from NewStringUTF, NullPointerException from an adapter's + * callBooleanMethod, any user-thrown RuntimeException, etc.). JNI rules + * require that pending exception to be explicitly checked and cleared + * (ExceptionCheck / ExceptionClear); otherwise subsequent JNI calls from + * the same native frame exhibit undefined behaviour. Before this file, + * runanywhere_commons_jni.cpp had 149 JNIEXPORT entry points but only + * 11 ExceptionCheck() calls and 76 unprotected NewStringUTF / 33 + * unprotected CallXxxMethod sites - an ~93% gap that could corrupt the + * Android runtime on the first Java-side exception. + * + * Solution + * -------- + * JniScope is a stack-scoped RAII helper that: + * - Wraps every throwing JNIEnv call with an automatic ExceptionCheck. + * - If an exception is pending, it logs (category, message, stack + * trace via ExceptionDescribe when debug is enabled), clears it, + * and returns RAC_ERROR_JNI_EXCEPTION / nullptr. The caller then + * bails out cleanly instead of racing into another JNIEnv call. + * - Owns any jstring / jobject locals it creates, deleting them on + * scope exit (no more manual DeleteLocalRef, no more leaked locals + * on error paths). + * - Is header-only; zero overhead when no exception is thrown. + * + * Usage pattern + * ------------- + * rac_result_t jni_something_callback(...) { + * JNIEnv* env = getJNIEnv(); + * if (env == nullptr) return RAC_ERROR_ADAPTER_NOT_SET; + * JniScope s(env, "file_exists"); + * + * auto jPath = s.new_string_utf(path ? path : ""); + * if (!jPath) return s.result(); // translates pending exception + * + * jboolean b = s.call_boolean_method(g_platform_adapter, + * g_method_file_exists, jPath.get()); + * if (s.failed()) return s.result(); + * + * return b ? RAC_TRUE : RAC_FALSE; + * } + * + * The `jPath` local is released automatically when the scope exits; + * s.result() is RAC_SUCCESS unless a prior call raised. + */ + +#ifndef RAC_JNI_SCOPE_H +#define RAC_JNI_SCOPE_H + +#include + +#include +#include +#include + +#include "rac/core/rac_error.h" +#include "rac/core/rac_logger.h" + +namespace rac { +namespace jni { + +// Small RAII wrapper around a jobject local reference. Deletes the local +// ref when the wrapper goes out of scope. Move-only. Implicitly convertible +// to the underlying jobject so it drops into existing JNI APIs naturally. +template +class Local { +public: + Local() = default; + Local(JNIEnv* env, T ref) : env_(env), ref_(ref) {} + Local(const Local&) = delete; + Local& operator=(const Local&) = delete; + Local(Local&& other) noexcept : env_(other.env_), ref_(other.ref_) { + other.env_ = nullptr; + other.ref_ = nullptr; + } + Local& operator=(Local&& other) noexcept { + if (this != &other) { + reset(); + env_ = other.env_; + ref_ = other.ref_; + other.env_ = nullptr; + other.ref_ = nullptr; + } + return *this; + } + ~Local() { reset(); } + + T get() const { return ref_; } + explicit operator bool() const { return ref_ != nullptr; } + // Implicit conversion so code can pass the Local directly + // where a jstring is expected. + operator T() const { return ref_; } // NOLINT(google-explicit-constructor) + + T release() { + T r = ref_; + ref_ = nullptr; + env_ = nullptr; + return r; + } + + void reset() { + if (env_ != nullptr && ref_ != nullptr) { + env_->DeleteLocalRef(ref_); + } + env_ = nullptr; + ref_ = nullptr; + } + +private: + JNIEnv* env_ = nullptr; + T ref_ = nullptr; +}; + +// Non-owning scope that auto-checks and clears JNI exceptions after +// every wrapped call. Intended to be stack-local to a single JNI entry +// point or callback. +class JniScope { +public: + // `context` shows up in log messages so an offending call site is + // easy to find in logcat. Keep it short (the function name). + JniScope(JNIEnv* env, const char* context) : env_(env), context_(context) {} + + // Copy/move: scopes are stack-local and should not migrate. + JniScope(const JniScope&) = delete; + JniScope& operator=(const JniScope&) = delete; + + // --- Exception handling ----------------------------------------------- + + // Returns RAC_SUCCESS if no exception has fired in this scope, + // otherwise RAC_ERROR_JNI_EXCEPTION. + rac_result_t result() const { return result_; } + + // Shortcut: did anything go wrong so far? + bool failed() const { return result_ != RAC_SUCCESS; } + + // Manually check for a pending exception (e.g. after raw JNIEnv calls + // made without going through the wrappers below). Logs and clears; + // subsequent calls to result()/failed() will see the failure. + void check(const char* phase = nullptr) { + if (env_ == nullptr || result_ != RAC_SUCCESS) { + return; + } + if (env_->ExceptionCheck() != JNI_TRUE) { + return; + } + // Describe the exception to logcat before clearing - super useful + // for debugging and cheap enough to leave on (only pays cost when + // an exception is actually live). + env_->ExceptionDescribe(); + env_->ExceptionClear(); + RAC_LOG_ERROR("JniScope", + "Java exception in [%s]%s%s - checked and cleared; " + "returning RAC_ERROR_JNI_EXCEPTION", + context_ != nullptr ? context_ : "?", + phase != nullptr ? " @ " : "", + phase != nullptr ? phase : ""); + result_ = RAC_ERROR_JNI_EXCEPTION; + } + + // --- Wrapped JNIEnv calls -------------------------------------------- + + Local new_string_utf(const char* s) { + if (failed() || env_ == nullptr) { + return {}; + } + jstring raw = env_->NewStringUTF(s != nullptr ? s : ""); + check("NewStringUTF"); + return Local(env_, raw); + } + + Local new_byte_array(jsize len) { + if (failed() || env_ == nullptr) { + return {}; + } + jbyteArray raw = env_->NewByteArray(len); + check("NewByteArray"); + return Local(env_, raw); + } + + template + Local call_object_method(jobject obj, jmethodID m, Args... args) { + if (failed() || env_ == nullptr || obj == nullptr || m == nullptr) { + return {}; + } + jobject raw = env_->CallObjectMethod(obj, m, std::forward(args)...); + check("CallObjectMethod"); + return Local(env_, raw); + } + + template + jboolean call_boolean_method(jobject obj, jmethodID m, Args... args) { + if (failed() || env_ == nullptr || obj == nullptr || m == nullptr) { + return JNI_FALSE; + } + jboolean r = env_->CallBooleanMethod(obj, m, std::forward(args)...); + check("CallBooleanMethod"); + return r; + } + + template + jint call_int_method(jobject obj, jmethodID m, Args... args) { + if (failed() || env_ == nullptr || obj == nullptr || m == nullptr) { + return 0; + } + jint r = env_->CallIntMethod(obj, m, std::forward(args)...); + check("CallIntMethod"); + return r; + } + + template + jlong call_long_method(jobject obj, jmethodID m, Args... args) { + if (failed() || env_ == nullptr || obj == nullptr || m == nullptr) { + return 0; + } + jlong r = env_->CallLongMethod(obj, m, std::forward(args)...); + check("CallLongMethod"); + return r; + } + + template + void call_void_method(jobject obj, jmethodID m, Args... args) { + if (failed() || env_ == nullptr || obj == nullptr || m == nullptr) { + return; + } + env_->CallVoidMethod(obj, m, std::forward(args)...); + check("CallVoidMethod"); + } + + JNIEnv* env() const { return env_; } + const char* context() const { return context_; } + +private: + JNIEnv* env_ = nullptr; + const char* context_ = nullptr; + rac_result_t result_ = RAC_SUCCESS; +}; + +} // namespace jni +} // namespace rac + +// Short helpers for the common "bail if JniScope already failed" pattern. +// These let us mechanically replace unchecked raw env->Xxx() calls. + +#define RAC_JNI_TRY(scope) do { if ((scope).failed()) return (scope).result(); } while (0) +#define RAC_JNI_TRY_PTR(scope, ptr_fallback) \ + do { if ((scope).failed()) return (ptr_fallback); } while (0) +#define RAC_JNI_TRY_VOID(scope) do { if ((scope).failed()) return; } while (0) + +#endif // RAC_JNI_SCOPE_H diff --git a/sdk/runanywhere-commons/src/jni/jni_shared.h b/sdk/runanywhere-commons/src/jni/jni_shared.h new file mode 100644 index 000000000..69133f255 --- /dev/null +++ b/sdk/runanywhere-commons/src/jni/jni_shared.h @@ -0,0 +1,104 @@ +/** + * @file src/jni/jni_shared.h + * @brief Shared state for the runanywhere-commons JNI bridge. + * + * INTERNAL ONLY. Not installed, not visible to SDK consumers. + * + * The JNI bridge in `runanywhere_commons_jni.cpp` has grown to ~4,800 + * lines that mix five distinct concerns: + * - JVM / adapter / method-ID registration (Init) + * - Platform adapter callback implementations (PlatformAdapter) + * - Per-feature JNIEXPORT entry points (LLM, STT, TTS, VAD, VLM) + * - Model registry / download orchestration bridges + * - Telemetry and device callback bridges + * + * Phase 7 of the C++ cleanup is a per-feature file split. This header + * is the scaffolding that all future .cpp shards will share: it + * declares the global state (g_jvm, g_platform_adapter, method-ID + * caches) as `extern`, and pulls in jni_scope.h + the RAC public + * headers that every JNI TU needs. The plan is: + * + * runanywhere_commons_jni.cpp (entry point, JNI_OnLoad, rac_init wrapper) + * jni_shared.h / .cpp (this file + the global-state definitions) + * jni_platform_adapter.cpp (~250 LOC, 9 callbacks) + * jni_llm.cpp (LLM JNIEXPORTs) + * jni_stt.cpp (STT JNIEXPORTs) + * jni_tts.cpp (TTS JNIEXPORTs) + * jni_vad_wakeword.cpp (VAD + wakeword JNIEXPORTs) + * jni_vlm.cpp (VLM JNIEXPORTs) + * jni_model_registry.cpp (model assignment / LoRA / download) + * jni_device.cpp (device registration) + * jni_telemetry.cpp (telemetry callbacks + HTTP) + * jni_benchmark.cpp (benchmark JNIEXPORTs) + * + * This commit ships the shared header; the actual file split across + * source files is incremental and can land in subsequent commits so + * each review is manageable. Every shard will `#include "jni_shared.h"` + * as its first internal include. + */ + +#ifndef RAC_JNI_SHARED_H +#define RAC_JNI_SHARED_H + +#include + +#include +#include + +#include "jni_scope.h" + +#include "rac/core/rac_core.h" +#include "rac/core/rac_error.h" +#include "rac/core/rac_logger.h" +#include "rac/core/rac_platform_adapter.h" + +// --- Logging shortcuts (already used throughout runanywhere_commons_jni.cpp) --- +#ifdef __ANDROID__ +#include +#define RAC_JNI_LOG_TAG "RACCommonsJNI" +#define LOGi(...) __android_log_print(ANDROID_LOG_INFO, RAC_JNI_LOG_TAG, __VA_ARGS__) +#define LOGd(...) __android_log_print(ANDROID_LOG_DEBUG, RAC_JNI_LOG_TAG, __VA_ARGS__) +#define LOGw(...) __android_log_print(ANDROID_LOG_WARN, RAC_JNI_LOG_TAG, __VA_ARGS__) +#define LOGe(...) __android_log_print(ANDROID_LOG_ERROR, RAC_JNI_LOG_TAG, __VA_ARGS__) +#else +#include +#define LOGi(...) do { std::fprintf(stdout, "[INFO] " __VA_ARGS__); std::fputc('\n', stdout); } while (0) +#define LOGd(...) do { std::fprintf(stdout, "[DEBUG] " __VA_ARGS__); std::fputc('\n', stdout); } while (0) +#define LOGw(...) do { std::fprintf(stderr, "[WARN] " __VA_ARGS__); std::fputc('\n', stderr); } while (0) +#define LOGe(...) do { std::fprintf(stderr, "[ERROR] " __VA_ARGS__); std::fputc('\n', stderr); } while (0) +#endif + +// --- Global state set by JNI_OnLoad + racSetPlatformAdapter ---------------- +// +// These are DEFINED in runanywhere_commons_jni.cpp (file-scope statics +// today). Shards produced by the phase-7 split will redeclare them as +// `extern` via this header once the definitions move to jni_shared.cpp. +// +// Today this header serves purely as documentation and as a staging point +// for the split; runanywhere_commons_jni.cpp continues to own these as +// `static` until the split lands. We include the forward declarations +// below commented-out so reviewers can see what will become extern. + +// extern JavaVM* g_jvm; +// extern jobject g_platform_adapter; // global ref, DeleteGlobalRef in cleanup +// extern std::mutex g_adapter_mutex; +// extern jmethodID g_method_log; +// extern jmethodID g_method_file_exists; +// extern jmethodID g_method_file_read; +// extern jmethodID g_method_file_write; +// extern jmethodID g_method_file_delete; +// extern jmethodID g_method_secure_get; +// extern jmethodID g_method_secure_set; +// extern jmethodID g_method_secure_delete; +// extern jmethodID g_method_now_ms; + +// --- Helpers shared by every TU -------------------------------------------- + +/** Get a JNIEnv for the current thread, attaching if needed. Implementation + * stays in runanywhere_commons_jni.cpp for now. */ +JNIEnv* getJNIEnv(); + +/** Copy a jstring to a std::string. Empty string on null input. */ +std::string getCString(JNIEnv* env, jstring str); + +#endif // RAC_JNI_SHARED_H diff --git a/sdk/runanywhere-commons/src/jni/runanywhere_commons_jni.cpp b/sdk/runanywhere-commons/src/jni/runanywhere_commons_jni.cpp index fa5e47e79..e050ced10 100644 --- a/sdk/runanywhere-commons/src/jni/runanywhere_commons_jni.cpp +++ b/sdk/runanywhere-commons/src/jni/runanywhere_commons_jni.cpp @@ -27,6 +27,9 @@ #include #endif +// RAII JNI exception-safety helpers. See jni_scope.h for design rationale. +#include "jni_scope.h" + // Include runanywhere-commons C API headers #include "rac/core/rac_analytics_events.h" #include "rac/core/rac_audio_utils.h" @@ -121,7 +124,12 @@ static JNIEnv* getJNIEnv() { int status = g_jvm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6); if (status == JNI_EDETACHED) { - if (g_jvm->AttachCurrentThread(&env, nullptr) != JNI_OK) { + // AttachCurrentThread's JNIEnv** param is typed as JNIEnv** on + // Android NDK but as void** on macOS/Linux host JDKs. Cast through + // void** - JNIEnv** and void** are layout-compatible on every + // supported platform. + if (g_jvm->AttachCurrentThread( + reinterpret_cast(&env), nullptr) != JNI_OK) { return nullptr; } } @@ -154,7 +162,7 @@ static const char* getNullableCString(JNIEnv* env, jstring str, std::string& sto static rac_platform_adapter_t g_c_adapter; static void jni_log_callback(rac_log_level_t level, const char* tag, const char* message, - void* user_data) { + void* /*user_data*/) { JNIEnv* env = getJNIEnv(); if (env == nullptr || g_platform_adapter == nullptr || g_method_log == nullptr) { // Fallback to direct native logging (NOT through RAC_LOG_* to avoid recursion, @@ -168,162 +176,206 @@ static void jni_log_callback(rac_log_level_t level, const char* tag, const char* return; } - jstring jTag = env->NewStringUTF(tag ? tag : "RAC"); - jstring jMessage = env->NewStringUTF(message ? message : ""); - - env->CallVoidMethod(g_platform_adapter, g_method_log, static_cast(level), jTag, jMessage); - - env->DeleteLocalRef(jTag); - env->DeleteLocalRef(jMessage); + // JniScope handles NewStringUTF failure (OOM) and CallVoidMethod + // exceptions automatically: if either raises on the Java side, the + // pending exception is logged + cleared and we just return. Locals + // (jTag, jMessage) are freed by Local RAII. + rac::jni::JniScope s(env, "jni_log_callback"); + auto jTag = s.new_string_utf(tag != nullptr ? tag : "RAC"); + auto jMessage = s.new_string_utf(message != nullptr ? message : ""); + if (s.failed()) { + return; + } + s.call_void_method(g_platform_adapter, g_method_log, + static_cast(level), jTag.get(), jMessage.get()); } -static rac_bool_t jni_file_exists_callback(const char* path, void* user_data) { +static rac_bool_t jni_file_exists_callback(const char* path, void* /*user_data*/) { JNIEnv* env = getJNIEnv(); if (env == nullptr || g_platform_adapter == nullptr || g_method_file_exists == nullptr) { return RAC_FALSE; } - jstring jPath = env->NewStringUTF(path ? path : ""); - jboolean result = env->CallBooleanMethod(g_platform_adapter, g_method_file_exists, jPath); - env->DeleteLocalRef(jPath); + rac::jni::JniScope s(env, "jni_file_exists_callback"); + auto jPath = s.new_string_utf(path != nullptr ? path : ""); + if (s.failed()) { + return RAC_FALSE; + } + + jboolean result = s.call_boolean_method(g_platform_adapter, g_method_file_exists, jPath.get()); + if (s.failed()) { + return RAC_FALSE; + } - return result ? RAC_TRUE : RAC_FALSE; + return (result != JNI_FALSE) ? RAC_TRUE : RAC_FALSE; } static rac_result_t jni_file_read_callback(const char* path, void** out_data, size_t* out_size, - void* user_data) { + void* /*user_data*/) { JNIEnv* env = getJNIEnv(); if (env == nullptr || g_platform_adapter == nullptr || g_method_file_read == nullptr) { return RAC_ERROR_ADAPTER_NOT_SET; } - jstring jPath = env->NewStringUTF(path ? path : ""); - jbyteArray result = static_cast( - env->CallObjectMethod(g_platform_adapter, g_method_file_read, jPath)); - env->DeleteLocalRef(jPath); + rac::jni::JniScope s(env, "jni_file_read_callback"); + auto jPath = s.new_string_utf(path != nullptr ? path : ""); + RAC_JNI_TRY(s); + + auto result = s.call_object_method(g_platform_adapter, g_method_file_read, jPath.get()); + RAC_JNI_TRY(s); - if (result == nullptr) { + if (!result) { *out_data = nullptr; *out_size = 0; return RAC_ERROR_FILE_NOT_FOUND; } - jsize len = env->GetArrayLength(result); - *out_data = malloc(len); + auto byte_array = static_cast(result.get()); + jsize len = env->GetArrayLength(byte_array); + s.check("GetArrayLength"); + RAC_JNI_TRY(s); + + *out_data = malloc(static_cast(len)); if (*out_data == nullptr) { *out_size = 0; - env->DeleteLocalRef(result); return RAC_ERROR_OUT_OF_MEMORY; } *out_size = static_cast(len); - env->GetByteArrayRegion(result, 0, len, reinterpret_cast(*out_data)); + env->GetByteArrayRegion(byte_array, 0, len, static_cast(*out_data)); + s.check("GetByteArrayRegion"); + if (s.failed()) { + free(*out_data); + *out_data = nullptr; + *out_size = 0; + return s.result(); + } - env->DeleteLocalRef(result); return RAC_SUCCESS; } static rac_result_t jni_file_write_callback(const char* path, const void* data, size_t size, - void* user_data) { + void* /*user_data*/) { JNIEnv* env = getJNIEnv(); if (env == nullptr || g_platform_adapter == nullptr || g_method_file_write == nullptr) { return RAC_ERROR_ADAPTER_NOT_SET; } - jstring jPath = env->NewStringUTF(path ? path : ""); - jbyteArray jData = env->NewByteArray(static_cast(size)); - env->SetByteArrayRegion(jData, 0, static_cast(size), - reinterpret_cast(data)); + rac::jni::JniScope s(env, "jni_file_write_callback"); + auto jPath = s.new_string_utf(path != nullptr ? path : ""); + auto jData = s.new_byte_array(static_cast(size)); + RAC_JNI_TRY(s); - jboolean result = env->CallBooleanMethod(g_platform_adapter, g_method_file_write, jPath, jData); + env->SetByteArrayRegion(jData.get(), 0, static_cast(size), + reinterpret_cast(data)); + s.check("SetByteArrayRegion"); + RAC_JNI_TRY(s); - env->DeleteLocalRef(jPath); - env->DeleteLocalRef(jData); + jboolean result = s.call_boolean_method(g_platform_adapter, g_method_file_write, + jPath.get(), jData.get()); + RAC_JNI_TRY(s); - return result ? RAC_SUCCESS : RAC_ERROR_FILE_WRITE_FAILED; + return (result != JNI_FALSE) ? RAC_SUCCESS : RAC_ERROR_FILE_WRITE_FAILED; } -static rac_result_t jni_file_delete_callback(const char* path, void* user_data) { +static rac_result_t jni_file_delete_callback(const char* path, void* /*user_data*/) { JNIEnv* env = getJNIEnv(); if (env == nullptr || g_platform_adapter == nullptr || g_method_file_delete == nullptr) { return RAC_ERROR_ADAPTER_NOT_SET; } - jstring jPath = env->NewStringUTF(path ? path : ""); - jboolean result = env->CallBooleanMethod(g_platform_adapter, g_method_file_delete, jPath); - env->DeleteLocalRef(jPath); + rac::jni::JniScope s(env, "jni_file_delete_callback"); + auto jPath = s.new_string_utf(path != nullptr ? path : ""); + RAC_JNI_TRY(s); + + jboolean result = s.call_boolean_method(g_platform_adapter, g_method_file_delete, jPath.get()); + RAC_JNI_TRY(s); - return result ? RAC_SUCCESS : RAC_ERROR_FILE_WRITE_FAILED; + return (result != JNI_FALSE) ? RAC_SUCCESS : RAC_ERROR_FILE_WRITE_FAILED; } -static rac_result_t jni_secure_get_callback(const char* key, char** out_value, void* user_data) { +static rac_result_t jni_secure_get_callback(const char* key, char** out_value, void* /*user_data*/) { JNIEnv* env = getJNIEnv(); if (env == nullptr || g_platform_adapter == nullptr || g_method_secure_get == nullptr) { return RAC_ERROR_ADAPTER_NOT_SET; } - jstring jKey = env->NewStringUTF(key ? key : ""); - jstring result = - static_cast(env->CallObjectMethod(g_platform_adapter, g_method_secure_get, jKey)); - env->DeleteLocalRef(jKey); + rac::jni::JniScope s(env, "jni_secure_get_callback"); + auto jKey = s.new_string_utf(key != nullptr ? key : ""); + RAC_JNI_TRY(s); - if (result == nullptr) { + auto result = s.call_object_method(g_platform_adapter, g_method_secure_get, jKey.get()); + RAC_JNI_TRY(s); + + if (!result) { *out_value = nullptr; return RAC_ERROR_NOT_FOUND; } - const char* chars = env->GetStringUTFChars(result, nullptr); - if (!chars) { - env->DeleteLocalRef(result); + auto jstr = static_cast(result.get()); + const char* chars = env->GetStringUTFChars(jstr, nullptr); + s.check("GetStringUTFChars"); + if (chars == nullptr || s.failed()) { *out_value = nullptr; - return RAC_ERROR_INTERNAL; + return s.failed() ? s.result() : RAC_ERROR_INTERNAL; } *out_value = strdup(chars); - env->ReleaseStringUTFChars(result, chars); - env->DeleteLocalRef(result); + env->ReleaseStringUTFChars(jstr, chars); + // result auto-freed by Local RAII on scope exit. - if (!*out_value) { + if (*out_value == nullptr) { return RAC_ERROR_OUT_OF_MEMORY; } return RAC_SUCCESS; } -static rac_result_t jni_secure_set_callback(const char* key, const char* value, void* user_data) { +static rac_result_t jni_secure_set_callback(const char* key, const char* value, + void* /*user_data*/) { JNIEnv* env = getJNIEnv(); if (env == nullptr || g_platform_adapter == nullptr || g_method_secure_set == nullptr) { return RAC_ERROR_ADAPTER_NOT_SET; } - jstring jKey = env->NewStringUTF(key ? key : ""); - jstring jValue = env->NewStringUTF(value ? value : ""); - jboolean result = env->CallBooleanMethod(g_platform_adapter, g_method_secure_set, jKey, jValue); + rac::jni::JniScope s(env, "jni_secure_set_callback"); + auto jKey = s.new_string_utf(key != nullptr ? key : ""); + auto jValue = s.new_string_utf(value != nullptr ? value : ""); + RAC_JNI_TRY(s); - env->DeleteLocalRef(jKey); - env->DeleteLocalRef(jValue); + jboolean result = s.call_boolean_method(g_platform_adapter, g_method_secure_set, + jKey.get(), jValue.get()); + RAC_JNI_TRY(s); - return result ? RAC_SUCCESS : RAC_ERROR_STORAGE_ERROR; + return (result != JNI_FALSE) ? RAC_SUCCESS : RAC_ERROR_STORAGE_ERROR; } -static rac_result_t jni_secure_delete_callback(const char* key, void* user_data) { +static rac_result_t jni_secure_delete_callback(const char* key, void* /*user_data*/) { JNIEnv* env = getJNIEnv(); if (env == nullptr || g_platform_adapter == nullptr || g_method_secure_delete == nullptr) { return RAC_ERROR_ADAPTER_NOT_SET; } - jstring jKey = env->NewStringUTF(key ? key : ""); - jboolean result = env->CallBooleanMethod(g_platform_adapter, g_method_secure_delete, jKey); - env->DeleteLocalRef(jKey); + rac::jni::JniScope s(env, "jni_secure_delete_callback"); + auto jKey = s.new_string_utf(key != nullptr ? key : ""); + RAC_JNI_TRY(s); + + jboolean result = s.call_boolean_method(g_platform_adapter, g_method_secure_delete, jKey.get()); + RAC_JNI_TRY(s); - return result ? RAC_SUCCESS : RAC_ERROR_STORAGE_ERROR; + return (result != JNI_FALSE) ? RAC_SUCCESS : RAC_ERROR_STORAGE_ERROR; } -static int64_t jni_now_ms_callback(void* user_data) { +static int64_t jni_now_ms_callback(void* /*user_data*/) { JNIEnv* env = getJNIEnv(); if (env == nullptr || g_platform_adapter == nullptr || g_method_now_ms == nullptr) { // Fallback to system time return static_cast(time(nullptr)) * 1000; } - return env->CallLongMethod(g_platform_adapter, g_method_now_ms); + rac::jni::JniScope s(env, "jni_now_ms_callback"); + jlong ms = s.call_long_method(g_platform_adapter, g_method_now_ms); + if (s.failed()) { + return static_cast(time(nullptr)) * 1000; + } + return static_cast(ms); } // ============================================================================= @@ -413,8 +465,12 @@ Java_com_runanywhere_sdk_native_bridge_RunAnywhereBridge_racSetPlatformAdapter(J env->DeleteLocalRef(adapterClass); - // Initialize the C adapter struct with our JNI callbacks + // Initialize the C adapter struct with our JNI callbacks. memset(&g_c_adapter, 0, sizeof(g_c_adapter)); + // ABI version - rac_init() rejects adapters where version is 0 or + // > RAC_PLATFORM_ADAPTER_VERSION. Always set to the header's current + // version so we stay compatible across upgrades. + g_c_adapter.version = RAC_PLATFORM_ADAPTER_VERSION; g_c_adapter.log = jni_log_callback; g_c_adapter.file_exists = jni_file_exists_callback; g_c_adapter.file_read = jni_file_read_callback; @@ -739,7 +795,7 @@ static rac_bool_t llm_stream_callback_token(const char* token, void* user_data) jint result = ctx->jvm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6); if (result == JNI_EDETACHED) { - if (ctx->jvm->AttachCurrentThread(&env, nullptr) == JNI_OK) { + if (ctx->jvm->AttachCurrentThread(reinterpret_cast(&env), nullptr) == JNI_OK) { needsDetach = true; } else { LOGe("Failed to attach thread for streaming callback"); @@ -748,30 +804,30 @@ static rac_bool_t llm_stream_callback_token(const char* token, void* user_data) } if (env) { + rac::jni::JniScope js(env, "llm_stream_callback_token"); jboolean continueGen = JNI_TRUE; if (ctx->onTokenExpectsBytes) { jsize len = static_cast(strlen(token)); - jbyteArray jToken = env->NewByteArray(len); - env->SetByteArrayRegion( - jToken, - 0, - len, - reinterpret_cast(token) - ); - continueGen = env->CallBooleanMethod(ctx->callback, ctx->onTokenMethod, jToken); - env->DeleteLocalRef(jToken); + auto jToken = js.new_byte_array(len); + if (!js.failed() && jToken) { + env->SetByteArrayRegion(jToken.get(), 0, len, + reinterpret_cast(token)); + js.check("SetByteArrayRegion"); + if (!js.failed()) { + continueGen = js.call_boolean_method(ctx->callback, + ctx->onTokenMethod, jToken.get()); + } + } } else { - jstring jToken = env->NewStringUTF(token); - continueGen = env->CallBooleanMethod(ctx->callback, ctx->onTokenMethod, jToken); - env->DeleteLocalRef(jToken); + auto jToken = js.new_string_utf(token); + if (!js.failed() && jToken) { + continueGen = js.call_boolean_method(ctx->callback, + ctx->onTokenMethod, jToken.get()); + } } - const bool hadException = env->ExceptionCheck(); - if (hadException) { - env->ExceptionDescribe(); - env->ExceptionClear(); - } + const bool hadException = js.failed(); if (needsDetach) { ctx->jvm->DetachCurrentThread(); @@ -2435,7 +2491,7 @@ static rac_result_t model_assignment_http_get_callback(const char* endpoint, jint get_result = g_model_assignment_state.jvm->GetEnv((void**)&env, JNI_VERSION_1_6); if (get_result == JNI_EDETACHED) { - if (g_model_assignment_state.jvm->AttachCurrentThread(&env, nullptr) == JNI_OK) { + if (g_model_assignment_state.jvm->AttachCurrentThread(reinterpret_cast(&env), nullptr) == JNI_OK) { did_attach = true; } else { LOGe("model_assignment_http_get_callback: failed to attach thread"); @@ -2447,17 +2503,24 @@ static rac_result_t model_assignment_http_get_callback(const char* endpoint, } // Call Kotlin callback: httpGet(endpoint: String, requiresAuth: Boolean): String - jstring jEndpoint = env->NewStringUTF(endpoint ? endpoint : ""); + rac::jni::JniScope s(env, "model_assignment_http_get_callback"); + auto jEndpoint = s.new_string_utf(endpoint != nullptr ? endpoint : ""); jboolean jRequiresAuth = requires_auth == RAC_TRUE ? JNI_TRUE : JNI_FALSE; + if (s.failed()) { + if (did_attach) { + g_model_assignment_state.jvm->DetachCurrentThread(); + } + if (out_response) { + out_response->result = RAC_ERROR_OUT_OF_MEMORY; + } + return RAC_ERROR_OUT_OF_MEMORY; + } - jstring jResponse = - (jstring)env->CallObjectMethod(g_model_assignment_state.callback_obj, - g_model_assignment_state.http_get_method, jEndpoint, jRequiresAuth); + auto response_obj = s.call_object_method(g_model_assignment_state.callback_obj, + g_model_assignment_state.http_get_method, + jEndpoint.get(), jRequiresAuth); - if (env->ExceptionCheck()) { - env->ExceptionClear(); - LOGe("model_assignment_http_get_callback: exception in Kotlin callback"); - env->DeleteLocalRef(jEndpoint); + if (s.failed()) { if (did_attach) { g_model_assignment_state.jvm->DetachCurrentThread(); } @@ -2467,6 +2530,8 @@ static rac_result_t model_assignment_http_get_callback(const char* endpoint, return RAC_ERROR_HTTP_REQUEST_FAILED; } + jstring jResponse = static_cast(response_obj.get()); + rac_result_t result = RAC_SUCCESS; if (jResponse) { const char* response_str = env->GetStringUTFChars(jResponse, nullptr); @@ -2489,7 +2554,7 @@ static rac_result_t model_assignment_http_get_callback(const char* endpoint, } } env->ReleaseStringUTFChars(jResponse, response_str); - env->DeleteLocalRef(jResponse); + // response_obj Local auto-releases jResponse on scope exit. } else { if (out_response) { out_response->result = RAC_ERROR_HTTP_REQUEST_FAILED; @@ -2497,7 +2562,7 @@ static rac_result_t model_assignment_http_get_callback(const char* endpoint, result = RAC_ERROR_HTTP_REQUEST_FAILED; } - env->DeleteLocalRef(jEndpoint); + // jEndpoint Local auto-releases on scope exit. if (did_attach) { g_model_assignment_state.jvm->DetachCurrentThread(); } @@ -2784,24 +2849,19 @@ static struct { } g_device_info_strings = {}; // Device callback implementations -static void jni_device_get_info(rac_device_registration_info_t* out_info, void* user_data) { +static void jni_device_get_info(rac_device_registration_info_t* out_info, void* /*user_data*/) { JNIEnv* env = getJNIEnv(); if (!env || !g_device_jni_state.callback_obj || !g_device_jni_state.get_device_info_method) { LOGe("jni_device_get_info: JNI not ready"); return; } - // Call Java getDeviceInfo() which returns a JSON string - jstring jResult = (jstring)env->CallObjectMethod(g_device_jni_state.callback_obj, - g_device_jni_state.get_device_info_method); + rac::jni::JniScope s(env, "jni_device_get_info"); + auto cb_result = s.call_object_method(g_device_jni_state.callback_obj, + g_device_jni_state.get_device_info_method); + RAC_JNI_TRY_VOID(s); - // Check for Java exception after CallObjectMethod - if (env->ExceptionCheck()) { - LOGe("jni_device_get_info: Java exception occurred in getDeviceInfo()"); - env->ExceptionDescribe(); - env->ExceptionClear(); - return; - } + jstring jResult = static_cast(cb_result.get()); if (jResult && out_info) { const char* json_str = env->GetStringUTFChars(jResult, nullptr); @@ -2886,63 +2946,55 @@ static void jni_device_get_info(rac_device_registration_info_t* out_info, void* out_info->architecture ? out_info->architecture : "(null)"); env->ReleaseStringUTFChars(jResult, json_str); - env->DeleteLocalRef(jResult); + // cb_result Local releases jResult on scope exit. } } -static const char* jni_device_get_id(void* user_data) { +static const char* jni_device_get_id(void* /*user_data*/) { JNIEnv* env = getJNIEnv(); if (!env || !g_device_jni_state.callback_obj || !g_device_jni_state.get_device_id_method) { LOGe("jni_device_get_id: JNI not ready"); return ""; } - jstring jResult = (jstring)env->CallObjectMethod(g_device_jni_state.callback_obj, - g_device_jni_state.get_device_id_method); + rac::jni::JniScope s(env, "jni_device_get_id"); + auto cb_result = s.call_object_method(g_device_jni_state.callback_obj, + g_device_jni_state.get_device_id_method); + RAC_JNI_TRY_PTR(s, ""); - // Check for Java exception after CallObjectMethod - if (env->ExceptionCheck()) { - LOGe("jni_device_get_id: Java exception occurred in getDeviceId()"); - env->ExceptionDescribe(); - env->ExceptionClear(); + if (!cb_result) { return ""; } + auto jResult = static_cast(cb_result.get()); - if (jResult) { - const char* str = env->GetStringUTFChars(jResult, nullptr); - if (str == nullptr) { - env->DeleteLocalRef(jResult); - return ""; - } - - // Lock mutex to protect g_cached_device_id from concurrent access - std::lock_guard lock(g_device_jni_state.mtx); - g_cached_device_id = str; - env->ReleaseStringUTFChars(jResult, str); - env->DeleteLocalRef(jResult); - return g_cached_device_id.c_str(); + const char* str = env->GetStringUTFChars(jResult, nullptr); + s.check("GetStringUTFChars"); + if (str == nullptr || s.failed()) { + return ""; } - return ""; + + // Lock mutex to protect g_cached_device_id from concurrent access + std::lock_guard lock(g_device_jni_state.mtx); + g_cached_device_id = str; + env->ReleaseStringUTFChars(jResult, str); + // cb_result Local auto-releases on scope exit. + return g_cached_device_id.c_str(); } -static rac_bool_t jni_device_is_registered(void* user_data) { +static rac_bool_t jni_device_is_registered(void* /*user_data*/) { JNIEnv* env = getJNIEnv(); if (!env || !g_device_jni_state.callback_obj || !g_device_jni_state.is_registered_method) { return RAC_FALSE; } - jboolean result = env->CallBooleanMethod(g_device_jni_state.callback_obj, - g_device_jni_state.is_registered_method); - - // Check for Java exception after CallBooleanMethod - if (env->ExceptionCheck()) { - LOGe("jni_device_is_registered: Java exception occurred in isRegistered()"); - env->ExceptionDescribe(); - env->ExceptionClear(); + rac::jni::JniScope s(env, "jni_device_is_registered"); + jboolean result = s.call_boolean_method(g_device_jni_state.callback_obj, + g_device_jni_state.is_registered_method); + if (s.failed()) { return RAC_FALSE; } - return result ? RAC_TRUE : RAC_FALSE; + return (result != JNI_FALSE) ? RAC_TRUE : RAC_FALSE; } static void jni_device_set_registered(rac_bool_t registered, void* user_data) { @@ -2951,21 +3003,18 @@ static void jni_device_set_registered(rac_bool_t registered, void* user_data) { return; } - env->CallVoidMethod(g_device_jni_state.callback_obj, g_device_jni_state.set_registered_method, - registered == RAC_TRUE ? JNI_TRUE : JNI_FALSE); - - // Check for Java exception after CallVoidMethod - if (env->ExceptionCheck()) { - LOGe("jni_device_set_registered: Java exception occurred in setRegistered()"); - env->ExceptionDescribe(); - env->ExceptionClear(); - } + rac::jni::JniScope s(env, "jni_device_set_registered"); + s.call_void_method(g_device_jni_state.callback_obj, + g_device_jni_state.set_registered_method, + registered == RAC_TRUE ? JNI_TRUE : JNI_FALSE); + // Errors silently swallowed - set_registered is fire-and-forget from + // the C++ side; JniScope has already logged + cleared any exception. } static rac_result_t jni_device_http_post(const char* endpoint, const char* json_body, rac_bool_t requires_auth, rac_device_http_response_t* out_response, - void* user_data) { + void* /*user_data*/) { JNIEnv* env = getJNIEnv(); if (!env || !g_device_jni_state.callback_obj || !g_device_jni_state.http_post_method) { LOGe("jni_device_http_post: JNI not ready"); @@ -2976,38 +3025,22 @@ static rac_result_t jni_device_http_post(const char* endpoint, const char* json_ return RAC_ERROR_ADAPTER_NOT_SET; } - jstring jEndpoint = env->NewStringUTF(endpoint ? endpoint : ""); - jstring jBody = env->NewStringUTF(json_body ? json_body : ""); - - // Check for allocation failures (can throw OutOfMemoryError) - if (env->ExceptionCheck() || !jEndpoint || !jBody) { - LOGe("jni_device_http_post: Failed to create JNI strings"); - if (env->ExceptionCheck()) { - env->ExceptionDescribe(); - env->ExceptionClear(); - } - if (jEndpoint) - env->DeleteLocalRef(jEndpoint); - if (jBody) - env->DeleteLocalRef(jBody); + rac::jni::JniScope s(env, "jni_device_http_post"); + auto jEndpoint = s.new_string_utf(endpoint != nullptr ? endpoint : ""); + auto jBody = s.new_string_utf(json_body != nullptr ? json_body : ""); + if (s.failed() || !jEndpoint || !jBody) { if (out_response) { out_response->result = RAC_ERROR_OUT_OF_MEMORY; out_response->status_code = -1; } - return RAC_ERROR_OUT_OF_MEMORY; + return s.failed() ? RAC_ERROR_OUT_OF_MEMORY : RAC_ERROR_OUT_OF_MEMORY; } - jint statusCode = - env->CallIntMethod(g_device_jni_state.callback_obj, g_device_jni_state.http_post_method, - jEndpoint, jBody, requires_auth == RAC_TRUE ? JNI_TRUE : JNI_FALSE); - - // Check for Java exception after CallIntMethod - if (env->ExceptionCheck()) { - LOGe("jni_device_http_post: Java exception occurred in httpPost()"); - env->ExceptionDescribe(); - env->ExceptionClear(); - env->DeleteLocalRef(jEndpoint); - env->DeleteLocalRef(jBody); + jint statusCode = s.call_int_method(g_device_jni_state.callback_obj, + g_device_jni_state.http_post_method, + jEndpoint.get(), jBody.get(), + requires_auth == RAC_TRUE ? JNI_TRUE : JNI_FALSE); + if (s.failed()) { if (out_response) { out_response->result = RAC_ERROR_NETWORK_ERROR; out_response->status_code = -1; @@ -3015,9 +3048,6 @@ static rac_result_t jni_device_http_post(const char* endpoint, const char* json_ return RAC_ERROR_NETWORK_ERROR; } - env->DeleteLocalRef(jEndpoint); - env->DeleteLocalRef(jBody); - if (out_response) { out_response->status_code = statusCode; out_response->result = @@ -3135,7 +3165,7 @@ static struct { } g_telemetry_jni_state = {}; // Telemetry HTTP callback from C++ to Java -static void jni_telemetry_http_callback(void* user_data, const char* endpoint, +static void jni_telemetry_http_callback(void* /*user_data*/, const char* endpoint, const char* json_body, size_t json_length, rac_bool_t requires_auth) { JNIEnv* env = getJNIEnv(); @@ -3145,34 +3175,17 @@ static void jni_telemetry_http_callback(void* user_data, const char* endpoint, return; } - jstring jEndpoint = env->NewStringUTF(endpoint ? endpoint : ""); - jstring jBody = env->NewStringUTF(json_body ? json_body : ""); - - // Check for NewStringUTF allocation failures - if (!jEndpoint || !jBody) { - LOGe("jni_telemetry_http_callback: failed to allocate JNI strings"); - if (jEndpoint) - env->DeleteLocalRef(jEndpoint); - if (jBody) - env->DeleteLocalRef(jBody); - return; - } - - env->CallVoidMethod(g_telemetry_jni_state.http_callback_obj, - g_telemetry_jni_state.http_callback_method, jEndpoint, jBody, - static_cast(json_length), - requires_auth == RAC_TRUE ? JNI_TRUE : JNI_FALSE); - - // Check for Java exception after CallVoidMethod - if (env->ExceptionCheck()) { - LOGe("jni_telemetry_http_callback: Java exception occurred in HTTP callback"); - env->ExceptionDescribe(); - env->ExceptionClear(); - } + rac::jni::JniScope s(env, "jni_telemetry_http_callback"); + auto jEndpoint = s.new_string_utf(endpoint != nullptr ? endpoint : ""); + auto jBody = s.new_string_utf(json_body != nullptr ? json_body : ""); + RAC_JNI_TRY_VOID(s); - // Always clean up local references - env->DeleteLocalRef(jEndpoint); - env->DeleteLocalRef(jBody); + s.call_void_method(g_telemetry_jni_state.http_callback_obj, + g_telemetry_jni_state.http_callback_method, jEndpoint.get(), jBody.get(), + static_cast(json_length), + requires_auth == RAC_TRUE ? JNI_TRUE : JNI_FALSE); + // JniScope has already logged + cleared any exception. Locals are + // auto-freed on scope exit via Local RAII. } JNIEXPORT jlong JNICALL @@ -4158,7 +4171,7 @@ static rac_bool_t vlm_stream_callback_token(const char* token, void* user_data) jint result = ctx->jvm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6); if (result == JNI_EDETACHED) { - if (ctx->jvm->AttachCurrentThread(&env, nullptr) == JNI_OK) { + if (ctx->jvm->AttachCurrentThread(reinterpret_cast(&env), nullptr) == JNI_OK) { needsDetach = true; } else { LOGe("VLM: Failed to attach thread for streaming callback"); diff --git a/sdk/runanywhere-commons/tests/CMakeLists.txt b/sdk/runanywhere-commons/tests/CMakeLists.txt index acc48b0ac..56a20af51 100644 --- a/sdk/runanywhere-commons/tests/CMakeLists.txt +++ b/sdk/runanywhere-commons/tests/CMakeLists.txt @@ -16,6 +16,15 @@ function(rac_link_archive_deps target) if(TARGET bz2_bundled) target_link_libraries(${target} PRIVATE bz2_bundled) endif() + # If sanitizers are enabled at the global level, test executables must link + # their runtime too (otherwise you get `__asan_after_dynamic_init` unresolved + # symbols coming from rac_commons's instrumentation). This is a no-op when + # no sanitizer is enabled. + target_enable_sanitizers(${target}) + # Tests may include internal shim headers like + # `internal/rac_platform_compat.h` (which lives under src/internal/). + # Add src/ as a PRIVATE include root so those references resolve. + target_include_directories(${target} PRIVATE ${CMAKE_SOURCE_DIR}/src) endfunction() # --- test_core: Always built (no backend dependency) --- @@ -23,6 +32,7 @@ add_executable(test_core test_core.cpp) target_include_directories(test_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/src ) target_link_libraries(test_core PRIVATE rac_commons) rac_link_archive_deps(test_core) @@ -61,6 +71,7 @@ if(RAC_BACKEND_ONNX AND TARGET rac_backend_onnx) ) target_link_libraries(test_vad PRIVATE rac_commons rac_backend_onnx) target_compile_features(test_vad PRIVATE cxx_std_20) + rac_link_archive_deps(test_vad) add_test(NAME vad_tests COMMAND test_vad --run-all) # STT test @@ -71,6 +82,7 @@ if(RAC_BACKEND_ONNX AND TARGET rac_backend_onnx) ) target_link_libraries(test_stt PRIVATE rac_commons rac_backend_onnx) target_compile_features(test_stt PRIVATE cxx_std_20) + rac_link_archive_deps(test_stt) add_test(NAME stt_tests COMMAND test_stt --run-all) # TTS test @@ -81,6 +93,7 @@ if(RAC_BACKEND_ONNX AND TARGET rac_backend_onnx) ) target_link_libraries(test_tts PRIVATE rac_commons rac_backend_onnx) target_compile_features(test_tts PRIVATE cxx_std_20) + rac_link_archive_deps(test_tts) add_test(NAME tts_tests COMMAND test_tts --run-all) # WakeWord test @@ -91,6 +104,7 @@ if(RAC_BACKEND_ONNX AND TARGET rac_backend_onnx) ) target_link_libraries(test_wakeword PRIVATE rac_commons rac_backend_onnx) target_compile_features(test_wakeword PRIVATE cxx_std_20) + rac_link_archive_deps(test_wakeword) add_test(NAME wakeword_tests COMMAND test_wakeword --run-all) endif() @@ -103,6 +117,7 @@ if(RAC_BACKEND_LLAMACPP AND TARGET rac_backend_llamacpp) ) target_link_libraries(test_llm PRIVATE rac_commons rac_backend_llamacpp) target_compile_features(test_llm PRIVATE cxx_std_20) + rac_link_archive_deps(test_llm) add_test(NAME llm_tests COMMAND test_llm --run-all) endif() @@ -117,6 +132,7 @@ if(RAC_BACKEND_ONNX AND RAC_BACKEND_LLAMACPP target_link_libraries(test_voice_agent PRIVATE rac_commons rac_backend_onnx rac_backend_llamacpp) target_compile_features(test_voice_agent PRIVATE cxx_std_20) + rac_link_archive_deps(test_voice_agent) add_test(NAME voice_agent_tests COMMAND test_voice_agent --run-all) endif() @@ -199,6 +215,7 @@ if(RAC_BUILD_BACKENDS AND RAC_BACKEND_RAG AND NOT (WIN32 AND RAC_BUILD_SHARED)) GTest::gtest_main ) target_compile_features(rac_rag_backend_thread_safety_test PRIVATE cxx_std_20) + target_enable_sanitizers(rac_rag_backend_thread_safety_test) gtest_discover_tests(rac_rag_backend_thread_safety_test DISCOVERY_MODE PRE_TEST ) @@ -222,6 +239,7 @@ if(RAC_BUILD_BACKENDS AND RAC_BACKEND_RAG AND NOT (WIN32 AND RAC_BUILD_SHARED)) GTest::gtest_main ) target_compile_features(rac_chunker_test PRIVATE cxx_std_20) + target_enable_sanitizers(rac_chunker_test) gtest_discover_tests(rac_chunker_test DISCOVERY_MODE PRE_TEST ) @@ -241,6 +259,7 @@ if(RAC_BUILD_BACKENDS AND RAC_BACKEND_RAG AND NOT (WIN32 AND RAC_BUILD_SHARED)) GTest::gtest_main ) target_compile_features(rac_simple_tokenizer_test PRIVATE cxx_std_20) + target_enable_sanitizers(rac_simple_tokenizer_test) gtest_discover_tests(rac_simple_tokenizer_test DISCOVERY_MODE PRE_TEST ) @@ -279,6 +298,7 @@ target_link_libraries(rac_benchmark_tests rac_commons GTest::gtest_main ) +target_enable_sanitizers(rac_benchmark_tests) target_include_directories(rac_benchmark_tests PRIVATE diff --git a/sdk/runanywhere-commons/tests/test_config.h b/sdk/runanywhere-commons/tests/test_config.h index 296c00ff4..bf488413a 100644 --- a/sdk/runanywhere-commons/tests/test_config.h +++ b/sdk/runanywhere-commons/tests/test_config.h @@ -4,7 +4,7 @@ #include #include #include -#include "rac/core/rac_platform_compat.h" +#include "internal/rac_platform_compat.h" #ifdef _WIN32 #include diff --git a/sdk/runanywhere-commons/tests/test_core.cpp b/sdk/runanywhere-commons/tests/test_core.cpp index dc28794f3..fae3054c2 100644 --- a/sdk/runanywhere-commons/tests/test_core.cpp +++ b/sdk/runanywhere-commons/tests/test_core.cpp @@ -13,6 +13,7 @@ #include "rac/core/rac_error.h" #include "rac/core/rac_logger.h" #include "rac/core/rac_audio_utils.h" +#include "rac/core/rac_hardware.h" #include "rac/core/rac_platform_adapter.h" #include @@ -37,6 +38,7 @@ static int64_t test_now_ms(void* /*ctx*/) { } static const rac_platform_adapter_t test_adapter = { + /* version */ RAC_PLATFORM_ADAPTER_VERSION, /* file_exists */ nullptr, /* file_read */ nullptr, /* file_write */ nullptr, @@ -98,6 +100,84 @@ static TestResult test_double_init() { return TEST_PASS(); } +// ============================================================================= +// Test: adapter.version = 0 is rejected (no grace period) +// ============================================================================= + +static TestResult test_adapter_version_zero_rejected() { + // Callers that zero-init their adapter without setting .version must be + // caught at init time with RAC_ERROR_BACKEND_INCOMPATIBLE_VERSION. + rac_platform_adapter_t adapter_v0 = test_adapter; + adapter_v0.version = 0; + + rac_config_t config = {}; + config.platform_adapter = &adapter_v0; + config.log_level = RAC_LOG_WARNING; + config.log_tag = "TEST"; + + rac_result_t rc = rac_init(&config); + ASSERT_EQ(rc, RAC_ERROR_BACKEND_INCOMPATIBLE_VERSION, + "rac_init with version=0 should return " + "RAC_ERROR_BACKEND_INCOMPATIBLE_VERSION"); + ASSERT_EQ(rac_is_initialized(), RAC_FALSE, + "init failure must not leave SDK in initialized state"); + return TEST_PASS(); +} + +// ============================================================================= +// Test: adapter.version too new is rejected +// ============================================================================= + +static TestResult test_adapter_version_future_rejected() { + // Callers compiled against a FUTURE version of the struct must be + // rejected, not silently misinterpreted. + rac_platform_adapter_t adapter_future = test_adapter; + adapter_future.version = RAC_PLATFORM_ADAPTER_VERSION + 999; + + rac_config_t config = {}; + config.platform_adapter = &adapter_future; + config.log_level = RAC_LOG_WARNING; + config.log_tag = "TEST"; + + rac_result_t rc = rac_init(&config); + ASSERT_EQ(rc, RAC_ERROR_BACKEND_INCOMPATIBLE_VERSION, + "rac_init with future version should return " + "RAC_ERROR_BACKEND_INCOMPATIBLE_VERSION"); + return TEST_PASS(); +} + +// ============================================================================= +// Test: hardware capability query +// ============================================================================= + +static TestResult test_hardware_query() { + rac_hardware_report_t report = {}; + report.version = RAC_HARDWARE_REPORT_V1; + rac_result_t rc = rac_hardware_query_capabilities(&report); + ASSERT_EQ(rc, RAC_SUCCESS, "hardware query should succeed"); + // logical CPU count is > 0 on every real machine + ASSERT_TRUE(report.num_logical_cpus > 0, + "should detect at least one logical CPU"); +#if defined(__APPLE__) && defined(__aarch64__) + ASSERT_EQ(report.is_apple_silicon, RAC_TRUE, + "is_apple_silicon should be TRUE on arm64 macOS host"); + ASSERT_EQ(report.has_neon, RAC_TRUE, + "has_neon should be TRUE on arm64"); + ASSERT_EQ(report.has_ane, RAC_TRUE, + "has_ane should be TRUE on Apple (compile-time honest)"); +#endif + return TEST_PASS(); +} + +static TestResult test_hardware_wrong_version() { + rac_hardware_report_t report = {}; + report.version = 0; // unset + rac_result_t rc = rac_hardware_query_capabilities(&report); + ASSERT_EQ(rc, RAC_ERROR_BACKEND_INCOMPATIBLE_VERSION, + "hardware query with unset version should fail"); + return TEST_PASS(); +} + // ============================================================================= // Test: version info // ============================================================================= @@ -367,6 +447,10 @@ int main(int argc, char** argv) { suite.add("init_shutdown", test_init_shutdown); suite.add("double_init", test_double_init); + suite.add("adapter_version_zero_rejected", test_adapter_version_zero_rejected); + suite.add("adapter_version_future_rejected", test_adapter_version_future_rejected); + suite.add("hardware_query", test_hardware_query); + suite.add("hardware_wrong_version", test_hardware_wrong_version); suite.add("get_version", test_get_version); suite.add("error_message_known", test_error_message_known); suite.add("error_message_unknown", test_error_message_unknown); diff --git a/sdk/runanywhere-commons/tests/test_download_orchestrator.cpp b/sdk/runanywhere-commons/tests/test_download_orchestrator.cpp index 5131e07af..f2c6dc108 100644 --- a/sdk/runanywhere-commons/tests/test_download_orchestrator.cpp +++ b/sdk/runanywhere-commons/tests/test_download_orchestrator.cpp @@ -19,7 +19,7 @@ #include #include #include -#include "rac/core/rac_platform_compat.h" +#include "internal/rac_platform_compat.h" #ifdef _WIN32 #include diff --git a/sdk/runanywhere-commons/tests/test_extraction.cpp b/sdk/runanywhere-commons/tests/test_extraction.cpp index 71ac19d80..86497931d 100644 --- a/sdk/runanywhere-commons/tests/test_extraction.cpp +++ b/sdk/runanywhere-commons/tests/test_extraction.cpp @@ -19,7 +19,7 @@ #include #include #include -#include "rac/core/rac_platform_compat.h" +#include "internal/rac_platform_compat.h" #include #ifdef _WIN32 diff --git a/sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_platform.dart b/sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_platform.dart index 7e4e12823..f33dad9fa 100644 --- a/sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_platform.dart +++ b/sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_platform.dart @@ -81,6 +81,11 @@ class DartBridgePlatform { _adapterPtr = calloc(); final adapter = _adapterPtr!; + // ABI version - rac_init() rejects adapters with version == 0 or + // version > RAC_PLATFORM_ADAPTER_VERSION. calloc zeroes the struct + // so we must set this explicitly. + adapter.ref.version = racPlatformAdapterVersion; + // Logging callback - MUST use NativeCallable.listener for thread safety // This allows C++ to call the logger from any thread (including background // threads used by LLM generation) without crashing with: diff --git a/sdk/runanywhere-flutter/packages/runanywhere/lib/native/ffi_types.dart b/sdk/runanywhere-flutter/packages/runanywhere/lib/native/ffi_types.dart index 89e096a83..2b295032e 100644 --- a/sdk/runanywhere-flutter/packages/runanywhere/lib/native/ffi_types.dart +++ b/sdk/runanywhere-flutter/packages/runanywhere/lib/native/ffi_types.dart @@ -857,9 +857,14 @@ typedef RacHttpDownloadCancelCallbackNative = Int32 Function( // ============================================================================= /// Platform adapter struct matching rac_platform_adapter_t -/// Note: This is a complex struct - for simplicity we use Pointer in FFI calls -/// and manage the struct manually in Dart +/// +/// ABI: the first field is a uint32_t `version` that rac_init() validates +/// against [racPlatformAdapterVersion]. Callers must set it before passing +/// this struct to racSetPlatformAdapter / rac_init. base class RacPlatformAdapterStruct extends Struct { + @Uint32() + external int version; + external Pointer> fileExists; external Pointer> fileRead; external Pointer> fileWrite; @@ -878,6 +883,10 @@ base class RacPlatformAdapterStruct extends Struct { external Pointer userData; } +/// Current schema version of [RacPlatformAdapterStruct]. Bump alongside +/// commons/include/rac/core/rac_platform_adapter.h when the struct grows. +const int racPlatformAdapterVersion = 1; + /// Memory info struct matching rac_memory_info_t base class RacMemoryInfoStruct extends Struct { @Uint64() diff --git a/sdk/runanywhere-react-native/packages/core/cpp/bridges/InitBridge.cpp b/sdk/runanywhere-react-native/packages/core/cpp/bridges/InitBridge.cpp index 131c0e25f..d08013e44 100644 --- a/sdk/runanywhere-react-native/packages/core/cpp/bridges/InitBridge.cpp +++ b/sdk/runanywhere-react-native/packages/core/cpp/bridges/InitBridge.cpp @@ -945,6 +945,10 @@ void InitBridge::registerPlatformAdapter() { // Reset adapter memset(&adapter_, 0, sizeof(adapter_)); + // ABI version - rac_init() rejects adapters where version is 0 or + // > RAC_PLATFORM_ADAPTER_VERSION. + adapter_.version = RAC_PLATFORM_ADAPTER_VERSION; + // File operations adapter_.file_exists = platformFileExistsCallback; adapter_.file_read = platformFileReadCallback; diff --git a/sdk/runanywhere-swift/Sources/RunAnywhere/CRACommons/include/CRACommons.h b/sdk/runanywhere-swift/Sources/RunAnywhere/CRACommons/include/CRACommons.h index b1439d93c..677a81d54 100644 --- a/sdk/runanywhere-swift/Sources/RunAnywhere/CRACommons/include/CRACommons.h +++ b/sdk/runanywhere-swift/Sources/RunAnywhere/CRACommons/include/CRACommons.h @@ -16,6 +16,7 @@ // ============================================================================= #include "rac_types.h" +#include "rac_attrs.h" // RAC_NODISCARD, RAC_NONNULL, RAC_DEPRECATED macros #include "rac_error.h" #include "rac_structured_error.h" #include "rac_logger.h" @@ -23,6 +24,7 @@ #include "rac_platform_adapter.h" #include "rac_component_types.h" #include "rac_audio_utils.h" +#include "rac_hardware.h" // rac_hardware_query_capabilities + rac_hardware_report_t // Lifecycle management #include "rac_lifecycle.h" diff --git a/sdk/runanywhere-swift/Sources/RunAnywhere/CRACommons/include/rac_attrs.h b/sdk/runanywhere-swift/Sources/RunAnywhere/CRACommons/include/rac_attrs.h new file mode 100644 index 000000000..655a485a1 --- /dev/null +++ b/sdk/runanywhere-swift/Sources/RunAnywhere/CRACommons/include/rac_attrs.h @@ -0,0 +1,178 @@ +/** + * @file rac_attrs.h + * @brief RunAnywhere Commons - Compiler Attribute Macros + * + * Portable macros for C and C++ attributes used across the public RAC API: + * - RAC_NODISCARD : warn when a return value is silently ignored + * - RAC_NONNULL(...) : assert that pointer parameters cannot be NULL + * - RAC_DEPRECATED(msg) : mark a symbol as deprecated with a migration hint + * - RAC_ATTR_PRINTF(f,a) : printf-style format check for variadic functions + * - RAC_NORETURN : function does not return (fatal error helpers) + * + * All macros degrade gracefully to nothing when the compiler does not support + * the underlying attribute, so the header is safe to include from any C or + * C++ translation unit on any supported platform. + * + * Compiler support matrix: + * | Attribute | Clang | GCC | MSVC | Apple Clang | Android NDK | + * | nodiscard | 3.9+ | 4.8+| 19.14+| 3.9+ | r14+ | + * | nonnull | 3.9+ | 4.0+| n/a | 3.9+ | r14+ | + * | deprecated(msg) | 3.9+ | 4.5+| 14.0+| 3.9+ | r14+ | + * + * For MSVC, RAC_NONNULL maps to _In_ SAL annotations where applicable. + * + * Include this header from every public RAC header that exposes functions + * returning rac_result_t or taking pointer parameters. + */ + +#ifndef RAC_ATTRS_H +#define RAC_ATTRS_H + +/* --------------------------------------------------------------------------- + * RAC_NODISCARD - warn if return value is ignored + * + * Usage: + * RAC_API RAC_NODISCARD rac_result_t rac_init(const rac_config_t* config); + * + * Callers that write `rac_init(&cfg);` without capturing the result will get + * a compiler warning (or error with -Werror). This catches silent error code + * drops, the #1 source of bugs in C-style API consumers. + * --------------------------------------------------------------------------- */ +#if defined(__cplusplus) && __cplusplus >= 201703L + /* C++17 [[nodiscard]] */ + #define RAC_NODISCARD [[nodiscard]] +#elif defined(__has_attribute) + #if __has_attribute(warn_unused_result) + #define RAC_NODISCARD __attribute__((warn_unused_result)) + #endif +#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 304) + /* GCC 3.4+ supports warn_unused_result */ + #define RAC_NODISCARD __attribute__((warn_unused_result)) +#endif +#if defined(_MSC_VER) && _MSC_VER >= 1700 && !defined(RAC_NODISCARD) + /* MSVC 2012+: _Check_return_ is a SAL annotation that triggers /analyze */ + #define RAC_NODISCARD _Check_return_ +#endif +#ifndef RAC_NODISCARD + #define RAC_NODISCARD +#endif + +/* --------------------------------------------------------------------------- + * RAC_NONNULL(...) - mark pointer parameters as non-null + * + * Usage: + * RAC_API rac_result_t rac_module_register( + * RAC_NONNULL(1) const rac_module_info_t* info); + * + * Index arguments are 1-based (because GCC / Clang count them that way). + * For member functions in C++ the implicit `this` is parameter 1, so shift. + * + * MSVC does not have a direct equivalent; falls back to empty macro (the + * intent can still be checked manually / via clang-tidy's + * bugprone-not-null-terminated-result check). + * --------------------------------------------------------------------------- */ +#if defined(__has_attribute) + #if __has_attribute(nonnull) + #define RAC_NONNULL(...) __attribute__((nonnull(__VA_ARGS__))) + #endif +#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 400) + #define RAC_NONNULL(...) __attribute__((nonnull(__VA_ARGS__))) +#endif +#ifndef RAC_NONNULL + #define RAC_NONNULL(...) +#endif + +/* Variant: mark ALL pointer params as nonnull (no indices). */ +#if defined(__has_attribute) + #if __has_attribute(nonnull) + #define RAC_NONNULL_ALL __attribute__((nonnull)) + #endif +#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 400) + #define RAC_NONNULL_ALL __attribute__((nonnull)) +#endif +#ifndef RAC_NONNULL_ALL + #define RAC_NONNULL_ALL +#endif + +/* --------------------------------------------------------------------------- + * RAC_DEPRECATED(msg) - mark a symbol as deprecated with migration text + * + * Usage: + * RAC_DEPRECATED("use rac_llm_generate_v2 instead") + * RAC_API rac_result_t rac_llm_generate(rac_handle_t h, ...); + * --------------------------------------------------------------------------- */ +#if defined(__cplusplus) && __cplusplus >= 201402L + #define RAC_DEPRECATED(msg) [[deprecated(msg)]] +#elif defined(__has_attribute) + #if __has_attribute(deprecated) + #if defined(__clang__) || (defined(__GNUC__) && __GNUC__ >= 5) + #define RAC_DEPRECATED(msg) __attribute__((deprecated(msg))) + #else + #define RAC_DEPRECATED(msg) __attribute__((deprecated)) + #endif + #endif +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + #define RAC_DEPRECATED(msg) __declspec(deprecated(msg)) +#endif +#ifndef RAC_DEPRECATED + #define RAC_DEPRECATED(msg) +#endif + +/* --------------------------------------------------------------------------- + * RAC_ATTR_PRINTF(fmt_index, args_index) - printf-style format checking + * + * Usage (variadic logger wrapper): + * RAC_API void rac_logger_log_fmt(rac_log_level_t lvl, + * const char* fmt, ...) + * RAC_ATTR_PRINTF(2, 3); + * --------------------------------------------------------------------------- */ +#if defined(__has_attribute) + #if __has_attribute(format) + #define RAC_ATTR_PRINTF(fmt_idx, args_idx) \ + __attribute__((format(printf, fmt_idx, args_idx))) + #endif +#elif defined(__GNUC__) + #define RAC_ATTR_PRINTF(fmt_idx, args_idx) \ + __attribute__((format(printf, fmt_idx, args_idx))) +#endif +#ifndef RAC_ATTR_PRINTF + #define RAC_ATTR_PRINTF(fmt_idx, args_idx) +#endif + +/* --------------------------------------------------------------------------- + * RAC_NORETURN - function does not return + * --------------------------------------------------------------------------- */ +#if defined(__cplusplus) && __cplusplus >= 201103L + #define RAC_NORETURN [[noreturn]] +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + #define RAC_NORETURN _Noreturn +#elif defined(__GNUC__) + #define RAC_NORETURN __attribute__((noreturn)) +#elif defined(_MSC_VER) + #define RAC_NORETURN __declspec(noreturn) +#else + #define RAC_NORETURN +#endif + +/* --------------------------------------------------------------------------- + * RAC_PURE - function result depends only on its arguments (no globals, no I/O) + * RAC_CONST - like pure, but also must not read any memory through a pointer + * + * Both enable compile-time deduplication and CSE by the optimizer. + * --------------------------------------------------------------------------- */ +#if defined(__has_attribute) + #if __has_attribute(pure) + #define RAC_PURE __attribute__((pure)) + #endif + #if __has_attribute(const) + #define RAC_CONST_FN __attribute__((const)) + #endif +#endif +#ifndef RAC_PURE + #define RAC_PURE +#endif +#ifndef RAC_CONST_FN + #define RAC_CONST_FN +#endif + +#endif /* RAC_ATTRS_H */ diff --git a/sdk/runanywhere-swift/Sources/RunAnywhere/CRACommons/include/rac_error.h b/sdk/runanywhere-swift/Sources/RunAnywhere/CRACommons/include/rac_error.h index d46fdf1e9..4c44cb8e3 100644 --- a/sdk/runanywhere-swift/Sources/RunAnywhere/CRACommons/include/rac_error.h +++ b/sdk/runanywhere-swift/Sources/RunAnywhere/CRACommons/include/rac_error.h @@ -365,6 +365,16 @@ extern "C" { #define RAC_ERROR_BACKEND_INIT_FAILED ((rac_result_t) - 602) /** Backend busy */ #define RAC_ERROR_BACKEND_BUSY ((rac_result_t) - 603) +/** Backend unavailable: backend compiled as stub, engine binary not installed */ +#define RAC_ERROR_BACKEND_UNAVAILABLE ((rac_result_t) - 604) +/** Backend runtime ABI is incompatible with the version the SDK was compiled + * against. Typically means the prebuilt third_party binary (sherpa-onnx, + * ONNX Runtime, etc.) was produced from a different major/minor version + * than the headers; struct layouts may not match, so we refuse to proceed. */ +#define RAC_ERROR_BACKEND_INCOMPATIBLE_VERSION ((rac_result_t) - 605) +/** JNI call from a Kotlin/Java callback left a pending Java exception on + * the JNIEnv - caught by JniScope, logged, and cleared. */ +#define RAC_ERROR_JNI_EXCEPTION ((rac_result_t) - 606) /** Invalid handle */ #define RAC_ERROR_INVALID_HANDLE ((rac_result_t) - 610) diff --git a/sdk/runanywhere-swift/Sources/RunAnywhere/CRACommons/include/rac_hardware.h b/sdk/runanywhere-swift/Sources/RunAnywhere/CRACommons/include/rac_hardware.h new file mode 100644 index 000000000..725abf3f9 --- /dev/null +++ b/sdk/runanywhere-swift/Sources/RunAnywhere/CRACommons/include/rac_hardware.h @@ -0,0 +1,104 @@ +/** + * @file rac_hardware.h + * @brief RunAnywhere Commons - Hardware capability detection + * + * Public C API for querying which inference accelerators are available at + * runtime on the current device. Used by backend selection logic and by + * SDK consumers that want to surface device-aware UI (e.g. "GPU acceleration + * enabled"). + * + * Detection is compile-time-honest: features gated by a build flag are + * reported absent on binaries that don't include them, even if the hardware + * itself is present. For example, an arm64 iOS build without + * -DGGML_USE_METAL will report has_metal = RAC_FALSE. + * + * Thread safety: + * rac_hardware_query_capabilities() is safe to call from any thread. + * Results are cached inside rac_commons after the first call; repeated + * calls are cheap (one atomic load). + */ + +#ifndef RAC_HARDWARE_H +#define RAC_HARDWARE_H + +#include "rac_error.h" +#include "rac_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Snapshot of detected hardware + accelerator capabilities. + * + * Every field carries a rac_bool_t (not std::optional) so C consumers can + * use the struct directly. Numeric fields like total_gpu_memory_mb are 0 + * when unknown or inapplicable. + * + * ABI-stable: this struct has a leading `version` field and a trailing + * reserved block so new capabilities can be added without breaking + * existing C consumers. Current schema version is RAC_HARDWARE_REPORT_V1. + */ +typedef struct rac_hardware_report { + /** Must equal RAC_HARDWARE_REPORT_V1. Checked at every query. */ + uint32_t version; + + /* -- CPU SIMD ---------------------------------------------------- */ + rac_bool_t has_neon; /* ARM NEON (Android arm64, iOS arm64, Apple Silicon) */ + rac_bool_t has_sse42; /* Intel SSE4.2 */ + rac_bool_t has_avx; /* Intel AVX */ + rac_bool_t has_avx2; /* Intel AVX2 */ + rac_bool_t has_avx512; /* Intel AVX-512 family */ + int32_t num_logical_cpus; + int32_t num_physical_cpus; + + /* -- GPU --------------------------------------------------------- */ + rac_bool_t has_metal; /* Apple Metal available AND commons built with it */ + rac_bool_t has_cuda; /* NVIDIA CUDA */ + rac_bool_t has_vulkan; /* Vulkan (Linux/Android/Windows) */ + rac_bool_t has_opencl; /* OpenCL (Android Adreno etc.) */ + rac_bool_t has_webgpu; /* WebGPU (WASM/Emscripten builds) */ + int64_t total_gpu_memory_mb; /* 0 if unknown */ + + /* -- NPU / Neural accelerator ------------------------------------ */ + rac_bool_t has_ane; /* Apple Neural Engine (via Core ML) */ + rac_bool_t has_qnn; /* Qualcomm Hexagon via QNN SDK */ + rac_bool_t has_genio; /* MediaTek Genio NPU */ + + /* -- OS / platform info ------------------------------------------ */ + rac_bool_t is_apple_silicon; + rac_bool_t is_ios_simulator; + rac_bool_t is_android_emulator; + + /** Reserved for future fields. Must be initialized to zero. */ + void* _reserved[4]; +} rac_hardware_report_t; + +/** Current schema version. Bump when adding fields in a breaking way. */ +#define RAC_HARDWARE_REPORT_V1 ((uint32_t)1) + +/** + * @brief Populate `out_report` with current hardware capabilities. + * + * The caller sets `out_report->version = RAC_HARDWARE_REPORT_V1` before + * calling; a mismatch returns RAC_ERROR_BACKEND_INCOMPATIBLE_VERSION. + * + * @param out_report Caller-owned struct. Required. + * @return RAC_SUCCESS, or RAC_ERROR_INVALID_ARGUMENT / _INCOMPATIBLE_VERSION. + */ +RAC_API RAC_NODISCARD rac_result_t rac_hardware_query_capabilities( + rac_hardware_report_t* out_report); + +/** + * @brief Return a pretty multi-line string summarising the report. + * + * The string is newly allocated by rac_strdup; caller must free it with + * rac_free. Intended for logging / diagnostics, not machine parsing. + */ +RAC_API char* rac_hardware_format_report(const rac_hardware_report_t* report); + +#ifdef __cplusplus +} +#endif + +#endif // RAC_HARDWARE_H diff --git a/sdk/runanywhere-swift/Sources/RunAnywhere/CRACommons/include/rac_platform_adapter.h b/sdk/runanywhere-swift/Sources/RunAnywhere/CRACommons/include/rac_platform_adapter.h index 143615b0a..ccfe37b47 100644 --- a/sdk/runanywhere-swift/Sources/RunAnywhere/CRACommons/include/rac_platform_adapter.h +++ b/sdk/runanywhere-swift/Sources/RunAnywhere/CRACommons/include/rac_platform_adapter.h @@ -20,6 +20,17 @@ extern "C" { #endif +// ============================================================================= +// ADAPTER ABI VERSIONING +// ============================================================================= +// +// Callers MUST set `rac_platform_adapter_t::version = RAC_PLATFORM_ADAPTER_VERSION` +// before passing the struct to rac_init(). The runtime validates this and +// refuses adapters whose version is 0 (uninitialised) or greater than the +// latest version it knows about. +// ============================================================================= +#define RAC_PLATFORM_ADAPTER_VERSION ((uint32_t)1) + // ============================================================================= // CALLBACK TYPES (defined outside struct for C compatibility) // ============================================================================= @@ -62,6 +73,11 @@ typedef void (*rac_extract_progress_callback_fn)(int32_t files_extracted, int32_ * The SDK layer (Swift/Kotlin) provides these implementations. */ typedef struct rac_platform_adapter { + // ------------------------------------------------------------------------- + // Version (set by caller; validated by rac_init) + // ------------------------------------------------------------------------- + uint32_t version; // Must equal RAC_PLATFORM_ADAPTER_VERSION. + // ------------------------------------------------------------------------- // File System Operations // ------------------------------------------------------------------------- diff --git a/sdk/runanywhere-swift/Sources/RunAnywhere/CRACommons/include/rac_types.h b/sdk/runanywhere-swift/Sources/RunAnywhere/CRACommons/include/rac_types.h index 793371d5d..50edbb1e1 100644 --- a/sdk/runanywhere-swift/Sources/RunAnywhere/CRACommons/include/rac_types.h +++ b/sdk/runanywhere-swift/Sources/RunAnywhere/CRACommons/include/rac_types.h @@ -13,6 +13,11 @@ #include #include +/* Pull in portable attribute macros (RAC_NODISCARD, RAC_NONNULL, RAC_DEPRECATED, + * RAC_NORETURN, RAC_ATTR_PRINTF, RAC_PURE) - every RAC public header gets + * these via its rac_types.h include. */ +#include "rac_attrs.h" + /** * Null pointer macro for use in static initializers. * Uses nullptr in C++ (preferred by clang-tidy modernize-use-nullptr) diff --git a/sdk/runanywhere-swift/Sources/RunAnywhere/Foundation/Bridge/Extensions/CppBridge+PlatformAdapter.swift b/sdk/runanywhere-swift/Sources/RunAnywhere/Foundation/Bridge/Extensions/CppBridge+PlatformAdapter.swift index 491fd6c77..795f3c04b 100644 --- a/sdk/runanywhere-swift/Sources/RunAnywhere/Foundation/Bridge/Extensions/CppBridge+PlatformAdapter.swift +++ b/sdk/runanywhere-swift/Sources/RunAnywhere/Foundation/Bridge/Extensions/CppBridge+PlatformAdapter.swift @@ -41,6 +41,12 @@ extension CppBridge { // Reset adapter adapter = rac_platform_adapter_t() + // MARK: ABI version + // Commons rac_init rejects any adapter whose .version is 0 + // (or > the latest known version). Set to the version the + // Swift SDK was compiled against. + adapter.version = UInt32(RAC_PLATFORM_ADAPTER_VERSION) + // MARK: Logging Callback adapter.log = platformLogCallback diff --git a/sdk/runanywhere-web/packages/llamacpp/src/Foundation/PlatformAdapter.ts b/sdk/runanywhere-web/packages/llamacpp/src/Foundation/PlatformAdapter.ts index bae522196..a5f81ee2b 100644 --- a/sdk/runanywhere-web/packages/llamacpp/src/Foundation/PlatformAdapter.ts +++ b/sdk/runanywhere-web/packages/llamacpp/src/Foundation/PlatformAdapter.ts @@ -80,12 +80,18 @@ export class PlatformAdapter { httpDownload: this.registerHttpDownload(m), }; - // Write function pointers into the struct. + // Write fields into the struct. // The struct layout matches rac_platform_adapter.h field order. - // Each field is a function pointer (4 bytes on wasm32). + // Field 1 is a uint32_t `version`; the rest are function pointers + // (4 bytes each on wasm32). const PTR_SIZE = 4; + const RAC_PLATFORM_ADAPTER_VERSION = 1; let offset = 0; + // ABI version - rac_init() rejects adapters with version == 0 or + // > RAC_PLATFORM_ADAPTER_VERSION. + m.setValue(this.adapterPtr + offset, RAC_PLATFORM_ADAPTER_VERSION, 'i32'); offset += 4; + m.setValue(this.adapterPtr + offset, this.callbacks.fileExists, '*'); offset += PTR_SIZE; m.setValue(this.adapterPtr + offset, this.callbacks.fileRead, '*'); offset += PTR_SIZE; m.setValue(this.adapterPtr + offset, this.callbacks.fileWrite, '*'); offset += PTR_SIZE;