Files
luban-lite/packages/artinchip/lvgl-ui/aic_demo/llm_demo/llm_demo.c
刘可亮 11c97ef399 v1.2.1
2025-07-22 11:15:46 +08:00

330 lines
8.7 KiB
C

/*
* Copyright (c) 2025, ArtInChip Technology Co., Ltd
*
* SPDX-License-Identifier: Apache-2.0
*
* Authors: Zequan Liang <zequan.liang@artinchip.com>
*/
#include "string.h"
#include "llm_demo.h"
#include "rtthread.h"
#include "view/view.h"
#include "./module/thread_pool.h"
#define THREADPOOL_MAX 3
#define WIFI_NAME "xxx"
#define WIFI_PASSWORD "xxx"
typedef struct _lv_llm_msg
{
int type;
bool has_text;
bool has_image;
bool has_audio;
lv_obj_t *obj;
char *message;
llm_config_t *llm_cfg;
} lv_llm_msg_t;
static lv_llm_config_t global_llm_config;
static lv_timer_t *ui_resp_timer;
static thread_pool_t *llm_thread_pool;
static rt_mq_t task_req_mq;
static rt_mq_t ui_resp_mq;
static void wifi_config_init(const char *wifi_name, const char *passward);
static void llm_msg_init(void);
static void ui_handle_response(lv_timer_t *t);
static void llm_send_ui_msg_cb(const char *message, void *usr_data);
static void llm_request_handle(void *ptr);
static void wifi_config_handle(void *ptr);
static void llm_screen_event_cb(lv_event_t *e);
extern char *llm_generate_response(llm_config_t *config);
void ui_init(void)
{
llm_config_load(LLM_DEEPSEEK_V3, NULL);
extern void llm_font_create(void);
llm_font_create();
lv_obj_t *llm_scr = lv_obj_create(NULL);
lv_obj_clear_flag(llm_scr, LV_OBJ_FLAG_SCROLLABLE);
chat_ui_create(llm_scr);
lv_obj_add_event_cb(llm_scr, llm_screen_event_cb, LV_EVENT_ALL, NULL);
lv_scr_load(llm_scr);
llm_msg_init();
llm_thread_pool = thread_pool_create(THREADPOOL_MAX);
if (!llm_thread_pool) {
LV_LOG_ERROR("Failed to create thread pool");
return;
}
wifi_config_init(WIFI_NAME, WIFI_PASSWORD);
ui_resp_timer = lv_timer_create(ui_handle_response, 50, NULL);
}
int llm_config_load(llm_type_t type, const char *config_file)
{
LV_UNUSED(config_file);
typedef struct {
lv_color_t color;
char *logo;
char *url;
char *api_key;
char *model;
} llm_config_entry;
static llm_config_entry config_table[] = {
[LLM_DEEPSEEK_DISTALL] = {
.color = {.blue = 0xfe, .green = 0x6b, .red = 0x4F},
.logo = LVGL_PATH(deepseek.png),
.url = "https://api.siliconflow.cn/v1/chat/completions",
.api_key = "sk-xheizvzayhbxgqvycgfhfteaqbzndofkvlggvmxflyhxeepl",
.model = "deepseek-ai/DeepSeek-R1-Distill-Qwen-7B"
},
[LLM_DEEPSEEK_V3] = {
.color = {.blue = 0xff, .green = 0xff, .red = 0xff},
.logo = LVGL_PATH(deepseek.png),
.url = "https://api.siliconflow.cn/v1/chat/completions",
.api_key = "sk-xheizvzayhbxgqvycgfhfteaqbzndofkvlggvmxflyhxeepl",
.model = "Pro/deepseek-ai/DeepSeek-V3"
},
[LLM_TONGYI_QWQ32B] = {
.color = {.blue = 0xed, .green = 0x5c, .red = 0x61},
.logo = LVGL_PATH(tongyi.png),
.url = "https://api.siliconflow.cn/v1/chat/completions",
.api_key = "sk-ttmshmgyiiyhbwoexmkzbtiobjulqpleyoghsjawhhhfhtgx",
.model = "Qwen/QwQ-32B"
}
};
if (type < 0 || type >= LLM_TYPE_MAX) {
LV_LOG_WARN("Unsupported LLM type: %d", type);
return -1;
}
const llm_config_entry *config_entry = &config_table[type];
global_llm_config.color = config_entry->color;
global_llm_config.logo = config_entry->logo;
global_llm_config.cfg.url = config_entry->url;
global_llm_config.cfg.api_key = config_entry->api_key;
global_llm_config.cfg.model = config_entry->model;
global_llm_config.cfg.config_file = LVGL_PATH_ORI(config/llm_config.json);
return 0;
}
int llm_request_submit(const char *user_input, lv_obj_t *obj)
{
if (!user_input || strlen(user_input) == 0)
return -1;
char *prompt = (char *)rt_strdup(user_input);
if (!prompt)
return -1;
llm_config_t *llm_cfg = (llm_config_t *)rt_malloc(sizeof(llm_config_t));
if (!llm_cfg) {
rt_free(prompt);
return -1;
}
memcpy(llm_cfg, &global_llm_config, sizeof(llm_config_t));
llm_cfg->prompt = prompt;
llm_cfg->user_data = obj;
llm_cfg->callback = llm_send_ui_msg_cb;
llm_cfg->callback_para = llm_cfg;
lv_llm_msg_t msg = {0};
msg.llm_cfg = llm_cfg;
msg.message = prompt;
if (rt_mq_send(task_req_mq, &msg, sizeof(lv_llm_msg_t)) != RT_EOK) {
rt_free(prompt);
rt_free(llm_cfg);
return -1;
}
if (thread_pool_add(llm_thread_pool, llm_request_handle, NULL) != 0) {
rt_free(prompt);
rt_free(llm_cfg);
return -1;
}
return 0;
}
lv_color_t llm_get_message_style_color(void)
{
return global_llm_config.color;
}
void * lv_llm_get_message_logo_src(void)
{
return global_llm_config.logo;
}
void * lv_llm_get_message_user_src(void)
{
return LVGL_PATH(user.png);
}
lv_color_t llm_get_message_user_color(void)
{
// return lv_color_hex(0x50cae9);
return lv_color_hex(0xffffff);
}
void wifi_config_init(const char *wifi_name, const char *password)
{
if (wifi_name == NULL || password == NULL)
return;
static char wifi_info[256] = {0};
snprintf(wifi_info, sizeof(wifi_info), "%s %s", wifi_name, password);
thread_pool_add(llm_thread_pool, wifi_config_handle, &wifi_info);
}
static void llm_msg_init(void)
{
task_req_mq = rt_mq_create("task_req_mq",
sizeof(lv_llm_msg_t),
128,
RT_IPC_FLAG_FIFO);
if (!task_req_mq) {
LV_LOG_ERROR("Failed to create request message queue");
return;
}
ui_resp_mq = rt_mq_create("ui_resp_mq",
sizeof(lv_llm_msg_t),
128,
RT_IPC_FLAG_FIFO);
if (!ui_resp_mq) {
LV_LOG_ERROR("Failed to create response message queue");
return;
}
}
static int cmd_exec_rtos(char *command)
{
extern int msh_exec(char *cmd, rt_size_t length);
static char cmd[256] = {0};
strncpy(cmd, command, sizeof(cmd) - 1);
cmd[sizeof(cmd) - 1] = '\0';
return msh_exec(cmd, strlen(cmd));
}
static void wifi_config_handle(void *ptr)
{
lv_llm_msg_t msg = {0};
msg.type = MSG_TYPE_WIFI;
msg.has_text = true;
msg.message = rt_strdup(WIFI_START_MSG);
if (rt_mq_send(ui_resp_mq, &msg, sizeof(lv_llm_msg_t)) != RT_EOK) {
return;
}
msg.message = rt_strdup(WIFI_CONNECTING_MSG);
if (rt_mq_send(ui_resp_mq, &msg, sizeof(lv_llm_msg_t)) != RT_EOK) {
return;
}
rt_thread_mdelay(1000);
char wifi_info[256] = {0};
snprintf(wifi_info, sizeof(wifi_info), "wlan wifi_connect %s", (char *)ptr);
cmd_exec_rtos("wlan wifi_on");
int status = cmd_exec_rtos(wifi_info);
if (status == 47)
msg.message = rt_strdup(WIFI_CONNECTED_MSG);
else
msg.message = rt_strdup(WIFI_ERROR_MSG);
if (rt_mq_send(ui_resp_mq, &msg, sizeof(lv_llm_msg_t)) != RT_EOK) {
return;
}
}
static void llm_screen_event_cb(lv_event_t *e)
{
lv_event_code_t code = (lv_event_code_t)lv_event_get_code(e);
if (code == LV_EVENT_SCREEN_UNLOAD_START) {
lv_timer_delete(ui_resp_timer);
thread_pool_destroy(llm_thread_pool);
rt_mq_delete(task_req_mq);
rt_mq_delete(ui_resp_mq);
}
if (code == LV_EVENT_DELETE) {;}
if (code == LV_EVENT_SCREEN_LOADED) {;}
}
static void ui_handle_response(lv_timer_t *t)
{
lv_llm_msg_t msg;
if(rt_mq_recv(ui_resp_mq, &msg, sizeof(lv_llm_msg_t), 0) == RT_EOK)
{
if (msg.has_text && msg.type == MSG_TYPE_LLM_TEXT) {
chat_ui_message_set_text(msg.obj, msg.message);
} else if (msg.has_text && msg.type == MSG_TYPE_WIFI) {
handle_ui_message(msg.message);
}
if (msg.message) {
rt_free(msg.message);
}
}
}
static void llm_send_ui_msg_cb(const char *message, void *callback_para)
{
lv_llm_msg_t msg;
llm_config_t *llm_cfg;
llm_cfg = (llm_config_t *)callback_para;
char *new_message = (char *)rt_strdup(message);
if (!new_message)
return;
msg.type = MSG_TYPE_LLM_TEXT;
msg.has_text = true;
msg.obj = llm_cfg->user_data;
msg.message = new_message;
rt_mq_send(ui_resp_mq, &msg, sizeof(lv_llm_msg_t));
}
static void llm_request_handle(void *ptr)
{
lv_llm_msg_t msg;
char *response;
/* create a thread to handle */
if (rt_mq_recv(task_req_mq, &msg, sizeof(msg), RT_WAITING_FOREVER) == RT_EOK) {
response = llm_generate_response(msg.llm_cfg);
if (response) {
rt_free(response);
}
if (msg.message) {
rt_free(msg.message);
}
if (msg.llm_cfg) {
rt_free(msg.llm_cfg);
}
}
}