feat: add Kimi Code and web APIs

This commit is contained in:
WHC
2026-10-19 10:41:00 +08:00
parent 60047ca5ce
commit 235652604d
21 changed files with 1189 additions and 49 deletions
+47 -3
View File
@@ -22,6 +22,25 @@
#define ERRBODY_MAX 4096
#define LLM_MAX_RETRIES 3
static int api_host_has(const char *base, const char *needle) {
return base && needle && strstr(base, needle) != NULL;
}
static int is_kimi_api(const char *base) {
return api_host_has(base, "api.moonshot.ai") ||
api_host_has(base, "api.moonshot.cn") ||
api_host_has(base, "api.kimi.ai") ||
api_host_has(base, "api.kimi.com");
}
static int is_kimi_k2(const char *model) {
return model && strncmp(model, "kimi-k2", 7) == 0;
}
static int is_kimi_k27(const char *model) {
return model && strncmp(model, "kimi-k2.7-code", 14) == 0;
}
static int ca_file_ok(const char *path) {
if (!path || !path[0])
return 0;
@@ -452,8 +471,31 @@ char *cst_llm_build_body(const cst_llm_request_t *req) {
return NULL;
cJSON *root = cJSON_CreateObject();
cJSON_AddStringToObject(root, "model", req->model);
if (req->reasoning_effort && req->reasoning_effort[0])
const int kimi = is_kimi_api(req->api_base);
const int kimi_k2 = kimi && is_kimi_k2(req->model);
if (req->reasoning_effort && req->reasoning_effort[0] && !kimi_k2)
cJSON_AddStringToObject(root, "reasoning_effort", req->reasoning_effort);
if (kimi_k2) {
/* K2.6/K2.7 do not understand K3's top-level reasoning_effort. They
* expose thinking as an object and require preserved reasoning to be
* sent back on the next turn. K2.7-code is always enabled/kept. */
cJSON *thinking = cJSON_AddObjectToObject(root, "thinking");
cJSON_AddStringToObject(thinking, "type", "enabled");
if (req->send_reasoning || is_kimi_k27(req->model))
cJSON_AddStringToObject(thinking, "keep", "all");
}
if (kimi && (!getenv("CST_NO_CACHE_HINTS") ||
!getenv("CST_NO_CACHE_HINTS")[0] ||
getenv("CST_NO_CACHE_HINTS")[0] == '0')) {
/* Kimi's implicit cache defaults to 5m. Agent sessions commonly pause
* longer than that, so opt into the 1h tier; users can select 5m/1h. */
const char *ttl = getenv("CST_KIMI_CACHE_TTL");
if (!ttl || (strcmp(ttl, "5m") != 0 && strcmp(ttl, "1h") != 0))
ttl = "1h";
cJSON *cache = cJSON_AddObjectToObject(root, "prompt_cache_options");
cJSON_AddStringToObject(cache, "mode", "implicit");
cJSON_AddStringToObject(cache, "ttl", ttl);
}
cJSON_AddTrueToObject(root, "stream");
const char *nousage = getenv("CST_NO_STREAM_USAGE");
if (!nousage || !nousage[0] || nousage[0] == '0') {
@@ -586,8 +628,10 @@ char *cst_llm_build_body(const cst_llm_request_t *req) {
if (req->send_reasoning && m->reasoning && m->reasoning[0])
cJSON_AddItemToObject(o, "reasoning_content",
clean_string(m->reasoning, 0));
if (m->prefix)
cJSON_AddTrueToObject(o, "prefix");
if (m->prefix) {
/* Kimi calls the OpenAI-style assistant prefill flag `partial`. */
cJSON_AddTrueToObject(o, kimi ? "partial" : "prefix");
}
}
cJSON_AddItemToArray(msgs, o);
if (role == CST_ROLE_TOOL && n_att)