v0.8.3: WebUI models, providers and keys; garbled thinking fixed
Streaming: Chinese reasoning showed up garbled in the WebUI. The agent loop now feeds visible/thinking text in runs instead of byte by byte, and the WebUI holds an incomplete UTF-8 tail until the next chunk (cst_utf8_hold_feed), so every think_delta/assistant_delta is whole. src/provider_switch.c: model list fetch/cache, key verify-and-save and provider switching moved out of tui.c and shared by TUI and WebUI. WebUI API: - GET /api/providers, GET /api/models[?refresh=1] (401 on a rejected key, still listing built-ins), POST /api/provider, POST /api/key (key checked against /v1/models, saved to credentials 0600, never echoed in replies, events or logs) - /api/config validates everything before applying, persists model, effort and approval (not yolo), warns when effort does not apply; plan_mode no longer forces read-only - state: turn_usage, api_base, key_set/key_from, model_info, effort_applies, sees_images, max_steps, tool_profile, perm_timeout; permission events carry their timeout Frontend: searchable model picker with refresh and manual entry, provider picker and key dialog, effort/vision hints, toasts instead of silent failures, Chinese error text, real protocol in the settings and usage panels, approval countdown from the server's timeout. Tests: utf8 hold unit test, scripts/e2e_webui_settings.py (fake /v1/models, wrong key, switching, persistence, Chinese thinking) run from e2e_webui.sh, scripts/tui_keys/provider.py.
This commit is contained in:
@@ -280,8 +280,38 @@ static void test_sanitize(void) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Streamed deltas cut anywhere must reach the browser as whole characters. */
|
||||
static void test_utf8_hold(void) {
|
||||
const char *src = "思考:先读 main.c,再改 😀 emoji。";
|
||||
size_t len = strlen(src);
|
||||
for (size_t step = 1; step <= 5; step++) {
|
||||
cst_utf8_hold_t h = {0};
|
||||
cst_buf_t out = {0};
|
||||
for (size_t i = 0; i < len; i += step) {
|
||||
size_t n = len - i < step ? len - i : step;
|
||||
size_t got = 0;
|
||||
char *piece = cst_utf8_hold_feed(&h, src + i, n, &got);
|
||||
CHECK(piece != NULL);
|
||||
CHECK(h.n < 4);
|
||||
CHECK(cst_utf8_valid(piece, got));
|
||||
CHECK(cst_buf_append(&out, piece, got) == 0);
|
||||
free(piece);
|
||||
}
|
||||
CHECK(h.n == 0);
|
||||
CHECK(out.len == len && memcmp(out.buf, src, len) == 0);
|
||||
cst_buf_free(&out);
|
||||
}
|
||||
/* a stray continuation byte is not held forever */
|
||||
cst_utf8_hold_t h = {0};
|
||||
size_t got = 0;
|
||||
char *piece = cst_utf8_hold_feed(&h, "\x80\x80\x80\x80", 4, &got);
|
||||
CHECK(piece && got == 4 && h.n == 0);
|
||||
free(piece);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
CHECK(os_net_init() == 0);
|
||||
test_utf8_hold();
|
||||
test_query();
|
||||
test_sanitize();
|
||||
test_http();
|
||||
|
||||
Reference in New Issue
Block a user