mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-04 10:48:20 +00:00
feat(ui): honor the requested node budget up to a 10M ceiling
The layout endpoint treated the default render cap (2000) as a ceiling: an explicit max_nodes above it was silently clamped back down, so the UI could never show more than 2000 nodes regardless of the request. Make the default a default — an explicit request is now honored up to a hard ceiling of 10M nodes; CBM_UI_MAX_RENDER_NODES still adjusts the ceiling for constrained deployments. The default budget rises to 5000, and the layout refinement pass scales its iteration count down past 100k/500k bodies to keep very large budgets responsive. Edges need no changes: the layout already returns every edge between loaded nodes. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
1 parent
5554f8a46a
commit
926e818122
3 files changed
+69
-7
No files matched your search
@@ -1333,7 +1333,7 @@ static void handle_layout(cbm_http_conn_t *c, const cbm_http_req_t *req) {
|
||||
return;
|
||||
}
|
||||
|
||||
int max_nodes = 50000;
|
||||
int max_nodes = 0; /* 0 → layout default budget */
|
||||
if (cbm_http_query_param(req->query, "max_nodes", max_str, (int)sizeof(max_str))) {
|
||||
int v = atoi(max_str);
|
||||
if (v > 0)
|
||||
|
||||
+25
-6
@@ -24,8 +24,8 @@
|
||||
|
||||
/* ── Constants ────────────────────────────────────────────────── */
|
||||
|
||||
#define DEFAULT_MAX_NODES 2000
|
||||
#define HARD_MAX_NODES 200000
|
||||
#define DEFAULT_MAX_NODES 5000
|
||||
#define HARD_MAX_NODES 10000000
|
||||
#define BH_THETA 1.2f
|
||||
#define OCTREE_MAX_DEPTH 26 /* stop subdividing coincident points (OOM guard) */
|
||||
#define OCTREE_MIN_HALF 1e-4f /* minimum octree cell half-size */
|
||||
@@ -173,16 +173,19 @@ static float rand_float(uint32_t *seed) {
|
||||
return (float)((*seed >> 16) & 0x7FFF) / 32768.0f - 0.5f;
|
||||
}
|
||||
|
||||
/* Ceiling for a caller-requested node budget. CBM_UI_MAX_RENDER_NODES lowers
|
||||
* (or raises, up to HARD_MAX_NODES) the ceiling for constrained deployments;
|
||||
* without it the full hard ceiling is available to explicit requests. */
|
||||
static int render_node_limit(void) {
|
||||
const char *raw = getenv("CBM_UI_MAX_RENDER_NODES");
|
||||
if (!raw || !raw[0]) {
|
||||
return DEFAULT_MAX_NODES;
|
||||
return HARD_MAX_NODES;
|
||||
}
|
||||
errno = 0;
|
||||
char *end = NULL;
|
||||
long v = strtol(raw, &end, 10);
|
||||
if (errno != 0 || end == raw || *end != '\0' || v <= 0) {
|
||||
return DEFAULT_MAX_NODES;
|
||||
return HARD_MAX_NODES;
|
||||
}
|
||||
if (v > HARD_MAX_NODES) {
|
||||
return HARD_MAX_NODES;
|
||||
@@ -190,9 +193,15 @@ static int render_node_limit(void) {
|
||||
return (int)v;
|
||||
}
|
||||
|
||||
/* The default is a default, not a ceiling: an explicit request is honored up
|
||||
* to the (env-adjustable) ceiling; only a missing/invalid request falls back
|
||||
* to DEFAULT_MAX_NODES. */
|
||||
static int clamp_max_nodes(int requested) {
|
||||
int cap = render_node_limit();
|
||||
if (requested <= 0 || requested > cap) {
|
||||
if (requested <= 0) {
|
||||
requested = DEFAULT_MAX_NODES;
|
||||
}
|
||||
if (requested > cap) {
|
||||
return cap;
|
||||
}
|
||||
return requested;
|
||||
@@ -317,7 +326,17 @@ typedef struct {
|
||||
/* ── Local optimization (gentle, anchor-preserving) ───────────── */
|
||||
|
||||
static void local_optimize(body_t *b, int n, const int *es, const int *ed, int ne) {
|
||||
for (int iter = 0; iter < LOCAL_ITERATIONS; iter++) {
|
||||
/* Scale iteration effort down for very large graphs: each iteration is
|
||||
* O(n log n) octree work, and past ~100k bodies the anchor layout already
|
||||
* dominates the visible structure — fewer refinement passes keep huge
|
||||
* budgets responsive instead of multiplying minutes of compute. */
|
||||
int iterations = LOCAL_ITERATIONS;
|
||||
if (n > 500000) {
|
||||
iterations = 10;
|
||||
} else if (n > 100000) {
|
||||
iterations = 20;
|
||||
}
|
||||
for (int iter = 0; iter < iterations; iter++) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
b[i].fx = 0;
|
||||
b[i].fy = 0;
|
||||
|
||||
@@ -358,6 +358,48 @@ TEST(layout_clamps_render_cap_from_env) {
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* A caller-requested budget above the default must be honored (up to the hard
|
||||
* ceiling) when no env cap is set — the default is a default, not a ceiling. */
|
||||
TEST(layout_honors_budget_above_default) {
|
||||
cbm_store_t *store = cbm_store_open_memory();
|
||||
ASSERT_NOT_NULL(store);
|
||||
|
||||
const char *old_raw = getenv("CBM_UI_MAX_RENDER_NODES");
|
||||
char *old_cap = old_raw ? strdup(old_raw) : NULL;
|
||||
cbm_unsetenv("CBM_UI_MAX_RENDER_NODES");
|
||||
|
||||
cbm_store_upsert_project(store, "test", "/tmp/test");
|
||||
|
||||
enum { BUDGET_NODES = 5100 };
|
||||
for (int i = 0; i < BUDGET_NODES; i++) {
|
||||
char name[32], qn[64];
|
||||
snprintf(name, sizeof(name), "fn%d", i);
|
||||
snprintf(qn, sizeof(qn), "test::fn%d", i);
|
||||
cbm_node_t n = {.project = "test",
|
||||
.label = "Function",
|
||||
.name = name,
|
||||
.qualified_name = qn,
|
||||
.file_path = "a.c",
|
||||
.start_line = i,
|
||||
.end_line = i + 1};
|
||||
cbm_store_upsert_node(store, &n);
|
||||
}
|
||||
|
||||
cbm_layout_result_t *r =
|
||||
cbm_layout_compute(store, "test", CBM_LAYOUT_OVERVIEW, NULL, 0, BUDGET_NODES);
|
||||
ASSERT_NOT_NULL(r);
|
||||
ASSERT_EQ(r->node_count, BUDGET_NODES);
|
||||
ASSERT_EQ(r->total_nodes, BUDGET_NODES);
|
||||
|
||||
cbm_layout_free(r);
|
||||
cbm_store_close(store);
|
||||
if (old_cap) {
|
||||
cbm_setenv("CBM_UI_MAX_RENDER_NODES", old_cap, 1);
|
||||
free(old_cap);
|
||||
}
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST(layout_deterministic) {
|
||||
cbm_store_t *store = cbm_store_open_memory();
|
||||
ASSERT_NOT_NULL(store);
|
||||
@@ -759,6 +801,7 @@ SUITE(ui) {
|
||||
RUN_TEST(layout_two_connected);
|
||||
RUN_TEST(layout_respects_max_nodes);
|
||||
RUN_TEST(layout_clamps_render_cap_from_env);
|
||||
RUN_TEST(layout_honors_budget_above_default);
|
||||
RUN_TEST(layout_deterministic);
|
||||
RUN_TEST(layout_to_json);
|
||||
RUN_TEST(layout_null_inputs);
|
||||
|
||||
Reference in new issue
Block a user