From ca841f8b4ca6a5bcb36d1f80c5247357d84c3c5c Mon Sep 17 00:00:00 2001 From: LittleMouse Date: Tue, 31 Dec 2024 18:12:49 +0800 Subject: [PATCH 1/4] api_yolo add "exit" function. --- src/api/api_yolo.cpp | 21 +++++++++++++++++++++ src/api/api_yolo.h | 8 ++++++++ 2 files changed, 29 insertions(+) diff --git a/src/api/api_yolo.cpp b/src/api/api_yolo.cpp index 2774cc0..e7bdbe9 100644 --- a/src/api/api_yolo.cpp +++ b/src/api/api_yolo.cpp @@ -42,6 +42,27 @@ String ApiYolo::setup(ApiYoloSetupConfig_t config, String request_id) return work_id; } +String ApiYolo::exit(String work_id, String request_id) +{ + String cmd; + { + JsonDocument doc; + doc["request_id"] = request_id; + doc["work_id"] = work_id; + doc["action"] = "exit"; + serializeJson(doc, cmd); + } + + _module_msg->sendCmdAndWaitToTakeMsg( + cmd.c_str(), request_id, + [&work_id](ResponseMsg_t& msg) { + // Copy work id + work_id = msg.work_id; + }, + 100); + return work_id; +} + int ApiYolo::inference(String& work_id, uint8_t* input, size_t& raw_len, String request_id) { String cmd; diff --git a/src/api/api_yolo.h b/src/api/api_yolo.h index fbd6368..e065182 100644 --- a/src/api/api_yolo.h +++ b/src/api/api_yolo.h @@ -28,6 +28,14 @@ public: */ String setup(ApiYoloSetupConfig_t config = ApiYoloSetupConfig_t(), String request_id = "yolo_setup"); + /** + * @brief Exit module YOLO, return YOLO work_id + * + * @param work_id + * @param request_id + * @return String + */ + String exit(String work_id, String request_id = "yolo_exit"); /** * @brief Inference input data by module LLM * From ee5a466a82c2155cbe317fe60039a13938bc19ec Mon Sep 17 00:00:00 2001 From: LittleMouse Date: Mon, 6 Jan 2025 15:30:21 +0800 Subject: [PATCH 2/4] add vlm, depth anything module. add doc. update api. --- docs/cn.md | 640 +++++++++++++++++++++++++++++++++ docs/en.md | 41 +++ src/M5ModuleLLM.cpp | 2 + src/M5ModuleLLM.h | 14 + src/api/api_depth_anything.cpp | 106 ++++++ src/api/api_depth_anything.h | 60 ++++ src/api/api_melotts.cpp | 21 ++ src/api/api_melotts.h | 9 + src/api/api_vlm.cpp | 137 +++++++ src/api/api_vlm.h | 72 ++++ src/api/api_yolo.h | 1 + 11 files changed, 1103 insertions(+) create mode 100644 docs/cn.md create mode 100644 docs/en.md create mode 100644 src/api/api_depth_anything.cpp create mode 100644 src/api/api_depth_anything.h create mode 100644 src/api/api_vlm.cpp create mode 100644 src/api/api_vlm.h diff --git a/docs/cn.md b/docs/cn.md new file mode 100644 index 0000000..137bd59 --- /dev/null +++ b/docs/cn.md @@ -0,0 +1,640 @@ +# M5Module-LLM Arduino API + +[M5Module-LLM](https://github.com/m5stack/M5Module-LLM) Arduino驱动库API文档。 + +## M5ModuleLLM Class + +`M5ModuleLLM`用于初始化LLM Module, 并且提供内部成员用于快速初始化LLM的各个单元, 方便根据自己的需求构建应用。 + +```cpp +class M5ModuleLLM { +public: + bool begin(Stream* targetPort); + bool checkConnection(); + void update(); + + m5_module_llm::ApiSys sys; + m5_module_llm::ApiLlm llm; + m5_module_llm::ApiAudio audio; + m5_module_llm::ApiTts tts; + m5_module_llm::ApiTts melotts; + m5_module_llm::ApiKws kws; + m5_module_llm::ApiAsr asr; + m5_module_llm::ApiAsr yolo; + m5_module_llm::ModuleMsg msg; + m5_module_llm::ModuleComm comm; +private: +}; +``` + +### begin + +**函数原型:** + +```cpp +bool begin(Stream* targetPort); +``` + +**功能说明:** + +- 初始化LLM Module UART接口配置 + +**传入参数:** + +- Stream* targetPort: + - 传入Serial指针 + +**返回值:** + +- bool: + - true: 初始化成功 + - false: 初始化失败 + +### checkConnection + +**函数原型:** + +```cpp +bool checkConnection(); +``` + +**功能说明:** + +- 发送`sys.ping`指令, 检查LLM Module连接状态 + +**传入参数:** + +- null + +**返回值:** + +- bool: + - true: 模组响应 + - false: 模组无响应 + +### update + +**函数原型:** + +```cpp +void update(); +``` + +**功能说明:** + +- 拉取LLM Module UART响应数据, 该API需包含在Loop中循环执行。 + +**传入参数:** + +- null + +**返回值:** + +- null + +## ApiSys Class + +`M5ModuleLLM`的内部成员`ApiSys sys`用于控制SYS单元实现系统复位等操作。 + +### ping + +**函数原型:** + +```cpp +int ping(); +``` + +**功能说明:** + +- 发送`sys.ping`指令, 检查LLM Module连接状态 + +**传入参数:** + +- null + +**返回值:** + +- int: + - MODULE_LLM_OK / Error Code + +### reset + +**函数原型:** + +```cpp +int reset(bool waitResetFinish = true); +``` + +**功能说明:** + +- 发送`sys.reset`指令, 复位软件服务。 + +**传入参数:** + +- bool waitResetFinish: + - true:阻塞等待复位 + - false:非阻塞执行复位 + +**返回值:** + +- int: + - MODULE_LLM_OK / Error Code + +### reboot + +**函数原型:** + +```cpp +int reboot(); +``` + +**功能说明:** + +- 发送`sys.reboot`指令, 复位系统。 + +**传入参数:** + +- null + +**返回值:** + +- int: + - MODULE_LLM_OK / Error Code + +## ApiAudio Class + +`M5ModuleLLM`的内部成员`ApiAudio audio`用于控制AUDIO单元的初始化和配置。 + +### setup + +**函数原型:** + +```cpp +String setup(ApiAudioSetupConfig_t config = ApiAudioSetupConfig_t(), String request_id = "audio_setup"); +``` + +**功能说明:** + +- 初始化Audio单元, 开启系统声卡。(使用KWS和TTS前需开启该功能) + +**传入参数:** + +ApiAudioSetupConfig_t config: + +- LLM单元初始化配置: +- String request_id: + - 会话id, 使用默认即可。 + +```cpp +struct ApiAudioSetupConfig_t { + int capcard = 0; + int capdevice = 0; + float capVolume = 0.5; + int playcard = 0; + int playdevice = 1; + float playVolume = 0.15; +}; +``` + +| 参数 | 描述 | 输入值 | +|------------|----------|---------------------------------| +| capcard | 麦克风声卡的索引 | 系统默认声卡:0 | +| capdevice | 麦克风设备索引 | 板载硅麦:0 | +| capVolume | 输入的音量 | 0.0~10.0 (1中文模型: "sherpa-onnx-kws-zipformer-wenetspeech-3.3M-2024-01-01" | +| kws | KWS唤醒词文本设置 | 不允许中文/英文混合, 英文要求全大写 | +| enoutput | 启用UART输出 | 启用: true
禁用: false | + +**返回值:** + +- String: + - kws_work_id: kws单元work_id + +## ApiAsr Class + +`M5ModuleLLM`的内部成员`ApiAsr asr`用于控制ASR单元的初始化和配置。 + +### setup + +**函数原型:** + +```cpp +String setup(ApiAsrSetupConfig_t config = ApiAsrSetupConfig_t(), String request_id = "asr_setup"); +``` + +**功能说明:** + +- 初始化ASR单元, 开启语音转文本功能。 + +**传入参数:** + +ApiAsrSetupConfig_t config: + +- ASR单元初始化配置: +- String request_id: + - 会话id, 使用默认即可。 + +```cpp +struct ApiAsrSetupConfig_t { + String model = "sherpa-ncnn-streaming-zipformer-20M-2023-02-17"; + String response_format = "asr.utf-8.stream"; + String input = ["sys.pcm", "kws.1000"]; + bool enoutput = true; + float rule1 = 2.4; + float rule2 = 1.2; + float rule3 = 30.0; +}; +``` + +| 参数 | 描述 | 输入值 | +|-----------------|---------------|---------------------------------------------------------------------------------------------------------------------| +| model | 转换模型 | 英文模型: "sherpa-ncnn-streaming-zipformer-20M-2023-02-17"
中文模型: "sherpa-ncnn-streaming-zipformer-zh-14M-2023-02-23" | +| response_format | 输出格式 | 普通输出: "asr.utf-8"
流式输出: "asr.utf-8.stream" | +| input | 输入 | KWS唤醒输入: "kws.xxx"(输入kws单元的work_id)
板载麦克风输入: "sys.pcm"
UART流式输入: "asr.wav.stream.base64" | +| rule1 | 唤醒到未识别到内容超时时间 | 单位:秒 | +| rule2 | 识别最大间隔时间 | 单位:秒 | +| rule3 | 识别最长超时时间 | 单位:秒 | +| enoutput | 启用UART输出 | 启用: true
禁用: false | + +**返回值:** + +- String: + - asr_work_id: asr单元work_id + +## ApiLlm Class + +`M5ModuleLLM`的内部成员`ApiLlm llm`用于控制LLM单元的初始化和配置。 + +### setup + +**函数原型:** + +```cpp +String setup(ApiLlmSetupConfig_t config = ApiLlmSetupConfig_t(), String request_id = "llm_setup"); +``` + +**功能说明:** + +- 初始化LLM单元, 支持配置LLM单元输入输出数据方式。 + +**传入参数:** + +- ApiLlmSetupConfig_t config: + - LLM单元初始化配置: +- String request_id: + - 会话id, 使用默认即可。 + +```cpp +struct ApiLlmSetupConfig_t { + String prompt; + String model = "qwen2.5-0.5B-prefill-20e"; + String response_format = "llm.utf-8.stream"; + String input = "llm.utf-8"; + bool enoutput = true; + bool enkws = true; + int max_token_len = 127; +}; +``` + +| 参数 | 描述 | 输入值 | +|-----------------|-------------------------|-------------------------------------------------------------------------------------------------| +| model | 转换模型 | 预置模型 "qwen2.5-0.5B-prefill-20e" | +| response_format | 输出格式 | 普通输出: "llm.utf-8"
流式输出: "llm.utf-8.stream" | +| input | 输入 | ASR输入: "asr.xxx"(输入asr单元的work_id)
UART输入: "llm.utf-8"
KWS唤醒打断: "kws.xxx"(输入kws单元的work_id) | +| enkws | KWS唤醒是否终止过程 | KWS打断过程: true
KWS不打断过程: false | +| max_length | 配置最大输出token(最大返回推理文本长度) | 最大值: 1024, 推荐使用127 | +| prompt | 模型初始化提示词 | String | +| enoutput | 启用UART输出 | 启用: true
禁用: false | + +**返回值:** + +- String: + - llm_work_id: llm单元work_id + +### inference + +**函数原型:** + +```cpp +int inference(String work_id, String input, String request_id = "llm_inference"); +``` + +**功能说明:** + +- 输入数据, 开始推理。返回结果内容将进入`M5ModuleLLM.msg`中的`responseMsgList`列表容器中。 + +**传入参数:** + +- String work_id: + - 调用的LLM单元work_id +- String input: + - 输入文本 +- String request_id: + - 会话ID, 当同时存在多个会话的时候用于区分。 + +**返回值:** + +- int: + - MODULE_LLM_OK / Error Code + +### inferenceAndWaitResult + +**函数原型:** + +```cpp +int inferenceAndWaitResult(String work_id, String input, std::function onResult, uint32_t timeout = 5000, String request_id = "llm_inference"); +``` + +**功能说明:** + +- 输入数据, 开始推理。并阻塞等待返回结果, 然后调用callback函数。 + +**传入参数:** + +- String work_id: + - 调用的LLM单元work_id +- String input: + - 输入文本 +- void onResult(String&) + - 推理结果callback函数 +- uint32_t timeout: + - 等待推理超时时间 +- String request_id: + - 会话ID, 当同时存在多个会话的时候用于区分。 + +**返回值:** + +- int: + - MODULE_LLM_OK / Error Code + +## ApiTts Class + +`M5ModuleLLM`的内部成员`ApiTts tts`用于控制TTS单元的初始化和配置。 + +### setup + +**函数原型:** + +```cpp +String setup(ApiTtsSetupConfig_t config = ApiTtsSetupConfig_t(), String request_id = "tts_setup"); +``` + +**功能说明:** + +- 初始化TTS单元, 开启文本转语音功能。 + +**传入参数:** + +ApiTtsSetupConfig_t config: + +- LLM单元初始化配置: +- String request_id: + - 会话id, 使用默认即可。 + +```cpp +struct ApiTtsSetupConfig_t { + String model = "single_speaker_english_fast"; + String response_format = "tts.base64.wav"; + String input = "tts.utf-8.stream"; + bool enoutput = true; + bool enkws = true; +}; +``` + +| 参数 | 描述 | 输入值 | +|----------|-------------|------------------------------------------------------------------------------------------| +| model | 转换模型 | 英文模型: "single_speaker_english_fast"
中文模型: "single_speaker_fast" | +| input | 输入 | LLM输入: "llm.xxx"(输入llm单元的work_id)
UART输入: "tts.utf-8"
UART流式输入: "tts.utf-8.stream" | +| enkws | KWS唤醒是否终止过程 | KWS打断过程: true
KWS不打断过程: false | +| enoutput | 启用UART输出 | 启用: true
禁用: false | + +**返回值:** + +- String: + - tts_work_id: tts单元work_id + +### inference + +**函数原型:** + +```cpp +int inference(String work_id, String input, uint32_t timeout = 0, String request_id = "tts_inference"); +``` + +**功能说明:** + +- 输入数据, 开始推理转换, 完成后将自动播放至扬声器。 + +**传入参数:** + +- String work_id: + - 调用的TTS单元work_id +- String input: + - 输入文本 +- uint32_t timeout: + - 等待推理超时时间 +- String request_id: + - 会话ID, 当同时存在多个会话的时候用于区分。 + +**返回值:** + +- int: + - MODULE_LLM_OK / Error Code + +## ModuleMsg Class + +`M5ModuleLLM`的内部成员`ModuleMsg msg`提供了`responseMsgList`容器用于用于缓存接收LLM Module返回的各种信息。参考以下案例,在主循环中遍历获取返回结果。 + +```cpp +void loop() +{ + module_llm.update(); + + // Handle response msg + for (auto& msg : module_llm.msg.responseMsgList) { + // KWS msg + if (msg.work_id == kws_work_id) { + Serial.printf(">> Keyword detected\n"); + } + + // ASR msg + if (msg.work_id == asr_work_id) { + if (msg.object == "asr.utf-8.stream") { + // Parse and get asr result + JsonDocument doc; + deserializeJson(doc, msg.raw_msg); + String asr_result = doc["data"]["delta"].as(); + Serial.printf(">> %s\n", asr_result.c_str()); + } + } + } + module_llm.msg.responseMsgList.clear(); +} + +``` + +## VoiceAssistant Class + +`M5ModuleLLM_VoiceAssistant`用于快速创建LLM语音助手实例, 快速实现KWS(语音唤醒)->ASR(语音转文本)->LLM(大模型推理)->TTS( +文本转语音)。 + +- 初始化时候只需要将`M5ModuleLLM`实例传入构造函数, 并注册对应事件的回调函数即可完成语音助手创建。 + +```cpp +/* + * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +#include +#include +#include + +M5ModuleLLM module_llm; +M5ModuleLLM_VoiceAssistant voice_assistant(&module_llm); + +/* On ASR data callback */ +void on_asr_data_input(String data, bool isFinish, int index) +{ + M5.Display.setTextColor(TFT_GREEN, TFT_BLACK); + M5.Display.printf(">> %s\n", data.c_str()); + + /* If ASR data is finish */ + if (isFinish) { + M5.Display.setTextColor(TFT_YELLOW, TFT_BLACK); + M5.Display.print(">> "); + } +}; + +/* On LLM data callback */ +void on_llm_data_input(String data, bool isFinish, int index) +{ + M5.Display.print(data); + + /* If LLM data is finish */ + if (isFinish) { + M5.Display.print("\n"); + } +}; + +void setup() +{ + M5.begin(); + M5.Display.setTextSize(2); + M5.Display.setTextScroll(true); + + /* Init module serial port */ + Serial2.begin(115200, SERIAL_8N1, 16, 17); // Basic + // Serial2.begin(115200, SERIAL_8N1, 13, 14); // Core2 + // Serial2.begin(115200, SERIAL_8N1, 18, 17); // CoreS3 + + /* Init module */ + module_llm.begin(&Serial2); + + /* Make sure module is connected */ + M5.Display.printf(">> Check ModuleLLM connection..\n"); + while (1) { + if (module_llm.checkConnection()) { + break; + } + } + + /* Begin voice assistant preset */ + M5.Display.printf(">> Begin voice assistant..\n"); + int ret = voice_assistant.begin("HELLO"); + if (ret != MODULE_LLM_OK) { + while (1) { + M5.Display.setTextColor(TFT_RED); + M5.Display.printf(">> Begin voice assistant failed\n"); + } + } + + /* Register on ASR data callback function */ + voice_assistant.onAsrDataInput(on_asr_data_input); + + /* Register on LLM data callback function */ + voice_assistant.onLlmDataInput(on_llm_data_input); + + M5.Display.printf(">> Voice assistant ready\n"); +} + +void loop() +{ + /* Keep voice assistant preset update */ + voice_assistant.update(); +} +``` + +## Error Code + +```cpp +enum ModuleLLMErrorCode_t { + MODULE_LLM_OK = 0, + MODULE_LLM_RESET_WARN = -1, + MODULE_LLM_JSON_FORMAT_ERROR = -2, + MODULE_LLM_ACTION_MATCH_FAILED = -3, + MODULE_LLM_INFERENCE_DATA_PUSH_FAILED = -4, + MODULE_LLM_MODEL_LOADING_FAILED = -5, + MODULE_LLM_UNIT_NOT_EXIST = -6, + MODULE_LLM_UNKNOWN_OPERATION = -7, + MODULE_LLM_UNIT_RESOURCE_ALLOCATION_FAILED = -8, + MODULE_LLM_UNIT_CALL_FAILED = -9, + MODULE_LLM_MODEL_INIT_FAILED = -10, + MODULE_LLM_MODEL_RUN_FAILED = -11, + MODULE_LLM_MODULE_NOT_INITIALISED = -12, + MODULE_LLM_MODULE_ALREADY_WORKING = -13, + MODULE_LLM_MODULE_NOT_WORKING = -14, + MODULE_LLM_NO_UPDATEABLE_MODULES = -15, + MODULE_LLM_NO_MODULES_AVAILABLE_FOR_UPDATE = -16, + MODULE_LLM_FILE_OPEN_FAILED = -17, + MODULE_LLM_WAIT_RESPONSE_TIMEOUT = -97, + MODULE_LLM_RESPONSE_PARSE_FAILED = -98, + MODULE_LLM_ERROR_NONE = -99, +}; +``` + diff --git a/docs/en.md b/docs/en.md new file mode 100644 index 0000000..1714e53 --- /dev/null +++ b/docs/en.md @@ -0,0 +1,41 @@ +# LLM Module Arduino Quick Start + +## Overview + +The `LLM Module` can be used with various M5 controllers. This tutorial demonstrates how to control the LLM Module using the `M5Core` series in the `Arduino IDE` with the LLM Module driver library. + + + +## Environment Setup + +- 1.Arduino IDE Installation: Refer to the [Arduino IDE Installation Guide](/en/arduino/arduino_ide) to complete the IDE installation. + +- 2.Board Manager Installation: Refer to the [Basic Environment Setup Guide](/en/arduino/arduino_board) to complete the M5Stack board manager installation and select the `M5Core` development board. + + + +- 3.Library Installation: Refer to the [Library Management Guide](/en/arduino/arduino_library) to install the `LLM Module` driver library. (Follow prompts to install the dependency library `M5Unified`) + + + + +## Program Compilation & Upload + +Open the example program "kws_asr" in the driver library, click the upload button, and the program will automatically compile and upload.The wake-up word used in the example program is "HELLO". After waiting for the device to be initialized, it will be woken up using the keyword. + + + + + + +- Examples: + - `kws_asr`: Uses KWS to wake up and triggers ASR for speech-to-text conversion. (KWS+ASR) + - `text_assistant`: Inputs text into the LLM model, performs inference, and outputs the result in text form. (LLM) + - `tts`: Uses the TTS unit to convert text to speech for playback. (TTS) + - `voice_assistant`: Uses KWS to wake up, triggers ASR for speech-to-text conversion, inputs the converted text into the LLM for inference, and outputs the inference result through TTS as speech. (KWS+ASR+LLM+TTS) + +## Related Links + +- [LLM Module Arduino Lib](https://github.com/m5stack/M5Module-LLM) +- [LLM Module Arduino Lib API](/en/guide/llm/llm/arduino_api) + diff --git a/src/M5ModuleLLM.cpp b/src/M5ModuleLLM.cpp index 0bac44b..2ac9119 100644 --- a/src/M5ModuleLLM.cpp +++ b/src/M5ModuleLLM.cpp @@ -12,6 +12,7 @@ bool M5ModuleLLM::begin(Stream* serialPort) msg.init(&comm); sys.init(&msg); llm.init(&msg); + vlm.init(&msg); audio.init(&msg); tts.init(&msg); melotts.init(&msg); @@ -19,6 +20,7 @@ bool M5ModuleLLM::begin(Stream* serialPort) asr.init(&msg); yolo.init(&msg); camera.init(&msg); + depthanything.init(&msg); return true; } diff --git a/src/M5ModuleLLM.h b/src/M5ModuleLLM.h index 42bae02..c652089 100644 --- a/src/M5ModuleLLM.h +++ b/src/M5ModuleLLM.h @@ -9,12 +9,14 @@ #include "utils/msg.h" #include "api/api_sys.h" #include "api/api_llm.h" +#include "api/api_vlm.h" #include "api/api_audio.h" #include "api/api_tts.h" #include "api/api_melotts.h" #include "api/api_kws.h" #include "api/api_asr.h" #include "api/api_yolo.h" +#include "api/api_depth_anything.h" #include "api/api_camera.h" #include "api/api_version.h" @@ -55,6 +57,12 @@ public: */ m5_module_llm::ApiLlm llm; + /** + * @brief VLM module api set + * + */ + m5_module_llm::ApiVlm vlm; + /** * @brief Audio module api set * @@ -97,6 +105,12 @@ public: */ m5_module_llm::ApiYolo yolo; + /** + * @brief DepthAnything module api set + * + */ + m5_module_llm::ApiDepthAnything depthanything; + /** * @brief MSG module to handle module response message * diff --git a/src/api/api_depth_anything.cpp b/src/api/api_depth_anything.cpp new file mode 100644 index 0000000..b8ceb2d --- /dev/null +++ b/src/api/api_depth_anything.cpp @@ -0,0 +1,106 @@ +/* + * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +#include "api_depth_anything.h" + +using namespace m5_module_llm; + +void ApiDepthAnything::init(ModuleMsg* moduleMsg) +{ + _module_msg = moduleMsg; +} + +String ApiDepthAnything::setup(ApiDepthAnythingSetupConfig_t config, String request_id) +{ + String cmd; + { + JsonDocument doc; + doc["request_id"] = request_id; + doc["work_id"] = "depth_anything"; + doc["action"] = "setup"; + doc["object"] = "depth_anything.setup"; + doc["data"]["model"] = config.model; + doc["data"]["response_format"] = config.response_format; + JsonArray inputArray = doc["data"]["input"].to(); + for (const String& str : config.input) { + inputArray.add(str); + } + doc["data"]["enoutput"] = config.enoutput; + serializeJson(doc, cmd); + } + + String work_id; + _module_msg->sendCmdAndWaitToTakeMsg( + cmd.c_str(), request_id, + [&work_id](ResponseMsg_t& msg) { + // Copy work id + work_id = msg.work_id; + }, + 5000); + return work_id; +} + +int ApiDepthAnything::inference(String& work_id, uint8_t* input, size_t& raw_len, String request_id) +{ + String cmd; + { + JsonDocument doc; + doc["RAW"] = raw_len; + doc["request_id"] = request_id; + doc["work_id"] = work_id; + doc["action"] = "inference"; + doc["object"] = "cv.jpeg.base64"; + serializeJson(doc, cmd); + } + + _module_msg->sendCmd(cmd.c_str()); + _module_msg->sendRaw(input, raw_len); + return MODULE_LLM_OK; +} + +int ApiDepthAnything::inferenceAndWaitResult(String& work_id, uint8_t* input, size_t& raw_len, + std::function onResult, uint32_t timeout, String request_id) +{ + inference(work_id, input, raw_len, request_id); + + uint32_t time_out_count = millis(); + bool is_time_out = false; + bool is_msg_finish = false; + while (1) { + _module_msg->update(); + _module_msg->takeMsg(request_id, [&time_out_count, &is_msg_finish, &onResult](ResponseMsg_t& msg) { + String response_msg; + { + JsonDocument doc; + deserializeJson(doc, msg.raw_msg); + response_msg = doc["data"]["delta"].as(); + if (!doc["data"]["finish"].isNull()) { + is_msg_finish = doc["data"]["finish"]; + if (is_msg_finish) { + response_msg += '\n'; + } + } + } + if (onResult) { + onResult(response_msg); + } + time_out_count = millis(); + }); + + if (is_msg_finish) { + break; + } + + if (millis() - time_out_count > timeout) { + is_time_out = true; + break; + } + } + + if (is_time_out) { + return MODULE_LLM_WAIT_RESPONSE_TIMEOUT; + } + return MODULE_LLM_OK; +} diff --git a/src/api/api_depth_anything.h b/src/api/api_depth_anything.h new file mode 100644 index 0000000..c1138e7 --- /dev/null +++ b/src/api/api_depth_anything.h @@ -0,0 +1,60 @@ +/* + * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +#pragma once +#include "../utils/msg.h" +#include + +namespace m5_module_llm { +struct ApiDepthAnythingSetupConfig_t { + String model = "depth_anything"; + String response_format = "jpeg.base64.stream"; + std::vector input = {"depth_anything.jpeg.raw"}; + bool enoutput = true; +}; + +class ApiDepthAnything { +public: + void init(ModuleMsg* moduleMsg); + + /** + * @brief Setup module YOLO, return YOLO work_id + * + * @param config + * @param request_id + * @return String + */ + String setup(ApiDepthAnythingSetupConfig_t config = ApiDepthAnythingSetupConfig_t(), + String request_id = "depth_anything_setup"); + + /** + * @brief Inference input data by module LLM + * + * @param raw_len + * @param work_id + * @param input + * @param request_id + * @return int + */ + int inference(String& work_id, uint8_t* input, size_t& raw_len, String request_id = "depth_anything_inference"); + + /** + * @brief Inference input data by module LLM, and wait inference result + * + * @param raw_len + * @param work_id + * @param input + * @param onResult On inference result callback + * @param timeout + * @param request_id + * @return int + */ + int inferenceAndWaitResult(String& work_id, uint8_t* input, size_t& raw_len, std::function onResult, + uint32_t timeout = 5000, String request_id = "depth_anything_inference"); + +private: + ModuleMsg* _module_msg = nullptr; +}; +} // namespace m5_module_llm diff --git a/src/api/api_melotts.cpp b/src/api/api_melotts.cpp index b4e6f8b..adcadbf 100644 --- a/src/api/api_melotts.cpp +++ b/src/api/api_melotts.cpp @@ -44,6 +44,27 @@ String ApiMelotts::setup(ApiMelottsSetupConfig_t config, String request_id, Stri return work_id; } +String ApiMelotts::exit(String work_id, String request_id) +{ + String cmd; + { + JsonDocument doc; + doc["request_id"] = request_id; + doc["work_id"] = work_id; + doc["action"] = "exit"; + serializeJson(doc, cmd); + } + + _module_msg->sendCmdAndWaitToTakeMsg( + cmd.c_str(), request_id, + [&work_id](ResponseMsg_t& msg) { + // Copy work id + work_id = msg.work_id; + }, + 100); + return work_id; +} + int ApiMelotts::inference(String work_id, String input, uint32_t timeout, String request_id) { String cmd; diff --git a/src/api/api_melotts.h b/src/api/api_melotts.h index 7a35219..22ab4ea 100644 --- a/src/api/api_melotts.h +++ b/src/api/api_melotts.h @@ -31,6 +31,15 @@ public: String setup(ApiMelottsSetupConfig_t config = ApiMelottsSetupConfig_t(), String request_id = "melotts_setup", String language = "en_US"); + /** + * @brief Exit module TTS, return TTS work_id + * + * @param work_id + * @param request_id + * @return String + */ + String exit(String work_id, String request_id = "yolo_exit"); + /** * @brief Inference input data by TTS module * diff --git a/src/api/api_vlm.cpp b/src/api/api_vlm.cpp new file mode 100644 index 0000000..651b986 --- /dev/null +++ b/src/api/api_vlm.cpp @@ -0,0 +1,137 @@ +/* + * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +#include "api_vlm.h" +#include "api_version.h" + +using namespace m5_module_llm; + +void ApiVlm::init(ModuleMsg* moduleMsg) +{ + _module_msg = moduleMsg; +} + +String ApiVlm::setup(ApiVlmSetupConfig_t config, String request_id) +{ + String cmd; + { + JsonDocument doc; + doc["request_id"] = request_id; + doc["work_id"] = "vlm"; + doc["action"] = "setup"; + doc["object"] = "vlm.setup"; + doc["data"]["model"] = config.model; + doc["data"]["response_format"] = config.response_format; + doc["data"]["enoutput"] = config.enoutput; + doc["data"]["enkws"] = config.enkws; + doc["data"]["max_token_len"] = config.max_token_len; + doc["data"]["prompt"] = config.prompt; + if (!llm_version) { + doc["data"]["model"] = "qwen2.5-0.5b"; + doc["data"]["input"] = config.input[0]; + } else { + JsonArray inputArray = doc["data"]["input"].to(); + for (const String& str : config.input) { + inputArray.add(str); + } + } + serializeJson(doc, cmd); + } + + String llm_work_id; + _module_msg->sendCmdAndWaitToTakeMsg( + cmd.c_str(), request_id, + [&llm_work_id](ResponseMsg_t& msg) { + // Copy work id + llm_work_id = msg.work_id; + }, + 20000); + return llm_work_id; +} + +String ApiVlm::exit(String work_id, String request_id) +{ + String cmd; + { + JsonDocument doc; + doc["request_id"] = request_id; + doc["work_id"] = work_id; + doc["action"] = "exit"; + serializeJson(doc, cmd); + } + + _module_msg->sendCmdAndWaitToTakeMsg( + cmd.c_str(), request_id, + [&work_id](ResponseMsg_t& msg) { + // Copy work id + work_id = msg.work_id; + }, + 100); + return work_id; +} + +int ApiVlm::inference(String work_id, String input, String request_id) +{ + String cmd; + { + JsonDocument doc; + doc["request_id"] = request_id; + doc["work_id"] = work_id; + doc["action"] = "inference"; + doc["object"] = "vlm.utf-8.stream"; + doc["data"]["delta"] = input; + doc["data"]["index"] = 0; + doc["data"]["finish"] = true; + serializeJson(doc, cmd); + } + + _module_msg->sendCmd(cmd.c_str()); + return MODULE_LLM_OK; +} + +int ApiVlm::inferenceAndWaitResult(String work_id, String input, std::function onResult, + uint32_t timeout, String request_id) +{ + inference(work_id, input, request_id); + + uint32_t time_out_count = millis(); + bool is_time_out = false; + bool is_msg_finish = false; + while (1) { + _module_msg->update(); + _module_msg->takeMsg(request_id, [&time_out_count, &is_msg_finish, &onResult](ResponseMsg_t& msg) { + String response_msg; + { + JsonDocument doc; + deserializeJson(doc, msg.raw_msg); + response_msg = doc["data"]["delta"].as(); + if (!doc["data"]["finish"].isNull()) { + is_msg_finish = doc["data"]["finish"]; + if (is_msg_finish) { + response_msg += '\n'; + } + } + } + if (onResult) { + onResult(response_msg); + } + time_out_count = millis(); + }); + + if (is_msg_finish) { + break; + } + + if (millis() - time_out_count > timeout) { + is_time_out = true; + break; + } + } + + if (is_time_out) { + return MODULE_LLM_WAIT_RESPONSE_TIMEOUT; + } + return MODULE_LLM_OK; +} diff --git a/src/api/api_vlm.h b/src/api/api_vlm.h new file mode 100644 index 0000000..01878d7 --- /dev/null +++ b/src/api/api_vlm.h @@ -0,0 +1,72 @@ +/* + * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +#pragma once +#include "../utils/msg.h" +#include + +namespace m5_module_llm { + +struct ApiVlmSetupConfig_t { + String prompt; + String model = "internvl2.5-1B-ax630c"; + String response_format = "vlm.utf-8.stream"; + std::vector input = {"vlm.utf-8.stream"}; + bool enoutput = true; + bool enkws = true; + // int max_token_len = 127; + int max_token_len = 255; +}; + +class ApiVlm { +public: + void init(ModuleMsg* moduleMsg); + + /** + * @brief Setup module LLM, return LLM work_id + * + * @param config + * @param request_id + * @return String + */ + String setup(ApiVlmSetupConfig_t config = ApiVlmSetupConfig_t(), String request_id = "vlm_setup"); + + /** + * @brief Exit module YOLO, return YOLO work_id + * + * @param work_id + * @param request_id + * @return String + */ + String exit(String work_id, String request_id = "vlm_exit"); + + /** + * @brief Inference input data by module LLM + * + * @param work_id + * @param input + * @param request_id + * @return int + */ + int inference(String work_id, String input, String request_id = "vlm_inference"); + + /** + * @brief Inference input data by module LLM, and wait inference result + * + * @param work_id + * @param input + * @param onResult On inference result callback + * @param timeout + * @param request_id + * @return int + */ + int inferenceAndWaitResult(String work_id, String input, std::function onResult, + uint32_t timeout = 5000, String request_id = "vlm_inference"); + +private: + ModuleMsg* _module_msg = nullptr; +}; + +} // namespace m5_module_llm diff --git a/src/api/api_yolo.h b/src/api/api_yolo.h index e065182..ebccc1a 100644 --- a/src/api/api_yolo.h +++ b/src/api/api_yolo.h @@ -36,6 +36,7 @@ public: * @return String */ String exit(String work_id, String request_id = "yolo_exit"); + /** * @brief Inference input data by module LLM * From af55e48bc04f4da3edd97e2b220418f885fa4fd3 Mon Sep 17 00:00:00 2001 From: LittleMouse Date: Mon, 6 Jan 2025 17:22:47 +0800 Subject: [PATCH 3/4] update module api. --- src/api/api_asr.cpp | 21 +++++++++++++++++++++ src/api/api_asr.h | 9 +++++++++ src/api/api_audio.cpp | 21 +++++++++++++++++++++ src/api/api_audio.h | 11 ++++++++++- src/api/api_camera.cpp | 21 +++++++++++++++++++++ src/api/api_camera.h | 11 ++++++++++- src/api/api_depth_anything.cpp | 21 +++++++++++++++++++++ src/api/api_depth_anything.h | 11 ++++++++++- src/api/api_kws.cpp | 21 +++++++++++++++++++++ src/api/api_kws.h | 9 +++++++++ src/api/api_llm.cpp | 21 +++++++++++++++++++++ src/api/api_llm.h | 9 +++++++++ src/api/api_melotts.h | 2 +- src/api/api_tts.cpp | 21 +++++++++++++++++++++ src/api/api_tts.h | 9 +++++++++ src/api/api_vlm.h | 8 ++++---- 16 files changed, 218 insertions(+), 8 deletions(-) diff --git a/src/api/api_asr.cpp b/src/api/api_asr.cpp index 900d531..2766fa6 100644 --- a/src/api/api_asr.cpp +++ b/src/api/api_asr.cpp @@ -51,3 +51,24 @@ String ApiAsr::setup(ApiAsrSetupConfig_t config, String request_id, String langu 10000); return work_id; } + +String ApiAsr::exit(String work_id, String request_id) +{ + String cmd; + { + JsonDocument doc; + doc["request_id"] = request_id; + doc["work_id"] = work_id; + doc["action"] = "exit"; + serializeJson(doc, cmd); + } + + _module_msg->sendCmdAndWaitToTakeMsg( + cmd.c_str(), request_id, + [&work_id](ResponseMsg_t& msg) { + // Copy work id + work_id = msg.work_id; + }, + 100); + return work_id; +} diff --git a/src/api/api_asr.h b/src/api/api_asr.h index 34d2364..0cd34a0 100644 --- a/src/api/api_asr.h +++ b/src/api/api_asr.h @@ -34,6 +34,15 @@ public: String setup(ApiAsrSetupConfig_t config = ApiAsrSetupConfig_t(), String request_id = "asr_setup", String language = "en_US"); + /** + * @brief Exit module ASR, return ASR work_id + * + * @param work_id + * @param request_id + * @return String + */ + String exit(String work_id, String request_id = "asr_exit"); + private: ModuleMsg* _module_msg = nullptr; }; diff --git a/src/api/api_audio.cpp b/src/api/api_audio.cpp index 343a586..caae9ae 100644 --- a/src/api/api_audio.cpp +++ b/src/api/api_audio.cpp @@ -40,3 +40,24 @@ String ApiAudio::setup(ApiAudioSetupConfig_t config, String request_id) 5000); return work_id; } + +String ApiAudio::exit(String work_id, String request_id) +{ + String cmd; + { + JsonDocument doc; + doc["request_id"] = request_id; + doc["work_id"] = work_id; + doc["action"] = "exit"; + serializeJson(doc, cmd); + } + + _module_msg->sendCmdAndWaitToTakeMsg( + cmd.c_str(), request_id, + [&work_id](ResponseMsg_t& msg) { + // Copy work id + work_id = msg.work_id; + }, + 100); + return work_id; +} diff --git a/src/api/api_audio.h b/src/api/api_audio.h index a1ab88f..2bf9eff 100644 --- a/src/api/api_audio.h +++ b/src/api/api_audio.h @@ -23,7 +23,7 @@ public: void init(ModuleMsg* moduleMsg); /** - * @brief Setup module audio, return work_id + * @brief Setup module audio, return audio work_id * * @param config * @param request_id @@ -31,6 +31,15 @@ public: */ String setup(ApiAudioSetupConfig_t config = ApiAudioSetupConfig_t(), String request_id = "audio_setup"); + /** + * @brief Exit module audio, return audio work_id + * + * @param work_id + * @param request_id + * @return String + */ + String exit(String work_id, String request_id = "audio_exit"); + private: ModuleMsg* _module_msg = nullptr; }; diff --git a/src/api/api_camera.cpp b/src/api/api_camera.cpp index 8b2c7da..395dc8b 100644 --- a/src/api/api_camera.cpp +++ b/src/api/api_camera.cpp @@ -39,3 +39,24 @@ String ApiCamera::setup(ApiCameraSetupConfig_t config, String request_id) 5000); return work_id; } + +String ApiCamera::exit(String work_id, String request_id) +{ + String cmd; + { + JsonDocument doc; + doc["request_id"] = request_id; + doc["work_id"] = work_id; + doc["action"] = "exit"; + serializeJson(doc, cmd); + } + + _module_msg->sendCmdAndWaitToTakeMsg( + cmd.c_str(), request_id, + [&work_id](ResponseMsg_t& msg) { + // Copy work id + work_id = msg.work_id; + }, + 100); + return work_id; +} diff --git a/src/api/api_camera.h b/src/api/api_camera.h index f0d0ea3..973de50 100644 --- a/src/api/api_camera.h +++ b/src/api/api_camera.h @@ -22,7 +22,7 @@ public: void init(ModuleMsg* moduleMsg); /** - * @brief Setup module camera, return work_id + * @brief Setup module camera, return camera work_id * * @param config * @param request_id @@ -30,6 +30,15 @@ public: */ String setup(ApiCameraSetupConfig_t config = ApiCameraSetupConfig_t(), String request_id = "camera_setup"); + /** + * @brief Exit module camera, return camera work_id + * + * @param work_id + * @param request_id + * @return String + */ + String exit(String work_id, String request_id = "camera_exit"); + private: ModuleMsg* _module_msg = nullptr; }; diff --git a/src/api/api_depth_anything.cpp b/src/api/api_depth_anything.cpp index b8ceb2d..85d0a97 100644 --- a/src/api/api_depth_anything.cpp +++ b/src/api/api_depth_anything.cpp @@ -42,6 +42,27 @@ String ApiDepthAnything::setup(ApiDepthAnythingSetupConfig_t config, String requ return work_id; } +String ApiDepthAnything::exit(String work_id, String request_id) +{ + String cmd; + { + JsonDocument doc; + doc["request_id"] = request_id; + doc["work_id"] = work_id; + doc["action"] = "exit"; + serializeJson(doc, cmd); + } + + _module_msg->sendCmdAndWaitToTakeMsg( + cmd.c_str(), request_id, + [&work_id](ResponseMsg_t& msg) { + // Copy work id + work_id = msg.work_id; + }, + 100); + return work_id; +} + int ApiDepthAnything::inference(String& work_id, uint8_t* input, size_t& raw_len, String request_id) { String cmd; diff --git a/src/api/api_depth_anything.h b/src/api/api_depth_anything.h index c1138e7..36cb450 100644 --- a/src/api/api_depth_anything.h +++ b/src/api/api_depth_anything.h @@ -20,7 +20,7 @@ public: void init(ModuleMsg* moduleMsg); /** - * @brief Setup module YOLO, return YOLO work_id + * @brief Setup module DepthAnything, return DepthAnything work_id * * @param config * @param request_id @@ -29,6 +29,15 @@ public: String setup(ApiDepthAnythingSetupConfig_t config = ApiDepthAnythingSetupConfig_t(), String request_id = "depth_anything_setup"); + /** + * @brief Exit module DepthAnything, return DepthAnything work_id + * + * @param work_id + * @param request_id + * @return String + */ + String exit(String work_id, String request_id = "depth_anything_exit"); + /** * @brief Inference input data by module LLM * diff --git a/src/api/api_kws.cpp b/src/api/api_kws.cpp index 07f417e..a4c3103 100644 --- a/src/api/api_kws.cpp +++ b/src/api/api_kws.cpp @@ -48,3 +48,24 @@ String ApiKws::setup(ApiKwsSetupConfig_t config, String request_id, String langu 30000); return work_id; } + +String ApiKws::exit(String work_id, String request_id) +{ + String cmd; + { + JsonDocument doc; + doc["request_id"] = request_id; + doc["work_id"] = work_id; + doc["action"] = "exit"; + serializeJson(doc, cmd); + } + + _module_msg->sendCmdAndWaitToTakeMsg( + cmd.c_str(), request_id, + [&work_id](ResponseMsg_t& msg) { + // Copy work id + work_id = msg.work_id; + }, + 100); + return work_id; +} diff --git a/src/api/api_kws.h b/src/api/api_kws.h index 6400eec..71dc51c 100644 --- a/src/api/api_kws.h +++ b/src/api/api_kws.h @@ -31,6 +31,15 @@ public: String setup(ApiKwsSetupConfig_t config = ApiKwsSetupConfig_t(), String request_id = "kws_setup", String language = "en_US"); + /** + * @brief Exit module KWS, return KWS work_id + * + * @param work_id + * @param request_id + * @return String + */ + String exit(String work_id, String request_id = "kws_exit"); + private: ModuleMsg* _module_msg = nullptr; }; diff --git a/src/api/api_llm.cpp b/src/api/api_llm.cpp index 57b4ee9..93e4fa9 100644 --- a/src/api/api_llm.cpp +++ b/src/api/api_llm.cpp @@ -51,6 +51,27 @@ String ApiLlm::setup(ApiLlmSetupConfig_t config, String request_id) return llm_work_id; } +String ApiLlm::exit(String work_id, String request_id) +{ + String cmd; + { + JsonDocument doc; + doc["request_id"] = request_id; + doc["work_id"] = work_id; + doc["action"] = "exit"; + serializeJson(doc, cmd); + } + + _module_msg->sendCmdAndWaitToTakeMsg( + cmd.c_str(), request_id, + [&work_id](ResponseMsg_t& msg) { + // Copy work id + work_id = msg.work_id; + }, + 100); + return work_id; +} + int ApiLlm::inference(String work_id, String input, String request_id) { String cmd; diff --git a/src/api/api_llm.h b/src/api/api_llm.h index 27536e9..45a3455 100644 --- a/src/api/api_llm.h +++ b/src/api/api_llm.h @@ -33,6 +33,15 @@ public: */ String setup(ApiLlmSetupConfig_t config = ApiLlmSetupConfig_t(), String request_id = "llm_setup"); + /** + * @brief Exit module LLM, return LLM work_id + * + * @param work_id + * @param request_id + * @return String + */ + String exit(String work_id, String request_id = "llm_exit"); + /** * @brief Inference input data by module LLM * diff --git a/src/api/api_melotts.h b/src/api/api_melotts.h index 22ab4ea..2708c82 100644 --- a/src/api/api_melotts.h +++ b/src/api/api_melotts.h @@ -38,7 +38,7 @@ public: * @param request_id * @return String */ - String exit(String work_id, String request_id = "yolo_exit"); + String exit(String work_id, String request_id = "tts_exit"); /** * @brief Inference input data by TTS module diff --git a/src/api/api_tts.cpp b/src/api/api_tts.cpp index 6cfe661..7aee3c8 100644 --- a/src/api/api_tts.cpp +++ b/src/api/api_tts.cpp @@ -52,6 +52,27 @@ String ApiTts::setup(ApiTtsSetupConfig_t config, String request_id, String langu return work_id; } +String ApiTts::exit(String work_id, String request_id) +{ + String cmd; + { + JsonDocument doc; + doc["request_id"] = request_id; + doc["work_id"] = work_id; + doc["action"] = "exit"; + serializeJson(doc, cmd); + } + + _module_msg->sendCmdAndWaitToTakeMsg( + cmd.c_str(), request_id, + [&work_id](ResponseMsg_t& msg) { + // Copy work id + work_id = msg.work_id; + }, + 100); + return work_id; +} + int ApiTts::inference(String work_id, String input, uint32_t timeout, String request_id) { String cmd; diff --git a/src/api/api_tts.h b/src/api/api_tts.h index e5b06b9..80aea4e 100644 --- a/src/api/api_tts.h +++ b/src/api/api_tts.h @@ -32,6 +32,15 @@ public: String setup(ApiTtsSetupConfig_t config = ApiTtsSetupConfig_t(), String request_id = "tts_setup", String language = "en_US"); + /** + * @brief Exit module TTS, return TTS work_id + * + * @param work_id + * @param request_id + * @return String + */ + String exit(String work_id, String request_id = "tts_exit"); + /** * @brief Inference input data by TTS module * diff --git a/src/api/api_vlm.h b/src/api/api_vlm.h index 01878d7..cad9e30 100644 --- a/src/api/api_vlm.h +++ b/src/api/api_vlm.h @@ -25,7 +25,7 @@ public: void init(ModuleMsg* moduleMsg); /** - * @brief Setup module LLM, return LLM work_id + * @brief Setup module VLLM, return VLLM work_id * * @param config * @param request_id @@ -34,7 +34,7 @@ public: String setup(ApiVlmSetupConfig_t config = ApiVlmSetupConfig_t(), String request_id = "vlm_setup"); /** - * @brief Exit module YOLO, return YOLO work_id + * @brief Exit module VLLM, return VLLM work_id * * @param work_id * @param request_id @@ -43,7 +43,7 @@ public: String exit(String work_id, String request_id = "vlm_exit"); /** - * @brief Inference input data by module LLM + * @brief Inference input data by module VLLM * * @param work_id * @param input @@ -53,7 +53,7 @@ public: int inference(String work_id, String input, String request_id = "vlm_inference"); /** - * @brief Inference input data by module LLM, and wait inference result + * @brief Inference input data by module VLLM, and wait inference result * * @param work_id * @param input From dfd4c1e5d8c94e52c60c603b5e33566f14961c0a Mon Sep 17 00:00:00 2001 From: LittleMouse Date: Mon, 6 Jan 2025 17:33:41 +0800 Subject: [PATCH 4/4] Increase llm setup timeout. --- src/api/api_llm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/api_llm.cpp b/src/api/api_llm.cpp index 93e4fa9..617c8bd 100644 --- a/src/api/api_llm.cpp +++ b/src/api/api_llm.cpp @@ -47,7 +47,7 @@ String ApiLlm::setup(ApiLlmSetupConfig_t config, String request_id) // Copy work id llm_work_id = msg.work_id; }, - 10000); + 20000); return llm_work_id; }