diff --git a/c/include/ml-lxm-service-internal.h b/c/include/ml-lxm-service-internal.h new file mode 100644 index 00000000..1d11a3ee --- /dev/null +++ b/c/include/ml-lxm-service-internal.h @@ -0,0 +1,265 @@ +/* SPDX-License-Identifier: Apache-2.0 */ +/** + * @file ml-lxm-service-internal.h + * @date 23 JULY 2025 + * @brief Machine Learning LXM(LLM, LVM, etc.) Service API + * @see https://github.com/nnstreamer/api + * @author Hyunil Park + * @bug No known bugs except for NYI items + */ + +/** + * @example sample_lxm_service.c + * @brief Sample application demonstrating ML LXM Service API usage + * + * This sample shows how to: + * - Create and configure an LXM session + * - Build prompts with text and instructions + * - Generate streaming responses with custom options + * - Handle token callbacks for real-time processing + * + * Configuration file example (config.json): + * @code + * { + * "single" : + * { + * "framework" : "flare", + * "model" : ["sflare_if_4bit_3b.bin"], + * "adapter" : ["history_lora.bin"], + * "custom" : "tokenizer_path:tokenizer.json,backend:CPU,output_size:1024,model_type:3B,data_type:W4A32", + * "invoke_dynamic" : "true", + * } + * } + * @endcode + * + * Basic usage workflow: + * @code + * // 1. Create session with callback + * ml_lxm_session_h session; + * ml_lxm_session_create("/path/to/config.json", NULL, token_handler, NULL, &session); + * + * // 2. Create prompt + * ml_lxm_prompt_h prompt; + * ml_lxm_prompt_create(&prompt); + * ml_lxm_prompt_append_text(prompt, "Hello AI"); + * + * // 3. Generate response with options (callback is already set during session creation) + * ml_option_h options = NULL; + * ml_option_create(&options); + * ml_option_set(options, "temperature", g_strdup_printf("%f", 1.0), g_free); + * ml_option_set(options, "max_tokens", g_strdup_printf("%zu", (size_t)50), g_free); + * ml_lxm_session_respond(session, prompt, options); + * ml_option_destroy(options); + * + * // 4. Cleanup + * ml_lxm_prompt_destroy(prompt); + * ml_lxm_session_destroy(session); + * @endcode + * + * Complete example with token callback: + * @code + * #include "ml-lxm-service-internal.h" + * #include + * + * static void token_handler(ml_service_event_e event, + * ml_information_h event_data, + * void *user_data); + * + * int main() { + * ml_lxm_session_h session = NULL; + * ml_lxm_prompt_h prompt = NULL; + * int ret; + * + * // Check availability first + * ml_lxm_availability_e status; + * ret = ml_lxm_check_availability(&status); + * if (ret != ML_ERROR_NONE || status != ML_LXM_AVAILABILITY_AVAILABLE) { + * std::cout << "LXM service not available" << std::endl; + * return -1; + * } + * + * // 1. Create session with config, instructions, and callback + * ret = ml_lxm_session_create("/path/to/config.json", "You are a helpful AI assistant", token_handler, NULL, &session); + * if (ret != ML_ERROR_NONE) { + * std::cout << "Failed to create session" << std::endl; + * return -1; + * } + * + * // 2. Create prompt + * ret = ml_lxm_prompt_create(&prompt); + * if (ret != ML_ERROR_NONE) { + * std::cout << "Failed to create prompt" << std::endl; + * ml_lxm_session_destroy(session); + * return -1; + * } + * + * // Add text to prompt + * ret = ml_lxm_prompt_append_text(prompt, "Explain quantum computing in simple terms"); + * if (ret != ML_ERROR_NONE) { + * std::cout << "Failed to append text to prompt" << std::endl; + * ml_lxm_prompt_destroy(prompt); + * ml_lxm_session_destroy(session); + * return -1; + * } + * + * // 3. Generate response with custom options + * ml_option_h options = NULL; + * ml_option_create(&options); + * ml_option_set(options, "temperature", g_strdup_printf("%f", 1.2), g_free); + * ml_option_set(options, "max_tokens", g_strdup_printf("%zu", (size_t)128), g_free); + * + * std::cout << "AI Response: "; + * ret = ml_lxm_session_respond(session, prompt, options); + * ml_option_destroy(options); + * if (ret != ML_ERROR_NONE) { + * std::cout << "Failed to generate response" << std::endl; + * } + * std::cout << std::endl; + * + * // 4. Cleanup + * ml_lxm_prompt_destroy(prompt); + * ml_lxm_session_destroy(session); + * + * return 0; + * } + * + * static void token_handler(ml_service_event_e event, + * ml_information_h event_data, + * void *user_data) { + * ml_tensors_data_h data = NULL; + * void *_raw = NULL; + * size_t _size = 0; + * int ret; + * + * switch (event) { + * case ML_SERVICE_EVENT_NEW_DATA: + * if (event_data != NULL) { + * ret = ml_information_get(event_data, "data", &data); + * if (ret == ML_ERROR_NONE) { + * ret = ml_tensors_data_get_tensor_data(data, 0U, &_raw, &_size); + * if (ret == ML_ERROR_NONE && _raw != NULL && _size > 0) { + * std::cout.write(static_cast(_raw), _size); + * std::cout.flush(); + * } + * } + * } + * break; + * default: + * break; + * } + * } + * @endcode + */ + +#ifndef __ML_LXM_SERVICE_INTERNAL_H__ +#define __ML_LXM_SERVICE_INTERNAL_H__ + +#include +#include +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @brief Enumeration for LXM service availability status. + */ +typedef enum +{ + ML_LXM_AVAILABILITY_AVAILABLE = 0, + ML_LXM_AVAILABILITY_DEVICE_NOT_ELIGIBLE, + ML_LXM_AVAILABILITY_SERVICE_DISABLED, + ML_LXM_AVAILABILITY_MODEL_NOT_READY, + ML_LXM_AVAILABILITY_UNKNOWN +} ml_lxm_availability_e; + +/** + * @brief Checks LXM service availability. + * @param[out] status Current availability status. + * @return ML_ERROR_NONE on success, error code otherwise. + */ +int ml_lxm_check_availability (ml_lxm_availability_e * status); + +/** + * @brief A handle for lxm session. + */ +typedef void *ml_lxm_session_h; + +/** + * @brief Creates an LXM session with mandatory callback. + * @param[in] config_path Path to configuration file. + * @param[in] instructions Initial instructions (optional). + * @param[in] callback Callback function for session events (mandatory). + * @param[in] user_data User data to be passed to the callback. + * @param[out] session Session handle. + * @return ML_ERROR_NONE on success. + * @note The callback parameter is mandatory and will be set during session creation. + */ +int ml_lxm_session_create (const char *config_path, const char *instructions, ml_service_event_cb callback, void *user_data, ml_lxm_session_h * session); + +/** + * @brief Destroys an LXM session. + * @param[in] session Session handle. + * @return ML_ERROR_NONE on success. + */ +int ml_lxm_session_destroy (ml_lxm_session_h session); + +/** + * @brief A handle for lxm prompt. + */ +typedef void *ml_lxm_prompt_h; + +/** + * @brief Creates a prompt object. + * @param[out] prompt Prompt handle. + * @return ML_ERROR_NONE on success. + */ +int ml_lxm_prompt_create (ml_lxm_prompt_h * prompt); + +/** + * @brief Appends text to a prompt. + * @param[in] prompt Prompt handle. + * @param[in] text Text to append. + * @return ML_ERROR_NONE on success. + */ +int ml_lxm_prompt_append_text (ml_lxm_prompt_h prompt, const char *text); + +/** + * @brief Appends an instruction to a prompt. + * @param[in] prompt Prompt handle. + * @param[in] instruction Instruction to append. + * @return ML_ERROR_NONE on success. + */ +int ml_lxm_prompt_append_instruction (ml_lxm_prompt_h prompt, const char *instruction); + +/** + * @brief Destroys a prompt object. + * @param[in] prompt Prompt handle. + * @return ML_ERROR_NONE on success. + */ +int ml_lxm_prompt_destroy (ml_lxm_prompt_h prompt); + +/** + * @brief Sets runtime instructions for a session. + * @param[in] session Session handle. + * @param[in] instructions New instructions. + * @return ML_ERROR_NONE on success. + */ +int ml_lxm_session_set_instructions (ml_lxm_session_h session, const char *instructions); + + +/** + * @brief Generates an token-streamed response. + * @param[in] session Session handle. + * @param[in] prompt Prompt handle. + * @param[in] options Generation parameters (ml_option_h). + * @return ML_ERROR_NONE on success. + * @note The callback should be set using ml_lxm_session_set_event_cb() before calling this function. + */ +int ml_lxm_session_respond (ml_lxm_session_h session, ml_lxm_prompt_h prompt, ml_option_h options); + +#ifdef __cplusplus +} +#endif +#endif +/* __ML_LXM_SERVICE_INTERNAL_H__ */ diff --git a/c/src/meson.build b/c/src/meson.build index e584cef3..9eead86c 100644 --- a/c/src/meson.build +++ b/c/src/meson.build @@ -1,7 +1,7 @@ nns_capi_common_srcs = files('ml-api-common.c', 'ml-api-inference-internal.c') nns_capi_single_srcs = files('ml-api-inference-single.c') nns_capi_pipeline_srcs = files('ml-api-inference-pipeline.c') -nns_capi_service_srcs = files('ml-api-service.c', 'ml-api-service-extension.c', 'ml-api-service-agent-client.c') +nns_capi_service_srcs = files('ml-api-service.c', 'ml-api-service-extension.c', 'ml-api-service-agent-client.c', 'ml-lxm-service.c') if support_nnstreamer_edge nns_capi_service_srcs += files('ml-api-service-query.c') diff --git a/c/src/ml-lxm-service.c b/c/src/ml-lxm-service.c new file mode 100644 index 00000000..85502f65 --- /dev/null +++ b/c/src/ml-lxm-service.c @@ -0,0 +1,287 @@ +/* SPDX-License-Identifier: Apache-2.0 */ +/** + * @file ml-lxm-service.c + * @date 23 JULY 2025 + * @brief Machine Learning LXM(LLM, LVM, etc.) Service API + * @see https://github.com/nnstreamer/api + * @author Hyunil Park + * @bug No known bugs except for NYI items + */ +#include +#include +#include +#include +#include "ml-lxm-service-internal.h" + +/** + * @brief Internal structure for the session. + */ +typedef struct +{ + ml_service_h service_handle; + char *config_path; + char *instructions; + ml_service_event_cb user_callback; /**< User callback function */ + void *user_data; /**< User data passed to callback */ +} ml_lxm_session_internal; + +/** + * @brief Internal structure for the prompt. + */ +typedef struct +{ + GString *text; +} ml_lxm_prompt_internal; + +/** + * @brief Checks LXM service availability. + * @param[out] status Current availability status. + * @return ML_ERROR_NONE on success, error code otherwise. + */ +int +ml_lxm_check_availability (ml_lxm_availability_e * status) +{ + *status = ML_LXM_AVAILABILITY_AVAILABLE; + + return ML_ERROR_NONE; +} + +/** + * @brief Creates an LXM session with mandatory callback. + * @param[in] config_path Path to configuration file. + * @param[in] instructions Initial instructions (optional). + * @param[in] callback Callback function for session events (mandatory). + * @param[in] user_data User data to be passed to the callback. + * @param[out] session Session handle. + * @return ML_ERROR_NONE on success. + * @note The callback parameter is mandatory and will be set during session creation. + */ +int +ml_lxm_session_create (const char *config_path, const char *instructions, + ml_service_event_cb callback, void *user_data, ml_lxm_session_h * session) +{ + ml_service_h handle; + ml_lxm_session_internal *s; + int ret; + + if (!session || !config_path || !callback) + return ML_ERROR_INVALID_PARAMETER; + + ret = ml_service_new (config_path, &handle); + if (ret != ML_ERROR_NONE) { + return ret; + } + + s = g_malloc0 (sizeof (ml_lxm_session_internal)); + if (!s) { + ml_service_destroy (handle); + return ML_ERROR_OUT_OF_MEMORY; + } + + s->config_path = g_strdup (config_path); + s->instructions = g_strdup (instructions); + s->service_handle = handle; + s->user_callback = callback; + s->user_data = user_data; + + ret = ml_service_set_event_cb (s->service_handle, callback, user_data); + if (ret != ML_ERROR_NONE) { + /* Cleanup on failure */ + ml_service_destroy (s->service_handle); + g_free (s->config_path); + g_free (s->instructions); + g_free (s); + return ret; + } + + *session = s; + return ML_ERROR_NONE; +} + + +/** + * @brief Destroys an LXM session. + * @param[in] session Session handle. + * @return ML_ERROR_NONE on success. + */ +int +ml_lxm_session_destroy (ml_lxm_session_h session) +{ + ml_lxm_session_internal *s; + + if (!session) + return ML_ERROR_INVALID_PARAMETER; + + s = (ml_lxm_session_internal *) session; + + ml_service_destroy (s->service_handle); + + g_free (s->config_path); + g_free (s->instructions); + g_free (s); + + return ML_ERROR_NONE; +} + +/** + * @brief Creates a prompt object. + * @param[out] prompt Prompt handle. + * @return ML_ERROR_NONE on success. + */ +int +ml_lxm_prompt_create (ml_lxm_prompt_h * prompt) +{ + ml_lxm_prompt_internal *p; + + if (!prompt) + return ML_ERROR_INVALID_PARAMETER; + + p = g_malloc0 (sizeof (ml_lxm_prompt_internal)); + if (!p) + return ML_ERROR_OUT_OF_MEMORY; + + p->text = g_string_new (""); + *prompt = p; + + return ML_ERROR_NONE; +} + +/** + * @brief Destroys a prompt object. + * @param[in] prompt Prompt handle. + * @return ML_ERROR_NONE on success. + */ +int +ml_lxm_prompt_destroy (ml_lxm_prompt_h prompt) +{ + ml_lxm_prompt_internal *p; + + if (!prompt) + return ML_ERROR_INVALID_PARAMETER; + + p = (ml_lxm_prompt_internal *) prompt; + g_string_free (p->text, TRUE); + g_free (p); + + return ML_ERROR_NONE; +} + +/** + * @brief Appends text to a prompt. + * @param[in] prompt Prompt handle. + * @param[in] text Text to append. + * @return ML_ERROR_NONE on success. + */ +int +ml_lxm_prompt_append_text (ml_lxm_prompt_h prompt, const char *text) +{ + ml_lxm_prompt_internal *p; + + if (!prompt || !text) + return ML_ERROR_INVALID_PARAMETER; + + p = (ml_lxm_prompt_internal *) prompt; + g_string_append (p->text, text); + + return ML_ERROR_NONE; +} + +/** + * @brief Appends an instruction to a prompt. + * @param[in] prompt Prompt handle. + * @param[in] instruction Instruction to append. + * @return ML_ERROR_NONE on success. + */ +int +ml_lxm_prompt_append_instruction (ml_lxm_prompt_h prompt, + const char *instruction) +{ + return ml_lxm_prompt_append_text (prompt, instruction); +} + +/** + * @brief Sets runtime instructions for a session. + * @param[in] session Session handle. + * @param[in] instructions New instructions. + * @return ML_ERROR_NONE on success. + */ +int +ml_lxm_session_set_instructions (ml_lxm_session_h session, + const char *instructions) +{ + ml_lxm_session_internal *s; + + if (!session) + return ML_ERROR_INVALID_PARAMETER; + + s = (ml_lxm_session_internal *) session; + g_free (s->instructions); + s->instructions = g_strdup (instructions); + + return ML_ERROR_NONE; +} + +/** + * @brief Generates an token-streamed response. + * @param[in] session Session handle. + * @param[in] prompt Prompt handle. + * @param[in] options Generation parameters (ml_option_h). + * @return ML_ERROR_NONE on success. + * @note The callback is automatically set during session creation. + */ +int +ml_lxm_session_respond (ml_lxm_session_h session, + ml_lxm_prompt_h prompt, ml_option_h options) +{ + int ret = ML_ERROR_NONE; + ml_lxm_session_internal *s; + ml_lxm_prompt_internal *p; + GString *full_input; + ml_tensors_info_h input_info = NULL; + ml_tensors_data_h input_data = NULL; + + if (!session || !prompt) + return ML_ERROR_INVALID_PARAMETER; + + s = (ml_lxm_session_internal *) session; + p = (ml_lxm_prompt_internal *) prompt; + + full_input = g_string_new (""); + if (s->instructions && s->instructions[0] != '\0') { + g_string_append (full_input, s->instructions); + g_string_append (full_input, "\n"); + } + g_string_append (full_input, p->text->str); + + /* Get input information */ + ret = ml_service_get_input_information (s->service_handle, NULL, &input_info); + if (ret != ML_ERROR_NONE) + goto error; + + /* Create input data */ + ret = ml_tensors_data_create (input_info, &input_data); + if (ret != ML_ERROR_NONE) { + goto error; + } + + /* Set tensor data */ + ret = + ml_tensors_data_set_tensor_data (input_data, 0U, full_input->str, + full_input->len); + if (ret != ML_ERROR_NONE) { + goto error; + } + + /* Send request */ + ret = ml_service_request (s->service_handle, NULL, input_data); + +error: + if (input_info) + ml_tensors_info_destroy (input_info); + if (input_data) + ml_tensors_data_destroy (input_data); + if (full_input) + g_string_free (full_input, TRUE); + + return ret; +}