From 50987d37b80299e1e8767510e8a60f4130db1c42 Mon Sep 17 00:00:00 2001 From: superbling Date: Sun, 10 May 2026 07:30:00 +0800 Subject: [PATCH] Fix /ctx error handling in interactive REPL In REPL, parse_int() exits the process via exit(2) on invalid input, which is correct for command-line parsing but kills the interactive session, dropping the KV cache and conversation. Add parse_int_repl() which prints the same error and returns false instead, so the REPL can just prompt again. repl_chat_set_ctx() freed the existing session before trying to create the replacement. If creation failed (e.g. allocation error for the new context size), the old session was already gone and run_repl broke out of the loop. Drop the redundant pre-free; repl_chat_create_session already keeps the old session intact on failure, so /ctx can now report the error and keep the current context. --- ds4_cli.c | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/ds4_cli.c b/ds4_cli.c index ed53248..2562219 100644 --- a/ds4_cli.c +++ b/ds4_cli.c @@ -180,6 +180,17 @@ static int parse_int(const char *s, const char *opt) { return (int)v; } +static bool parse_int_repl(const char *s, const char *opt, int *out) { + char *end = NULL; + long v = strtol(s, &end, 10); + if (s[0] == '\0' || *end != '\0' || v <= 0 || v > INT32_MAX) { + fprintf(stderr, "ds4: invalid value for %s: %s\n", opt, s); + return false; + } + *out = (int)v; + return true; +} + static uint64_t parse_u64(const char *s, const char *opt) { char *end = NULL; unsigned long long v = strtoull(s, &end, 10); @@ -861,9 +872,6 @@ static void repl_chat_free(repl_chat *chat) { } static int repl_chat_set_ctx(ds4_engine *engine, repl_chat *chat, int ctx_size) { - ds4_session_free(chat->session); - chat->session = NULL; - chat->ctx_size = 0; return repl_chat_create_session(engine, chat, ctx_size); } @@ -1057,17 +1065,20 @@ static int run_repl(ds4_engine *engine, cli_config *cfg) { if (!arg[0]) { fprintf(stderr, "ds4: /ctx needs a positive integer\n"); } else { - cfg->gen.ctx_size = parse_int(arg, "/ctx"); - log_context_memory(cfg->engine.backend, cfg->gen.ctx_size); - rc = repl_chat_set_ctx(engine, &chat, cfg->gen.ctx_size); - if (rc != 0) { - linenoiseFree(line); - break; + int new_ctx; + if (parse_int_repl(arg, "/ctx", &new_ctx)) { + log_context_memory(cfg->engine.backend, new_ctx); + if (repl_chat_set_ctx(engine, &chat, new_ctx) == 0) { + cfg->gen.ctx_size = new_ctx; + bool active = ds4_think_mode_for_context(cfg->gen.think_mode, + chat.ctx_size) == DS4_THINK_MAX; + repl_chat_apply_max_prefix(engine, &chat, active); + cli_warn_think_max_downgraded(&cfg->gen, "/ctx"); + } else { + fprintf(stderr, "ds4: /ctx %d failed; keeping current ctx %d\n", + new_ctx, chat.ctx_size); + } } - bool active = ds4_think_mode_for_context(cfg->gen.think_mode, - chat.ctx_size) == DS4_THINK_MAX; - repl_chat_apply_max_prefix(engine, &chat, active); - cli_warn_think_max_downgraded(&cfg->gen, "/ctx"); } } else if (!strcmp(cmd, "/quit") || !strcmp(cmd, "/exit")) { linenoiseFree(line);