feat: add persistent multi-model workers and orchestration budgets

This commit is contained in:
WHC
2026-10-19 11:29:26 +08:00
parent 235652604d
commit 143dba203b
14 changed files with 1043 additions and 32 deletions
+65
View File
@@ -2248,10 +2248,15 @@ static int h_extensions(cst_http_conn_t *c, void *ud) {
cJSON_AddStringToObject(e, "source", ro[i].source);
cJSON_AddStringToObject(e, "desc", ro[i].desc);
cJSON_AddStringToObject(e, "model", ro[i].model);
cJSON_AddStringToObject(e, "profile", ro[i].profile);
cJSON_AddBoolToObject(e, "read_only", ro[i].read_only);
cJSON_AddItemToArray(a, e);
}
cst_roles_free(ro, n);
char *budget_json = cst_workers_budget_json(cfg);
cJSON *budget = budget_json ? cJSON_Parse(budget_json) : NULL;
free(budget_json);
if (budget) cJSON_AddItemToObject(o, "worker_budget", budget);
a = cJSON_AddArrayToObject(o, "runs");
n = cst_subagent_list(ws, sid, ru, WUI_RUNS_MAX);
for (int i = 0; a && i < n; i++) {
@@ -2262,6 +2267,23 @@ static int h_extensions(cst_http_conn_t *c, void *ud) {
cJSON_AddStringToObject(e, "role", ru[i].role);
cJSON_AddStringToObject(e, "desc", ru[i].desc);
cJSON_AddStringToObject(e, "status", ru[i].status);
cJSON_AddStringToObject(e, "provider", ru[i].provider);
cJSON_AddStringToObject(e, "model", ru[i].model);
cJSON_AddNumberToObject(e, "requests", ru[i].requests);
cJSON_AddNumberToObject(e, "max_steps", ru[i].max_steps);
cJSON_AddNumberToObject(e, "prompt_tokens", ru[i].prompt_tokens);
if (ru[i].cache_known) {
cJSON_AddNumberToObject(e, "cached_tokens", ru[i].cached_tokens);
cJSON_AddNumberToObject(e, "cache_write_tokens", ru[i].cache_write_tokens);
} else {
cJSON_AddNullToObject(e, "cached_tokens");
cJSON_AddNullToObject(e, "cache_write_tokens");
}
cJSON_AddNumberToObject(e, "output_tokens", ru[i].output_tokens);
if (ru[i].priced) {
cJSON_AddNumberToObject(e, "cost", ru[i].cost);
cJSON_AddStringToObject(e, "currency", ru[i].currency);
}
cJSON_AddNumberToObject(e, "steps", ru[i].steps);
cJSON_AddNumberToObject(e, "tools", ru[i].tools);
cJSON_AddNumberToObject(e, "ms", ru[i].ms);
@@ -2297,6 +2319,45 @@ static int h_ext_reload_x(cst_http_conn_t *c, void *ud) {
return wui_ext_reply(ud, c);
}
static int h_workers_get(cst_http_conn_t *c, void *ud) {
wui_t *w = ud;
if (wui_claim_read(w) != 0)
return cst_http_send_err(c, 409, "another change is running");
char *s = cst_workers_config_get(cst_agent_config(w->agent)->workspace_root);
cJSON *j = s ? cJSON_Parse(s) : NULL;
free(s);
wui_release(w);
if (!j) return cst_http_send_err(c, 400, "invalid worker configuration");
return wui_send_obj(c, 200, j);
}
static int h_workers_x(cst_http_conn_t *c, void *ud) {
wui_t *w = ud;
cJSON *j = wui_body(c);
char *s = j ? cJSON_PrintUnformatted(j) : NULL;
char err[256] = "expected JSON configuration";
int rc = s ? cst_workers_config_set(
cst_agent_config(w->agent)->workspace_root, s, err, sizeof(err)) : -1;
free(s); cJSON_Delete(j);
if (rc != 0) return cst_http_send_err(c, 400, err);
cst_workers_config_apply(cst_agent_config_mut(w->agent));
return cst_http_send_str(c, 200, "application/json", "{\"ok\":true}");
}
static int h_worker_cancel(cst_http_conn_t *c, void *ud) {
wui_t *w = ud;
if (wui_claim_read(w) != 0)
return cst_http_send_err(c, 409, "another change is running");
cJSON *j = wui_body(c);
const cJSON *id = cJSON_GetObjectItemCaseSensitive(j, "worker_id");
char sid[128];
cst_agent_session_id(w->agent, sid, sizeof(sid));
int rc = cJSON_IsString(id) ? cst_worker_cancel(
cst_agent_config(w->agent)->workspace_root, sid, id->valuestring) : -1;
cJSON_Delete(j);
wui_release(w);
if (rc != 0) return cst_http_send_err(c, 404, "running worker not found");
return cst_http_send_str(c, 200, "application/json", "{\"ok\":true}");
}
/* POST /api/extensions/trust {"on": bool}: whether .agent/plugins and
* .agent/mcp.json of this workspace load, then reload. */
static int h_ext_trust_x(cst_http_conn_t *c, void *ud) {
@@ -2321,6 +2382,7 @@ WUI_EXCLUSIVE(h_session_delete)
WUI_EXCLUSIVE(h_settings)
WUI_EXCLUSIVE(h_perms_reset)
WUI_EXCLUSIVE(h_ext_reload)
WUI_EXCLUSIVE(h_workers)
WUI_EXCLUSIVE(h_ext_trust)
WUI_EXCLUSIVE(h_undo)
WUI_EXCLUSIVE(h_compact)
@@ -2568,6 +2630,9 @@ int cst_webui_run(cst_agent_t *agent, const cst_webui_opts_t *opts) {
{"POST", "/api/ask_user", h_ask_user, NULL, 0, 0},
{"POST", "/api/config", h_config, NULL, 0, 0},
{"GET", "/api/providers", h_providers, NULL, 0, 0},
{"GET", "/api/workers/config", h_workers_get, NULL, 0, 0},
{"POST", "/api/workers/config", h_workers, NULL, 0, 0},
{"POST", "/api/workers/cancel", h_worker_cancel, NULL, 0, 0},
{"GET", "/api/models", h_models, NULL, 0, 0},
{"POST", "/api/provider", h_provider, NULL, 0, 0},
{"POST", "/api/key", h_key, NULL, 0, 0},