Skip to content

Commit 5f906a2

Browse files
authored
Merge pull request #2574 from DeusData/fix/semantic-tfidf-vocabulary
Semantic relations with a judged p; doc sections linked to their code by format and position
2 parents cb8b641 + 827b526 commit 5f906a2

13 files changed

Lines changed: 3433 additions & 104 deletions

File tree

‎src/mcp/mcp.c‎

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12906,6 +12906,154 @@ static char *snippet_budget_floor(bool json_format, int max_output_tokens) {
1290612906
return json;
1290712907
}
1290812908

12909+
/* Doc -> code candidates (doc_link_candidates): on a Section, the functions,
12910+
* whole files and the home folder it is possibly about; on a function or
12911+
* method, the sections that possibly describe it; on a file (get_file_outline),
12912+
* the sections that possibly describe the file or a folder holding it --
12913+
* highest p first. Candidates with a judged probability for the agent to
12914+
* verify, never links. A row whose other end no longer resolves (a delta
12915+
* update renamed or removed it) is skipped. */
12916+
enum { MCP_DOC_CANDIDATES_MAX = 5, MCP_DOC_CANDIDATES_SECTION_MAX = 10 };
12917+
static const char MCP_DOC_CANDIDATES_NOTE[] =
12918+
"doc<->code candidates by shared terms or the doc's folder, not links: p is the judged "
12919+
"probability the section is about the target; verify";
12920+
12921+
/* The evidence fields of a candidate row as shown: kind, position, and (on
12922+
* the section side) the shared terms and name tokens. */
12923+
static void doc_candidate_evidence(yyjson_mut_doc *doc, yyjson_mut_val *o, const char *evidence,
12924+
bool counts) {
12925+
yyjson_doc *ev = evidence ? yyjson_read(evidence, strlen(evidence), 0) : NULL;
12926+
yyjson_val *root = ev ? yyjson_doc_get_root(ev) : NULL;
12927+
static const char *const STRS[] = {"kind", "position", NULL};
12928+
for (int k = 0; root && STRS[k]; k++) {
12929+
yyjson_val *v = yyjson_obj_get(root, STRS[k]);
12930+
if (v && yyjson_is_str(v)) {
12931+
yyjson_mut_obj_add_strcpy(doc, o, STRS[k], yyjson_get_str(v));
12932+
}
12933+
}
12934+
static const char *const INTS[] = {"shared_terms", "name_tokens", NULL};
12935+
for (int k = 0; counts && root && INTS[k]; k++) {
12936+
yyjson_val *v = yyjson_obj_get(root, INTS[k]);
12937+
if (v && yyjson_is_int(v)) {
12938+
yyjson_mut_obj_add_int(doc, o, INTS[k], (int)yyjson_get_int(v));
12939+
}
12940+
}
12941+
yyjson_doc_free(ev);
12942+
}
12943+
12944+
/* rows -> an array of shown candidates; the other end of each row is its
12945+
* target (by_section) or its section. Returns how many resolved. */
12946+
static int doc_candidates_array(yyjson_mut_doc *doc, yyjson_mut_val *arr, cbm_store_t *store,
12947+
const char *project, const cbm_doc_candidate_t *rows, int n,
12948+
bool by_section) {
12949+
int added = 0;
12950+
for (int i = 0; i < n; i++) {
12951+
cbm_node_t other = {0};
12952+
if (cbm_store_find_node_by_qn(store, project,
12953+
by_section ? rows[i].target_qn : rows[i].section_qn,
12954+
&other) != CBM_STORE_OK) {
12955+
continue;
12956+
}
12957+
yyjson_mut_val *o = yyjson_mut_obj(doc);
12958+
yyjson_mut_obj_add_strcpy(doc, o, by_section ? "qualified_name" : "section",
12959+
other.qualified_name ? other.qualified_name : "");
12960+
if (by_section) {
12961+
yyjson_mut_obj_add_strcpy(doc, o, "label", other.label ? other.label : "");
12962+
}
12963+
yyjson_mut_obj_add_strcpy(doc, o, "file_path", other.file_path ? other.file_path : "");
12964+
yyjson_mut_obj_add_int(doc, o, "start_line", other.start_line);
12965+
if (by_section) {
12966+
yyjson_mut_obj_add_int(doc, o, "end_line", other.end_line);
12967+
}
12968+
yyjson_mut_obj_add_real(doc, o, "p", (double)(int)(rows[i].p * 1000.0 + 0.5) / 1000.0);
12969+
doc_candidate_evidence(doc, o, rows[i].evidence, by_section);
12970+
yyjson_mut_arr_append(arr, o);
12971+
added++;
12972+
free_node_contents(&other);
12973+
}
12974+
return added;
12975+
}
12976+
12977+
static void add_doc_candidates(yyjson_mut_doc *doc, yyjson_mut_val *root, cbm_store_t *store,
12978+
const cbm_node_t *node) {
12979+
if (!store || !node->label || !node->qualified_name || !node->project) {
12980+
return;
12981+
}
12982+
bool section = strcmp(node->label, "Section") == 0;
12983+
if (!section && strcmp(node->label, "Function") != 0 && strcmp(node->label, "Method") != 0) {
12984+
return;
12985+
}
12986+
cbm_doc_candidate_t *rows = NULL;
12987+
int n = 0;
12988+
if (cbm_store_doc_candidates_get(store, node->project, section ? node->qualified_name : NULL,
12989+
section ? NULL : node->qualified_name,
12990+
section ? MCP_DOC_CANDIDATES_SECTION_MAX
12991+
: MCP_DOC_CANDIDATES_MAX,
12992+
&rows, &n) != CBM_STORE_OK) {
12993+
return;
12994+
}
12995+
yyjson_mut_val *arr = yyjson_mut_arr(doc);
12996+
int added = doc_candidates_array(doc, arr, store, node->project, rows, n, section);
12997+
cbm_store_doc_candidates_free(rows, n);
12998+
if (added > 0) {
12999+
yyjson_mut_obj_add_val(doc, root, section ? "possibly_about" : "possibly_described_in",
13000+
arr);
13001+
yyjson_mut_obj_add_str(doc, root, "candidates_note", MCP_DOC_CANDIDATES_NOTE);
13002+
}
13003+
}
13004+
13005+
/* get_file_outline: the sections possibly about the file as a whole or about
13006+
* a folder holding it (NULL: none). */
13007+
static yyjson_mut_val *file_doc_candidates(yyjson_mut_doc *doc, cbm_store_t *store,
13008+
const char *project, const char *path) {
13009+
cbm_doc_candidate_t *rows = NULL;
13010+
int n = 0;
13011+
if (cbm_store_doc_candidates_for_path(store, project, path, MCP_DOC_CANDIDATES_MAX, &rows,
13012+
&n) != CBM_STORE_OK) {
13013+
return NULL;
13014+
}
13015+
yyjson_mut_val *arr = yyjson_mut_arr(doc);
13016+
int added = doc_candidates_array(doc, arr, store, project, rows, n, false);
13017+
cbm_store_doc_candidates_free(rows, n);
13018+
return added > 0 ? arr : NULL;
13019+
}
13020+
13021+
static const char *mut_str(yyjson_mut_val *o, const char *key) {
13022+
yyjson_mut_val *v = yyjson_mut_obj_get(o, key);
13023+
return v && yyjson_mut_is_str(v) ? yyjson_mut_get_str(v) : "";
13024+
}
13025+
13026+
/* The tree form of file_doc_candidates. */
13027+
static void file_doc_candidates_tree(cbm_sb_t *sb, cbm_store_t *store, const char *project,
13028+
const char *path) {
13029+
yyjson_mut_doc *doc = yyjson_mut_doc_new(NULL);
13030+
yyjson_mut_val *arr = doc ? file_doc_candidates(doc, store, project, path) : NULL;
13031+
if (arr) {
13032+
static const char *const columns[] = {"section", "file_path", "line", "p", "kind"};
13033+
cbm_tree_table_header(sb, "possibly_described_in", (int)yyjson_mut_arr_size(arr), columns,
13034+
(int)(sizeof(columns) / sizeof(columns[0])));
13035+
size_t i = 0;
13036+
size_t max = 0;
13037+
yyjson_mut_val *o = NULL;
13038+
yyjson_mut_arr_foreach(arr, i, max, o) {
13039+
char line[CBM_SZ_32];
13040+
char p[CBM_SZ_32];
13041+
snprintf(line, sizeof(line), "%d",
13042+
(int)yyjson_mut_get_int(yyjson_mut_obj_get(o, "start_line")));
13043+
snprintf(p, sizeof(p), "%.2f", yyjson_mut_get_real(yyjson_mut_obj_get(o, "p")));
13044+
cbm_tree_row_begin(sb);
13045+
cbm_tree_cell_str(sb, mut_str(o, "section"), true);
13046+
cbm_tree_cell_str(sb, mut_str(o, "file_path"), false);
13047+
cbm_tree_cell_str(sb, line, false);
13048+
cbm_tree_cell_str(sb, p, false);
13049+
cbm_tree_cell_str(sb, mut_str(o, "kind"), false);
13050+
cbm_tree_row_end(sb);
13051+
}
13052+
cbm_tree_scalar_str(sb, "candidates_note", MCP_DOC_CANDIDATES_NOTE);
13053+
}
13054+
yyjson_mut_doc_free(doc);
13055+
}
13056+
1290913057
static char *build_snippet_response(cbm_mcp_server_t *srv, cbm_node_t *node,
1291013058
const char *match_method, bool include_neighbors,
1291113059
cbm_node_t *alternatives, int alt_count, const char *args) {
@@ -13122,6 +13270,8 @@ static char *build_snippet_response(cbm_mcp_server_t *srv, cbm_node_t *node,
1312213270
yyjson_mut_obj_add_val(doc, root_obj, "alternatives", arr);
1312313271
}
1312413272

13273+
add_doc_candidates(doc, root_obj, store, node);
13274+
1312513275
bool bounded_default = max_output_tokens > 0;
1312613276
size_t snippet_byte_budget =
1312713277
bounded_default ? (size_t)max_output_tokens * (size_t)MCP_OUTPUT_BYTES_PER_TOKEN_ESTIMATE
@@ -13387,6 +13537,9 @@ static char *handle_get_file_outline(cbm_mcp_server_t *srv, const char *args) {
1338713537
cbm_tree_scalar_int(&sb, "limit", limit);
1338813538
cbm_tree_scalar_int(&sb, "returned", row_count);
1338913539
cbm_tree_scalar_bool(&sb, "has_more", (int64_t)offset + row_count < total);
13540+
if (offset == 0) {
13541+
file_doc_candidates_tree(&sb, store, project, normalized_path);
13542+
}
1339013543
payload = cbm_sb_finish(&sb);
1339113544
} else {
1339213545
yyjson_mut_doc *doc = yyjson_mut_doc_new(NULL);
@@ -13422,6 +13575,12 @@ static char *handle_get_file_outline(cbm_mcp_server_t *srv, const char *args) {
1342213575
yyjson_mut_obj_add_int(doc, object, "limit", limit);
1342313576
yyjson_mut_obj_add_int(doc, object, "returned", row_count);
1342413577
yyjson_mut_obj_add_bool(doc, object, "has_more", (int64_t)offset + row_count < total);
13578+
yyjson_mut_val *described =
13579+
offset == 0 ? file_doc_candidates(doc, store, project, normalized_path) : NULL;
13580+
if (described) {
13581+
yyjson_mut_obj_add_val(doc, object, "possibly_described_in", described);
13582+
yyjson_mut_obj_add_str(doc, object, "candidates_note", MCP_DOC_CANDIDATES_NOTE);
13583+
}
1342513584
payload = yy_doc_to_str(doc);
1342613585
yyjson_mut_doc_free(doc);
1342713586
}

0 commit comments

Comments
 (0)