mirror of
https://github.com/m5stack/ESP-Claw.git
synced 2026-05-20 11:51:49 -07:00
refactor: simplify skill catalog metadata
This commit is contained in:
@@ -27,28 +27,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "im_hello_esp_claw",
|
||||
"description": "Intercept IM hello! ESP-Claw and reply directly.Just for demonstration",
|
||||
"enabled": true,
|
||||
"ack": "{{event.source_channel}} hello intercepted",
|
||||
"match": {
|
||||
"event_type": "message",
|
||||
"event_key": "text",
|
||||
"content_type": "text",
|
||||
"text": "hello! ESP-Claw"
|
||||
},
|
||||
"actions": [
|
||||
{
|
||||
"type": "send_message",
|
||||
"input": {
|
||||
"channel": "{{event.source_channel}}",
|
||||
"chat_id": "{{event.chat_id}}",
|
||||
"message": "Nice to meet you!"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "im_any_message_working_reply",
|
||||
"description": "Reply to any IM text message before other automation handling.",
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
{
|
||||
"id": "weather_search",
|
||||
"file": "weather.md",
|
||||
"title": "weather",
|
||||
"summary": "How to answer current weather, temperature, and forecast queries through direct web search capabilities.",
|
||||
"cap_groups": [
|
||||
"cap_time",
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
{
|
||||
"id": "cap_im_feishu",
|
||||
"file": "cap_im_feishu.md",
|
||||
"title": "cap_im_feishu",
|
||||
"summary": "How to send additional text, images, and files to Feishu.",
|
||||
"cap_groups": [
|
||||
"cap_im_feishu"
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
{
|
||||
"id": "cap_im_qq",
|
||||
"file": "cap_im_qq.md",
|
||||
"title": "cap_im_qq",
|
||||
"summary": "How to send additional text or send local files back to QQ.",
|
||||
"cap_groups": [
|
||||
"cap_im_qq"
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
{
|
||||
"id": "cap_im_tg",
|
||||
"file": "cap_im_tg.md",
|
||||
"title": "cap_im_tg",
|
||||
"summary": "How to send additional text, images, and files to Telegram.",
|
||||
"cap_groups": [
|
||||
"cap_im_tg"
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
{
|
||||
"id": "cap_im_wechat",
|
||||
"file": "cap_im_wechat.md",
|
||||
"title": "cap_im_wechat",
|
||||
"summary": "How to send additional text and images to WeChat.",
|
||||
"cap_groups": [
|
||||
"cap_im_wechat"
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
{
|
||||
"id": "cap_llm_inspect_image",
|
||||
"file": "cap_llm_inspect.md",
|
||||
"title": "cap_llm_inspect_image",
|
||||
"summary": "How to inspect a local image with inspect_image.",
|
||||
"cap_groups": [
|
||||
"cap_llm_inspect"
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
{
|
||||
"id": "cap_lua_run",
|
||||
"file": "cap_lua_run.md",
|
||||
"title": "cap_lua_run",
|
||||
"summary": "This Files describes how to correctly use Lua scripts.",
|
||||
"cap_groups": [
|
||||
"cap_lua"
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
{
|
||||
"id": "cap_router_mgr",
|
||||
"file": "cap_router_mgr.md",
|
||||
"title": "cap_router_mgr",
|
||||
"summary": "Manage router automation rules: list/get/add/update/delete/reload with strict rule_json format.",
|
||||
"cap_groups": [
|
||||
"cap_router_mgr"
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
{
|
||||
"id": "cap_scheduler",
|
||||
"file": "cap_scheduler.md",
|
||||
"title": "cap_scheduler",
|
||||
"summary": "Manage scheduler rules: list/get/add/enable/disable/pause/resume/trigger/reload.",
|
||||
"cap_groups": [
|
||||
"cap_scheduler"
|
||||
|
||||
@@ -558,7 +558,6 @@ static esp_err_t cap_skill_register_execute(const char *input_json,
|
||||
cJSON *skills = NULL;
|
||||
cJSON *skill_id_item = NULL;
|
||||
cJSON *file_item = NULL;
|
||||
cJSON *title_item = NULL;
|
||||
cJSON *summary_item = NULL;
|
||||
cJSON *skill = NULL;
|
||||
esp_err_t err;
|
||||
@@ -573,14 +572,12 @@ static esp_err_t cap_skill_register_execute(const char *input_json,
|
||||
|
||||
skill_id_item = cJSON_GetObjectItemCaseSensitive(root, "skill_id");
|
||||
file_item = cJSON_GetObjectItemCaseSensitive(root, "file");
|
||||
title_item = cJSON_GetObjectItemCaseSensitive(root, "title");
|
||||
summary_item = cJSON_GetObjectItemCaseSensitive(root, "summary");
|
||||
if (!cJSON_IsString(skill_id_item) || !skill_id_item->valuestring || !skill_id_item->valuestring[0] ||
|
||||
!cJSON_IsString(file_item) || !file_item->valuestring || !file_item->valuestring[0] ||
|
||||
!cJSON_IsString(title_item) || !title_item->valuestring || !title_item->valuestring[0] ||
|
||||
!cJSON_IsString(summary_item) || !summary_item->valuestring || !summary_item->valuestring[0]) {
|
||||
cJSON_Delete(root);
|
||||
cap_skill_write_error(output, output_size, "skill_id, file, title and summary are required", NULL);
|
||||
cap_skill_write_error(output, output_size, "skill_id, file and summary are required", NULL);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
@@ -642,7 +639,6 @@ static esp_err_t cap_skill_register_execute(const char *input_json,
|
||||
}
|
||||
cJSON_AddStringToObject(skill, "id", skill_id_item->valuestring);
|
||||
cJSON_AddStringToObject(skill, "file", file_item->valuestring);
|
||||
cJSON_AddStringToObject(skill, "title", title_item->valuestring);
|
||||
cJSON_AddStringToObject(skill, "summary", summary_item->valuestring);
|
||||
cJSON_AddItemToArray(skills, skill);
|
||||
|
||||
@@ -677,7 +673,6 @@ static esp_err_t cap_skill_register_execute(const char *input_json,
|
||||
}
|
||||
cJSON_AddStringToObject(skill, "id", skill_id_item->valuestring);
|
||||
cJSON_AddStringToObject(skill, "file", file_item->valuestring);
|
||||
cJSON_AddStringToObject(skill, "title", title_item->valuestring);
|
||||
cJSON_AddStringToObject(skill, "summary", summary_item->valuestring);
|
||||
|
||||
cJSON_Delete(root);
|
||||
@@ -778,7 +773,7 @@ static const claw_cap_descriptor_t s_skill_descriptors[] = {
|
||||
.kind = CLAW_CAP_KIND_CALLABLE,
|
||||
.cap_flags = CLAW_CAP_FLAG_CALLABLE_BY_LLM,
|
||||
.input_schema_json =
|
||||
"{\"type\":\"object\",\"properties\":{\"skill_id\":{\"type\":\"string\"},\"file\":{\"type\":\"string\"},\"title\":{\"type\":\"string\"},\"summary\":{\"type\":\"string\"}},\"required\":[\"skill_id\",\"file\",\"title\",\"summary\"]}",
|
||||
"{\"type\":\"object\",\"properties\":{\"skill_id\":{\"type\":\"string\"},\"file\":{\"type\":\"string\"},\"summary\":{\"type\":\"string\"}},\"required\":[\"skill_id\",\"file\",\"summary\"]}",
|
||||
.execute = cap_skill_register_execute,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -22,7 +22,6 @@ static struct {
|
||||
struct arg_str *register_skill;
|
||||
struct arg_str *unregister_skill;
|
||||
struct arg_str *file;
|
||||
struct arg_str *title;
|
||||
struct arg_str *summary;
|
||||
struct arg_str *activate;
|
||||
struct arg_str *deactivate;
|
||||
@@ -66,7 +65,6 @@ static int call_skill_cap(const char *cap_name, const char *input_json, const ch
|
||||
|
||||
static char *build_register_skill_json(const char *skill_id,
|
||||
const char *file,
|
||||
const char *title,
|
||||
const char *summary)
|
||||
{
|
||||
cJSON *root = NULL;
|
||||
@@ -79,7 +77,6 @@ static char *build_register_skill_json(const char *skill_id,
|
||||
|
||||
cJSON_AddStringToObject(root, "skill_id", skill_id);
|
||||
cJSON_AddStringToObject(root, "file", file);
|
||||
cJSON_AddStringToObject(root, "title", title);
|
||||
cJSON_AddStringToObject(root, "summary", summary);
|
||||
rendered = cJSON_PrintUnformatted(root);
|
||||
cJSON_Delete(root);
|
||||
@@ -134,14 +131,13 @@ static int skill_func(int argc, char **argv)
|
||||
}
|
||||
|
||||
if (skill_args.register_skill->count) {
|
||||
if (!skill_args.file->count || !skill_args.title->count || !skill_args.summary->count) {
|
||||
printf("--register requires --file, --title and --summary\n");
|
||||
if (!skill_args.file->count || !skill_args.summary->count) {
|
||||
printf("--register requires --file and --summary\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
input_json = build_register_skill_json(skill_args.register_skill->sval[0],
|
||||
skill_args.file->sval[0],
|
||||
skill_args.title->sval[0],
|
||||
skill_args.summary->sval[0]);
|
||||
if (!input_json) {
|
||||
printf("Out of memory\n");
|
||||
@@ -211,7 +207,6 @@ void register_cap_skill(void)
|
||||
skill_args.register_skill = arg_str0("r", "register", "<skill_id>", "Register one skill in the catalog");
|
||||
skill_args.unregister_skill = arg_str0("u", "unregister", "<skill_id>", "Remove one skill from the catalog");
|
||||
skill_args.file = arg_str0("f", "file", "<file.md>", "Skill markdown path relative to <storage_root>/skills");
|
||||
skill_args.title = arg_str0("t", "title", "<title>", "Skill title for catalog registration");
|
||||
skill_args.summary = arg_str0(NULL, "summary", "<summary>", "Skill summary for catalog registration");
|
||||
skill_args.activate = arg_str0("a", "activate", "<skill_id>", "Activate one skill");
|
||||
skill_args.deactivate = arg_str0("d", "deactivate", "<skill_id>", "Deactivate one skill");
|
||||
@@ -224,7 +219,7 @@ void register_cap_skill(void)
|
||||
.help = "Skill operations.\n"
|
||||
"Examples:\n"
|
||||
" skill --catalog\n"
|
||||
" skill --register weather_v2 --file weather_v2.md --title \"Weather V2\" --summary \"Get forecast and alerts\"\n"
|
||||
" skill --register weather_v2 --file weather_v2.md --summary \"Get forecast and alerts\"\n"
|
||||
" skill --unregister weather_v2\n"
|
||||
" skill --list --session default\n"
|
||||
" skill --activate weather --session default\n"
|
||||
|
||||
@@ -352,6 +352,11 @@ static esp_err_t build_response_payload_json(const claw_core_request_t *request,
|
||||
cJSON_Delete(root);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
if (response->error_message && response->error_message[0] &&
|
||||
!cJSON_AddStringToObject(root, "error_message", response->error_message)) {
|
||||
cJSON_Delete(root);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
payload_json = cJSON_PrintUnformatted(root);
|
||||
cJSON_Delete(root);
|
||||
@@ -370,18 +375,23 @@ static esp_err_t build_agent_response_event(const claw_core_request_t *request,
|
||||
{
|
||||
const char *channel = NULL;
|
||||
const char *chat_id = NULL;
|
||||
const char *text = NULL;
|
||||
int64_t now_ms;
|
||||
esp_err_t err;
|
||||
|
||||
if (!request || !response || !out_event || !out_payload_json ||
|
||||
response->status != CLAW_CORE_RESPONSE_STATUS_OK ||
|
||||
!response->text || !response->text[0]) {
|
||||
if (!request || !response || !out_event || !out_payload_json) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
memset(out_event, 0, sizeof(*out_event));
|
||||
*out_payload_json = NULL;
|
||||
|
||||
text = (response->status == CLAW_CORE_RESPONSE_STATUS_OK) ?
|
||||
response->text : response->error_message;
|
||||
if (!text || !text[0]) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
err = build_response_payload_json(request, response, out_payload_json);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
@@ -416,7 +426,7 @@ static esp_err_t build_agent_response_event(const claw_core_request_t *request,
|
||||
}
|
||||
out_event->timestamp_ms = now_ms;
|
||||
out_event->session_policy = CLAW_EVENT_SESSION_POLICY_CHAT;
|
||||
out_event->text = response->text;
|
||||
out_event->text = (char *)text;
|
||||
out_event->payload_json = *out_payload_json;
|
||||
|
||||
return ESP_OK;
|
||||
@@ -433,10 +443,6 @@ static void publish_response_event_if_requested(const claw_core_request_item_t *
|
||||
!(request->view.flags & CLAW_CORE_REQUEST_FLAG_PUBLISH_RESPONSE_EVENT)) {
|
||||
return;
|
||||
}
|
||||
if (response->view.status != CLAW_CORE_RESPONSE_STATUS_OK ||
|
||||
!response->view.text || !response->view.text[0]) {
|
||||
return;
|
||||
}
|
||||
|
||||
err = build_agent_response_event(&request->view,
|
||||
&response->view,
|
||||
@@ -946,7 +952,7 @@ static esp_err_t build_iteration_context(const claw_core_request_item_t *request
|
||||
goto cleanup;
|
||||
}
|
||||
context_len = strlen(context.content);
|
||||
ESP_LOGD(TAG,
|
||||
ESP_LOGI(TAG,
|
||||
"context_loaded request=%" PRIu32 " provider=%s context_kind=%s context_len=%u",
|
||||
request->view.request_id,
|
||||
provider->name,
|
||||
|
||||
@@ -26,7 +26,6 @@ static const char *SKILLS_LIST_FILE = "skills_list.json";
|
||||
typedef struct {
|
||||
char *id;
|
||||
char *file;
|
||||
char *title;
|
||||
char *summary;
|
||||
char **cap_groups;
|
||||
size_t cap_group_count;
|
||||
@@ -113,7 +112,6 @@ static void free_registry_entry(claw_skill_registry_entry_t *entry)
|
||||
|
||||
free(entry->id);
|
||||
free(entry->file);
|
||||
free(entry->title);
|
||||
free(entry->summary);
|
||||
free_string_array(entry->cap_groups, entry->cap_group_count);
|
||||
memset(entry, 0, sizeof(*entry));
|
||||
@@ -456,7 +454,7 @@ static esp_err_t validate_registry_entry(claw_skill_registry_entry_t *entry)
|
||||
FILE *file = NULL;
|
||||
size_t i;
|
||||
|
||||
if (!entry || !entry->id || !entry->file || !entry->title || !entry->summary) {
|
||||
if (!entry || !entry->id || !entry->file || !entry->summary) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (!skill_path_is_valid(entry->file) || !is_markdown_skill_file(entry->file)) {
|
||||
@@ -543,9 +541,6 @@ static esp_err_t load_registry_from_json(void)
|
||||
if (err == ESP_OK) {
|
||||
err = json_dup_required_string(skill, "file", &entry->file);
|
||||
}
|
||||
if (err == ESP_OK) {
|
||||
err = json_dup_required_string(skill, "title", &entry->title);
|
||||
}
|
||||
if (err == ESP_OK) {
|
||||
err = json_dup_required_string(skill, "summary", &entry->summary);
|
||||
}
|
||||
@@ -746,8 +741,7 @@ static esp_err_t claw_skill_render_skills_list(char *buf, size_t size)
|
||||
|
||||
off += snprintf(buf + off,
|
||||
size - off,
|
||||
"- %s (%s): %s\n",
|
||||
entry->title,
|
||||
"- %s: %s\n",
|
||||
entry->id,
|
||||
entry->summary);
|
||||
}
|
||||
@@ -792,7 +786,7 @@ static esp_err_t claw_skill_build_prompt_block(const char *const *skill_ids,
|
||||
size - off,
|
||||
"%s### %s\n%s\n",
|
||||
i == 0 ? "" : "\n",
|
||||
entry->title,
|
||||
entry->id,
|
||||
content);
|
||||
free(content);
|
||||
}
|
||||
@@ -1024,7 +1018,6 @@ static esp_err_t claw_skill_skills_list_collect(const claw_core_request_t *reque
|
||||
|
||||
content_size = 64;
|
||||
for (size_t i = 0; i < s_skill.entry_count; i++) {
|
||||
content_size += strlen(s_skill.entries[i].title ? s_skill.entries[i].title : "");
|
||||
content_size += strlen(s_skill.entries[i].id ? s_skill.entries[i].id : "");
|
||||
content_size += strlen(s_skill.entries[i].summary ? s_skill.entries[i].summary : "");
|
||||
content_size += 16;
|
||||
|
||||
Reference in New Issue
Block a user