feat(search): add CLI web_search command and Tavily bearer auth

Signed-off-by: Asklv <boironic@gmail.com>
This commit is contained in:
Asklv
2026-03-03 01:20:01 +08:00
parent b75370a290
commit 443cb97f59
2 changed files with 88 additions and 2 deletions

View File

@@ -608,6 +608,78 @@ static int cmd_tool_exec(int argc, char **argv)
return (err == ESP_OK) ? 0 : 1;
}
/* --- web_search command --- */
static struct {
struct arg_str *query;
struct arg_end *end;
} web_search_args;
static bool json_escape_string(const char *in, char *out, size_t out_size)
{
if (!in || !out || out_size == 0) return false;
size_t o = 0;
for (size_t i = 0; in[i] != '\0'; ++i) {
const char c = in[i];
const char *esc = NULL;
switch (c) {
case '\\': esc = "\\\\"; break;
case '\"': esc = "\\\""; break;
case '\n': esc = "\\n"; break;
case '\r': esc = "\\r"; break;
case '\t': esc = "\\t"; break;
default: break;
}
if (esc) {
size_t n = strlen(esc);
if (o + n >= out_size) return false;
memcpy(&out[o], esc, n);
o += n;
continue;
}
if ((unsigned char)c < 0x20) {
continue;
}
if (o + 1 >= out_size) return false;
out[o++] = c;
}
out[o] = '\0';
return true;
}
static int cmd_web_search(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **)&web_search_args);
if (nerrors != 0) {
arg_print_errors(stderr, web_search_args.end, argv[0]);
return 1;
}
char escaped_query[512];
if (!json_escape_string(web_search_args.query->sval[0], escaped_query, sizeof(escaped_query))) {
printf("Query too long.\n");
return 1;
}
char input_json[640];
int n = snprintf(input_json, sizeof(input_json), "{\"query\":\"%s\"}", escaped_query);
if (n <= 0 || n >= (int)sizeof(input_json)) {
printf("Query too long.\n");
return 1;
}
char *output = calloc(1, 4096);
if (!output) {
printf("Out of memory.\n");
return 1;
}
esp_err_t err = tool_web_search_execute(input_json, output, 4096);
printf("web_search status: %s\n", esp_err_to_name(err));
printf("%s\n", output[0] ? output : "(empty)");
free(output);
return (err == ESP_OK) ? 0 : 1;
}
/* --- restart command --- */
static int cmd_restart(int argc, char **argv)
{
@@ -896,6 +968,17 @@ esp_err_t serial_cli_init(void)
};
esp_console_cmd_register(&tool_exec_cmd);
/* web_search */
web_search_args.query = arg_str1(NULL, NULL, "<query>", "Search query");
web_search_args.end = arg_end(1);
esp_console_cmd_t web_search_cmd = {
.command = "web_search",
.help = "Run web search tool directly (e.g. web_search \"latest esp-idf\")",
.func = &cmd_web_search,
.argtable = &web_search_args,
};
esp_console_cmd_register(&web_search_cmd);
/* restart */
esp_console_cmd_t restart_cmd = {
.command = "restart",

View File

@@ -190,7 +190,6 @@ static char *build_tavily_payload(const char *query)
{
cJSON *root = cJSON_CreateObject();
if (!root) return NULL;
cJSON_AddStringToObject(root, "api_key", s_tavily_key);
cJSON_AddStringToObject(root, "query", query);
cJSON_AddNumberToObject(root, "max_results", SEARCH_RESULT_COUNT);
cJSON_AddBoolToObject(root, "include_answer", false);
@@ -315,6 +314,9 @@ static esp_err_t tavily_search_direct(const char *query, search_buf_t *sb)
esp_http_client_set_method(client, HTTP_METHOD_POST);
esp_http_client_set_header(client, "Accept", "application/json");
esp_http_client_set_header(client, "Content-Type", "application/json");
char auth[192];
snprintf(auth, sizeof(auth), "Bearer %s", s_tavily_key);
esp_http_client_set_header(client, "Authorization", auth);
esp_http_client_set_post_field(client, payload, strlen(payload));
esp_err_t err = esp_http_client_perform(client);
@@ -347,9 +349,10 @@ static esp_err_t tavily_search_via_proxy(const char *query, search_buf_t *sb)
"Host: api.tavily.com\r\n"
"Accept: application/json\r\n"
"Content-Type: application/json\r\n"
"Authorization: Bearer %s\r\n"
"Content-Length: %d\r\n"
"Connection: close\r\n\r\n",
(int)strlen(payload));
s_tavily_key, (int)strlen(payload));
if (proxy_conn_write(conn, header, hlen) < 0 ||
proxy_conn_write(conn, payload, strlen(payload)) < 0) {