Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ MimiClaw supports tool calling for both Anthropic and OpenAI — the LLM can cal
| `cron_add` | Schedule a recurring or one-shot task (the LLM creates cron jobs on its own) |
| `cron_list` | List all scheduled cron jobs |
| `cron_remove` | Remove a cron job by ID |
| `wifi_scan` | Scan for nearby WiFi networks and return details like SSID, signal strength, and security status |

Comment on lines +262 to 263
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Tools table is missing system_info, so docs are now incomplete.

system_info is registered and exposed (see main/tools/tool_registry.c, Lines 212-218), but it is not listed in this table. Please add it here (and mirror in localized READMEs) so users discover all available tools.

📝 Proposed doc patch
 | `cron_remove` | Remove a cron job by ID |
 | `wifi_scan` | Scan for nearby WiFi networks and return details like SSID, signal strength, and security status |
+| `system_info` | Get system diagnostics including chip details, memory usage, WiFi status, and uptime |
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
| `wifi_scan` | Scan for nearby WiFi networks and return details like SSID, signal strength, and security status |
| `cron_remove` | Remove a cron job by ID |
| `wifi_scan` | Scan for nearby WiFi networks and return details like SSID, signal strength, and security status |
| `system_info` | Get system diagnostics including chip details, memory usage, WiFi status, and uptime |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.md` around lines 262 - 263, The tools table in README.md is missing
the registered tool "system_info", so update the table row list (near the
`wifi_scan` entry) to add a new row describing `system_info` (e.g., "system_info
| Gather host system information such as OS, CPU, memory, and disk details");
also mirror the same addition in all localized README files so documentation
matches the registered tool set (the tool is exposed in tool_registry via the
system_info registration).

To enable web search, set a [Tavily API key](https://app.tavily.com/home) via `MIMI_SECRET_TAVILY_KEY` (preferred), or a [Brave Search API key](https://brave.com/search/api/) via `MIMI_SECRET_SEARCH_KEY` in `mimi_secrets.h`.

Expand Down
1 change: 1 addition & 0 deletions README_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ MimiClaw 同时支持 Anthropic 和 OpenAI 的工具调用 — LLM 在对话中
| `cron_add` | 创建定时或一次性任务(LLM 自主创建 cron 任务) |
| `cron_list` | 列出所有已调度的 cron 任务 |
| `cron_remove` | 按 ID 删除 cron 任务 |
| `wifi_scan` | 扫描附近的 WiFi 网络并返回 SSID、信号强度、信道和加密状态等详细信息 |

启用网页搜索可在 `mimi_secrets.h` 中设置 [Tavily API key](https://app.tavily.com/home)(优先,`MIMI_SECRET_TAVILY_KEY`),或 [Brave Search API key](https://brave.com/search/api/)(`MIMI_SECRET_SEARCH_KEY`)。

Expand Down
1 change: 1 addition & 0 deletions README_JA.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ MimiClawはAnthropicとOpenAI両方のツール呼び出しをサポート — L
| `cron_add` | 定期または単発タスクをスケジュール(LLMが自律的にcronジョブを作成) |
| `cron_list` | スケジュール済みのcronジョブを一覧表示 |
| `cron_remove` | IDでcronジョブを削除 |
| `wifi_scan` | 近くのWiFiネットワークをスキャンし、SSID、信号強度、チャネル、セキュリティ状態などの詳細を返す |

ウェブ検索を有効にするには、`mimi_secrets.h`で[Tavily APIキー](https://app.tavily.com/home)(優先、`MIMI_SECRET_TAVILY_KEY`)または[Brave Search APIキー](https://brave.com/search/api/)(`MIMI_SECRET_SEARCH_KEY`)を設定してください。

Expand Down
4 changes: 3 additions & 1 deletion main/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ idf_component_register(
"tools/tool_files.c"
"tools/tool_gpio.c"
"tools/gpio_policy.c"
"tools/tool_system_info.c"
"tools/tool_wifi_scan.c"
"skills/skill_loader.c"
"onboard/wifi_onboard.c"
INCLUDE_DIRS
"."
REQUIRES
nvs_flash esp_wifi esp_netif esp_http_client esp_http_server
esp_https_ota esp_event json spiffs console vfs app_update esp-tls
esp_timer esp_websocket_client esp_driver_gpio
esp_timer esp_websocket_client esp_driver_gpio spi_flash
)
43 changes: 36 additions & 7 deletions main/llm/llm_proxy.c
Original file line number Diff line number Diff line change
Expand Up @@ -187,19 +187,48 @@ static bool provider_is_openai(void)
return strcmp(s_provider, "openai") == 0;
}

static bool provider_is_moonshot(void)
{
return strcmp(s_provider, "moonshot") == 0;
}

static bool provider_is_hunyuan(void)
{
return strcmp(s_provider, "hunyuan") == 0;
}

static bool provider_is_openai_compatible(void)
{
return provider_is_openai() || provider_is_moonshot() || provider_is_hunyuan();
}

static const char *llm_api_url(void)
{
return provider_is_openai() ? MIMI_OPENAI_API_URL : MIMI_LLM_API_URL;
if (provider_is_openai()) {
return MIMI_OPENAI_API_URL;
} else if (provider_is_moonshot()) {
return MIMI_MOONSHOT_API_URL;
} else if (provider_is_hunyuan()) {
return MIMI_HUNYUAN_API_URL;
}
return MIMI_LLM_API_URL;
}

static const char *llm_api_host(void)
{
return provider_is_openai() ? "api.openai.com" : "api.anthropic.com";
if (provider_is_openai()) {
return "api.openai.com";
} else if (provider_is_moonshot()) {
return "api.moonshot.cn";
} else if (provider_is_hunyuan()) {
return "api.hunyuan.cloud.tencent.com";
}
return "api.anthropic.com";
}

static const char *llm_api_path(void)
{
return provider_is_openai() ? "/v1/chat/completions" : "/v1/messages";
return provider_is_openai_compatible() ? "/v1/chat/completions" : "/v1/messages";
}

/* ── Init ─────────────────────────────────────────────────────── */
Expand Down Expand Up @@ -265,7 +294,7 @@ static esp_err_t llm_http_direct(const char *post_data, resp_buf_t *rb, int *out

esp_http_client_set_method(client, HTTP_METHOD_POST);
esp_http_client_set_header(client, "Content-Type", "application/json");
if (provider_is_openai()) {
if (provider_is_openai_compatible()) {
if (s_api_key[0]) {
char auth[LLM_API_KEY_MAX_LEN + 16];
snprintf(auth, sizeof(auth), "Bearer %s", s_api_key);
Expand Down Expand Up @@ -293,7 +322,7 @@ static esp_err_t llm_http_via_proxy(const char *post_data, resp_buf_t *rb, int *
int body_len = strlen(post_data);
char header[1024];
int hlen = 0;
if (provider_is_openai()) {
if (provider_is_openai_compatible()) {
hlen = snprintf(header, sizeof(header),
"POST %s HTTP/1.1\r\n"
"Host: %s\r\n"
Expand Down Expand Up @@ -565,7 +594,7 @@ esp_err_t llm_chat_tools(const char *system_prompt,
cJSON_AddNumberToObject(body, "max_tokens", MIMI_LLM_MAX_TOKENS);
}

if (provider_is_openai()) {
if (provider_is_openai_compatible()) {
cJSON *openai_msgs = convert_messages_openai(system_prompt, messages);
cJSON_AddItemToObject(body, "messages", openai_msgs);

Expand Down Expand Up @@ -635,7 +664,7 @@ esp_err_t llm_chat_tools(const char *system_prompt,
return ESP_FAIL;
}

if (provider_is_openai()) {
if (provider_is_openai_compatible()) {
cJSON *choices = cJSON_GetObjectItem(root, "choices");
cJSON *choice0 = choices && cJSON_IsArray(choices) ? cJSON_GetArrayItem(choices, 0) : NULL;
if (choice0) {
Expand Down
4 changes: 3 additions & 1 deletion main/mimi_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,16 @@
#define MIMI_AGENT_SEND_WORKING_STATUS 1

/* Timezone (POSIX TZ format) */
#define MIMI_TIMEZONE "PST8PDT,M3.2.0,M11.1.0"
#define MIMI_TIMEZONE "CST-8"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don't change the global default timezone in this PR.

MIMI_TIMEZONE is used when main/tools/tool_get_time.c, Lines 43-49 restore local time after syncing. Moving the default to CST-8 changes every local timestamp the firmware emits and anything that depends on local time. Unless the device is now intentionally China-only, this needs to stay configurable or preserve the previous default.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@main/mimi_config.h` at line 83, Revert the hardcoded change to MIMI_TIMEZONE
(do not set it to "CST-8") and restore the previous default value or make it
configurable via build-time config so local timestamps remain unchanged; keep
MIMI_TIMEZONE as the existing default macro and, if you need region-specific
overrides, add a new configurable macro or build flag instead of changing
MIMI_TIMEZONE itself so the local time restore logic in the time-sync code (the
restore-local-time block in tool_get_time.c) continues to behave as before.


/* LLM */
#define MIMI_LLM_DEFAULT_MODEL "claude-opus-4-5"
#define MIMI_LLM_PROVIDER_DEFAULT "anthropic"
#define MIMI_LLM_MAX_TOKENS 4096
#define MIMI_LLM_API_URL "https://api.anthropic.com/v1/messages"
#define MIMI_OPENAI_API_URL "https://api.openai.com/v1/chat/completions"
#define MIMI_MOONSHOT_API_URL "https://api.moonshot.cn/v1/chat/completions"
#define MIMI_HUNYUAN_API_URL "https://api.hunyuan.cloud.tencent.com/v1/chat/completions"
#define MIMI_LLM_API_VERSION "2023-06-01"
#define MIMI_LLM_STREAM_BUF_SIZE (32 * 1024)
#define MIMI_LLM_LOG_VERBOSE_PAYLOAD 0
Expand Down
2 changes: 2 additions & 0 deletions main/onboard/onboard_html.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ static const char ONBOARD_HTML[] =
"<select id='provider'>"
"<option value='anthropic'>Anthropic</option>"
"<option value='openai'>OpenAI</option>"
"<option value='moonshot'>Moonshot (Kimi)</option>"
"<option value='hunyuan'>Hunyuan</option>"
"</select>"
"</div></div>"

Expand Down
8 changes: 4 additions & 4 deletions main/tools/tool_get_time.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,15 @@ static bool parse_and_set_time(const char *date_str, char *out, size_t out_size)
return true;
}

/* Fetch time via proxy: HEAD request to api.telegram.org, parse Date header */
/* Fetch time via proxy: HEAD request to t.cn, parse Date header */
static esp_err_t fetch_time_via_proxy(char *out, size_t out_size)
{
proxy_conn_t *conn = proxy_conn_open("api.telegram.org", 443, 10000);
proxy_conn_t *conn = proxy_conn_open("t.cn", 443, 10000);
if (!conn) return ESP_ERR_HTTP_CONNECT;

const char *req =
"HEAD / HTTP/1.1\r\n"
"Host: api.telegram.org\r\n"
"Host: t.cn\r\n"
"Connection: close\r\n\r\n";

if (proxy_conn_write(conn, req, strlen(req)) < 0) {
Expand Down Expand Up @@ -136,7 +136,7 @@ static esp_err_t fetch_time_direct(char *out, size_t out_size)
time_header_ctx_t ctx = {0};

esp_http_client_config_t config = {
.url = "https://api.telegram.org/",
.url = "https://t.cn/",
.method = HTTP_METHOD_HEAD,
.timeout_ms = 10000,
.crt_bundle_attach = esp_crt_bundle_attach,
Expand Down
40 changes: 27 additions & 13 deletions main/tools/tool_registry.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include "tools/tool_files.h"
#include "tools/tool_cron.h"
#include "tools/tool_gpio.h"
#include "tools/tool_system_info.h"
#include "tools/tool_wifi_scan.h"

#include <string.h>
#include "esp_log.h"
Expand Down Expand Up @@ -183,37 +185,49 @@ esp_err_t tool_registry_init(void)
mimi_tool_t gw = {
.name = "gpio_write",
.description = "Set a GPIO pin HIGH or LOW. Controls LEDs, relays, and other digital outputs.",
.input_schema_json =
"{\"type\":\"object\","
"\"properties\":{\"pin\":{\"type\":\"integer\",\"description\":\"GPIO pin number\"},"
"\"state\":{\"type\":\"integer\",\"description\":\"1 for HIGH, 0 for LOW\"}},"
"\"required\":[\"pin\",\"state\"]}",
.input_schema_json = "{\"type\":\"object\", \"properties\":{\"pin\":{\"type\":\"integer\",\"description\":\"GPIO pin number\"}, \"state\":{\"type\":\"integer\",\"description\":\"1 for HIGH, 0 for LOW\"}}, \"required\":[\"pin\",\"state\"]}",
.execute = tool_gpio_write_execute,
};
register_tool(&gw);

mimi_tool_t gr = {
.name = "gpio_read",
.description = "Read a GPIO pin state. Returns HIGH or LOW. Use for checking switches, sensors, and digital inputs.",
.input_schema_json =
"{\"type\":\"object\","
"\"properties\":{\"pin\":{\"type\":\"integer\",\"description\":\"GPIO pin number\"}},"
"\"required\":[\"pin\"]}",
.input_schema_json = "{\"type\":\"object\", \"properties\":{\"pin\":{\"type\":\"integer\",\"description\":\"GPIO pin number\"}}, \"required\":[\"pin\"]}",
.execute = tool_gpio_read_execute,
};
register_tool(&gr);

mimi_tool_t ga = {
.name = "gpio_read_all",
.description = "Read all allowed GPIO pin states in a single call. Returns each pin's HIGH/LOW state.",
.input_schema_json =
"{\"type\":\"object\","
"\"properties\":{},"
"\"required\":[]}",
.input_schema_json = "{\"type\":\"object\", \"properties\":{}, \"required\":[]}",
.execute = tool_gpio_read_all_execute,
};
register_tool(&ga);

/* Register system_info */
tool_system_info_init();

mimi_tool_t si = {
.name = "system_info",
.description = "Get system and hardware information including chip details, memory usage, WiFi status, and uptime.",
.input_schema_json = "{\"type\":\"object\", \"properties\":{}, \"required\":[]}",
.execute = tool_system_info_execute,
};
register_tool(&si);

/* Register wifi_scan */
tool_wifi_scan_init();

mimi_tool_t wifi_scan_tool = {
.name = "wifi_scan",
.description = "Scan for nearby WiFi networks and return the results with SSID, signal strength, channel, and security status.",
.input_schema_json = "{\"type\":\"object\", \"properties\":{}, \"required\":[]}",
.execute = tool_wifi_scan_execute,
};
register_tool(&wifi_scan_tool);

build_tools_json();

ESP_LOGI(TAG, "Tool registry initialized");
Expand Down
96 changes: 96 additions & 0 deletions main/tools/tool_system_info.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#include "tool_system_info.h"
#include "mimi_config.h"

#include <string.h>
#include <stdlib.h>
#include "esp_log.h"
#include "esp_system.h"
#include "esp_heap_caps.h"
#include "esp_chip_info.h"
#include "esp_timer.h"
#include "esp_wifi.h"
#include "esp_flash.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

static const char *TAG = "system_info";

esp_err_t tool_system_info_init(void)
{
ESP_LOGI(TAG, "System info tool initialized");
return ESP_OK;
}

esp_err_t tool_system_info_execute(const char *input_json, char *output, size_t output_size)
{
ESP_LOGI(TAG, "Getting system info...");

/* Get chip info */
esp_chip_info_t chip_info;
esp_chip_info(&chip_info);

/* Get heap info */
size_t free_heap = heap_caps_get_free_size(MALLOC_CAP_DEFAULT);
size_t free_psram = heap_caps_get_free_size(MALLOC_CAP_SPIRAM);
size_t total_heap = heap_caps_get_total_size(MALLOC_CAP_DEFAULT);
size_t total_psram = heap_caps_get_total_size(MALLOC_CAP_SPIRAM);

/* Get system uptime */
int64_t uptime_ms = esp_timer_get_time() / 1000;
int uptime_hours = uptime_ms / (1000 * 60 * 60);
int uptime_minutes = (uptime_ms / (1000 * 60)) % 60;
int uptime_seconds = (uptime_ms / 1000) % 60;

/* Get WiFi status */
wifi_ap_record_t ap_info;
wifi_sta_list_t sta_list;
char wifi_status[256] = "Disconnected";

esp_wifi_ap_get_sta_list(&sta_list);
if (esp_wifi_sta_get_ap_info(&ap_info) == ESP_OK) {
snprintf(wifi_status, sizeof(wifi_status), "Connected to %s (RSSI: %d dBm, Channel: %d)",
(char*)ap_info.ssid, ap_info.rssi, ap_info.primary);
}

/* Get task info */
UBaseType_t uxHighWaterMark = uxTaskGetStackHighWaterMark(NULL);

/* Get flash size */
uint32_t flash_size;
esp_flash_get_size(NULL, &flash_size);

/* Format the result */
int written = snprintf(output, output_size,
"=== System Information ===\n"
"Chip: ESP32-S3\n"
"Features: %s%s%s%s\n"
"Flash Size: %u MB\n"
"RAM: %u KB free / %u KB total\n"
"PSRAM: %u KB free / %u KB total\n"
"Uptime: %d:%02d:%02d\n"
"WiFi: %s\n"
"Stack High Water Mark: %u bytes\n"
"FreeRTOS Version: %s\n"
"ESP-IDF Version: %s\n"
"MimiClaw Version: v0.1.0",
(chip_info.features & CHIP_FEATURE_WIFI_BGN) ? "WiFi " : "",
(chip_info.features & CHIP_FEATURE_BLE) ? "BLE " : "",
(chip_info.features & CHIP_FEATURE_BT) ? "BT " : "",
(chip_info.features & CHIP_FEATURE_EMB_PSRAM) ? "PSRAM" : "",
(unsigned int)(flash_size / (1024 * 1024)),
(unsigned int)(free_heap / 1024), (unsigned int)(total_heap / 1024),
(unsigned int)(free_psram / 1024), (unsigned int)(total_psram / 1024),
uptime_hours, uptime_minutes, uptime_seconds,
wifi_status,
(unsigned int)uxHighWaterMark,
tskKERNEL_VERSION_NUMBER,
IDF_VER);

if (written < 0 || (size_t)written >= output_size) {
ESP_LOGE(TAG, "Output buffer too small");
return ESP_ERR_NO_MEM;
}

ESP_LOGI(TAG, "System info collected");
return ESP_OK;
}
7 changes: 7 additions & 0 deletions main/tools/tool_system_info.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#pragma once

#include <stddef.h>
#include "esp_err.h"

esp_err_t tool_system_info_execute(const char *input_json, char *output, size_t output_size);
esp_err_t tool_system_info_init(void);
Loading