Repository navigation
Description
Summary
Issue #578 / PR #715 appears fixed for the clean minimal Python module-attribute cases in codegraph@1.0.1, but the original RAGFlow production case still has missing calls edges after a full reindex.
The narrowed finding:
from pkg import mod; mod.target(...)now works in a fresh scratch project.import pkg.mod as mod; mod.target(...)now works in a fresh scratch project.from api.db.services import dialog_service; dialog_service._private_shim(...)also works in a fresh scratch project.- But in the real RAGFlow tree,
from api.db.services import dialog_service; dialog_service._repair_grounded_citation_alignment(...)still reports no callers, even though both caller and callee nodes are indexed and the call expressions are visible incodegraph node.
So the broad #578 bug is fixed, but there is still a narrower miss in the larger package/import context.
Environment
codegraph --version->1.0.1- Linux
- Project: RAGFlow fork
- Full reindex performed after upgrading:
codegraph index .The index reported:
Indexed 2,982 files
0 nodes, 4,425 edges in 14.9s
Control checks that now pass
I created a fresh scratch project under /tmp/codegraph-attr-test:
# pkg/mod.py
def target(value: int) -> int:
return value + 1
# use_from_pkg_import_module.py
from pkg import mod
def caller_from_pkg_import_module() -> int:
return mod.target(1)
# use_import_pkg_module_as_module.py
import pkg.mod as mod
def caller_import_pkg_module_as_module() -> int:
return mod.target(2)
# use_from_pkg_mod_import_target.py
from pkg.mod import target
def caller_direct_import() -> int:
return target(3)After codegraph init /tmp/codegraph-attr-test:
codegraph callers target -p /tmp/codegraph-attr-testcorrectly reports all three callers:
caller_from_pkg_import_module
caller_direct_import
caller_import_pkg_module_as_module
I also tested the exact package-level import shape in a fresh scratch project under /tmp/codegraph-dialog-shape:
# api/db/services/dialog_service.py
def shim(value: int) -> int:
return value + 10
def _private_shim(value: int) -> int:
return value + 20
# test/unit_test/api/db/services/test_prompt.py
from api.db.services import dialog_service
def test_calls_module_attribute() -> None:
assert dialog_service.shim(1) == 11
def test_calls_private_module_attribute() -> None:
assert dialog_service._private_shim(1) == 21After codegraph init /tmp/codegraph-dialog-shape:
codegraph callers shim -p /tmp/codegraph-dialog-shape
codegraph callers _private_shim -p /tmp/codegraph-dialog-shapeboth correctly report the test callers.
Real RAGFlow case that still fails
The real code has:
# test/unit_test/api/db/services/test_dialog_service_prompt_sanitization.py:21
from api.db.services import dialog_serviceand calls such as:
# test/unit_test/api/db/services/test_dialog_service_prompt_sanitization.py:1770
answer, cited = dialog_service._repair_grounded_citation_alignment(
"Feng Shi Pian and Feng Shi Xiao Tong Pian are listed as formulas [ID:2].",
ledger,
)
# test/unit_test/api/db/services/test_dialog_service_prompt_sanitization.py:1774
answer = dialog_service._preserve_grounded_reference_labels(answer, ledger)The target functions are indexed:
codegraph node _repair_grounded_citation_alignment -p /home/jo/rf/ragflow_p1
codegraph node _preserve_grounded_reference_labels -p /home/jo/rf/ragflow_p1and the caller function is indexed with the call expressions visible:
codegraph node test_repair_grounded_citation_alignment_replaces_wrong_valid_id_before_label_injection -p /home/jo/rf/ragflow_p1shows:
answer, cited = dialog_service._repair_grounded_citation_alignment(...)
answer = dialog_service._preserve_grounded_reference_labels(answer, ledger)But callers are still missing:
codegraph callers _repair_grounded_citation_alignment -p /home/jo/rf/ragflow_p1
codegraph callers _preserve_grounded_reference_labels -p /home/jo/rf/ragflow_p1Observed:
No callers found for "_repair_grounded_citation_alignment"
No callers found for "_preserve_grounded_reference_labels"
Same symptom for other module-attribute calls from the same test file, for example:
dialog_service._compute_prior_weight(...)
dialog_service._expand_query_with_symbols(...)codegraph callers _compute_prior_weight reports internal/import-related callers, but not the test functions that call it via dialog_service._compute_prior_weight(...).
Expected
After #715, the real RAGFlow production case from #578 should also get calls edges:
test_repair_grounded_citation_alignment_replaces_wrong_valid_id_before_label_injection
-> _repair_grounded_citation_alignment
test_repair_grounded_citation_alignment_replaces_wrong_valid_id_before_label_injection
-> _preserve_grounded_reference_labels
and similarly for the other dialog_service.<function>(...) test calls.
Why this matters
The practical failure mode is still the same as #578: these functions look unused through codegraph_callers, even though regression tests call them by module-attribute access. That makes dead-code cleanup and impact analysis unsafe unless users pair CodeGraph with textual grep.
The fact that fresh scratch projects pass suggests the remaining bug is not the original broad resolver gap, but something about the larger package/import context in the real RAGFlow tree.
Ask
Can you take a second look at Python module-attribute resolution for larger package trees where:
from api.db.services import dialog_service
dialog_service.some_function(...)The minimal fixture now passes in 1.0.1, but the original production case still misses the edge after a full reindex.
Activity
maxmilian commentedon Jun 18, 2026
Thanks for the unusually precise narrowing — the scratch-vs-real split is exactly the right way to corner this.
I built
codegraph@mainand tried to reproduce the remaining miss with isolatedresolutiontests that match your documented shape and then some, to find the trigger. All of these pass (thecallsedge resolves correctly), so they rule those factors out as the cause on their own:from api.db.services import dialog_service+__init__.pyat every leveltest/unit_test/api/db/services/…dir_repair_grounded_citation_alignment)@functools.lru_cache(...)-decorated targetunittest.TestCaseclass method rather than a baredefagent/services/dialog_service.pythat also defines_repair_grounded_citation_alignment(resolution still picks the correctapi/db/servicesone)So in isolation none of these reproduce it, which lines up with your read that it's something about the larger tree.
Code-level, a missing edge here means
resolvePythonModuleMember(src/resolution/import-resolver.ts) returnednull, which happens for exactly one of three reasons:importsdon't include a binding withlocalName === "dialog_service", orfindPythonModuleFile("api.db.services.dialog_service", …)matched no file ending inapi/db/services/dialog_service.py, orkindfunction/class/variable/constantand deliberately excludesmethod(so amod.foonever lands on a same-named class method).To pin which branch it is in the real tree, could you grab a few signals from
ragflow_p1:codegraph node _repair_grounded_citation_alignment— what kind does it report (function vs method), and is it defined at module level or nested inside a class /if/try?api/db/services/dialog_service.py, or more than onedialog_service.pyin the tree?from api.db.services import dialog_service, or multi-name / aliased / inside aTYPE_CHECKINGblock?0 nodes, 4,425 edges— was that an incrementalcodegraph index .over an already-populated DB? A cleancodegraph init(fresh DB) vs an incremental re-index can resolve differently; worth confirming the miss survives a from-scratch index.If any one of those points at the failing branch, a minimal fixture (and a fix) follows quickly. Happy to keep digging once we know which of the three it is.
JosefAschauer commentedon Jun 18, 2026
Thanks, I collected those signals and also ran the clean-index check.
Signals from the real
ragflow_p1tree:codegraph node _repair_grounded_citation_alignment -p /home/jo/rf/ragflow_p1reportskind=function, module-level, atapi/db/services/dialog_service.py:784. It is not nested in a class/if/try.api/db/services/dialog_service.py.test/unit_test/api/db/services/test_dialog_service_prompt_sanitization.py:21hasfrom api.db.services import dialog_service. It is not aliased, multi-name, or insideTYPE_CHECKING.codegraph callers _repair_grounded_citation_alignment -p /home/jo/rf/ragflow_p1returned no callers.Then I tested a fresh DB without touching the real repo index:
/tmp/ragflow-p1-codegraph-clean, excluding.git,.codegraph,.venv,web/node_modules, generated dirs, logs, and large resource dirs.codegraph init /tmp/ragflow-p1-codegraph-clean.Indexed 3,291 files,67,456 nodes,208,154 edges.Fresh-index results:
Also:
returns the expected 5 test callers, and:
returns
async_chatplus the expected test callers.So
1.0.1/ current resolver behavior appears to fix the module-attribute caller issue on a clean index. The remaining repro is: an existing.codegraphDB that had previously missed these edges did not recover aftercodegraph index .incremental reindex. My best read is that this issue is now about stale/incomplete incremental index invalidation, notresolvePythonModuleMemberfailing on this import shape anymore.JosefAschauer commentedon Jun 18, 2026
One more useful signal: I hit a very small incremental-index repro in the real tree.
Sequence:
.codegraphaside.codegraph init .in/home/jo/rf/ragflow_p1.Indexed 3,291 files,67,456 nodes,208,154 edges._repair_grounded_citation_alignmentreturned 4 test callers._preserve_grounded_reference_labelsreturned 5 test callers.api/db/services/dialog_service.pyaround the wrapper comment. No call sites changed.codegraph index ..Indexed 3,291 files,0 nodes,5,296 edges.Then I moved that broken incremental DB aside and ran another clean
codegraph init .; the callers resolved again.So the repro is now tighter: a clean index resolves module-attribute callers correctly, but an incremental
codegraph index .after a docstring-only edit in the target module can drop those caller edges. I preserved the broken incremental DB locally as/tmp/ragflow-p1-codegraph-incremental-broken-20260618in case there is a useful local inspection command to run against it.JosefAschauer commentedon Jun 18, 2026
@/tmp/issue899-followup-corrected.md
JosefAschauer commentedon Jun 19, 2026
Follow-up 2: #894 already fixes the
indexpath onmain;syncstill brokenBuilt
main(commitf34f606, post-#894, not yet released —v1.0.1is still the latest tag) and re-ran the same docstring-edit repro against it. Results:main(post-#894)codegraph index .0 nodes, 5,295 edges)67,445 nodes, 211,532 edges)codegraph sync .sync())So #894 (make
indexa clear-then-rebuild) already closes theindexpath — once it ships,codegraph index .becomes a safe refresh. Thanks for that.The remaining bug is
sync()only — which is the path the watcher uses, so everycodegraph serve --mcpeditor/agent integration is affected.sync()'s filesystem reconcile re-indexes just the changed callee file viaindexFile(extraction/index.js:1611onmain) and, in doing so, drops the incomingcallsedges from unchanged caller files that import it via module-attribute access. A docstring-only edit on the callee is sufficient to trigger it.I'm going to look at a fix for the
syncpath — likely preserving existing cross-filecallsedges whose caller endpoint still has the call expression in its indexed body, rather than dropping them on callee re-index. Will open a PR if it pans out.JosefAschauer commentedon Jun 19, 2026
Opened PR #927 with a fix for the
syncpath: snapshot incoming cross-file edges before the callee re-index, re-resolve targets by (filePath, kind, name) after, re-insert. Two regression tests model the RAGFlow repro (line-shift case + rename case). Full suite green (1412 passed). Closes #899.fix(sync): preserve cross-file caller edges across callee re-index (#899
colbymchenry commentedon Jun 21, 2026
Resolved — and thank you for the outstanding diagnostic work here, @JosefAschauer. The scratch-vs-real narrowing that proved a clean index resolves fine, then pinning the regression to the incremental path, was what cracked this.
For posterity, the two-part resolution:
codegraph index— fixed by fix(cli): makecodegraph indexa full rebuild so it stops reporting 0 nodes (#874) #894 (it now clears-and-rebuilds instead of leaving a stale graph). Already onmain.codegraph sync(and the file-watcher / MCP auto-sync that calls it) — fixed by fix(sync): preserve cross-file caller edges across callee re-index (#899) #927, just merged. The real bug wasn'tresolvePythonModuleMemberat all; it was an FK-cascade edge drop. Re-indexing a callee file deletes the incoming cross-filecalls/referencesedges from unchanged caller files (module-attribute access likefrom pkg import mod; mod.fn()), and only same-file edges get re-emitted. fix(sync): preserve cross-file caller edges across callee re-index (#899) #927 snapshots those incoming edges and re-resolves them by(kind, name)after re-index, so they survive line shifts — a docstring-only edit was enough to trigger the original drop.I reproduced the RAGFlow shape, confirmed #927 fixes it, and the full suite is green. The fix is language-agnostic, so it also covers the same edge-drop for other languages (and even synthesized cross-file edges that
syncwas dropping). Landing in the next release.Thanks again for the careful narrowing and the clean PR with regression tests. 🙏
JosefAschauer commentedon Jun 21, 2026
docs(changelog): note the sync cross-file caller-edge fix (#899)
fix(sync): resolve cross-file refs when an edit adds or removes the s…
br41nlet commentedon Jul 25, 2026
Reproduced on 1.5.0, and the remaining cause looks like an alias mismatch rather than a package-depth problem.
resolvePythonModuleMember(andresolveModuleImportToFilefor the file→file edge) name the target submodule by joining the import source withimp.localName. Underfrom pkg import module as aliasthat is the alias, so the lookup asks forpkg.alias— which names no module — and the edge is dropped.localNameandexportedNamecoincide only when the import is unaliased, which is why the scratch cases in this thread passed and the real tree did not: minimal repros rarely alias, production code usually does.Minimal repro:
callergets no outgoingcallsedge andcodegraph callers funcreports none. Removingas mod_aliasmakes both appear. Same for the file→fileimportsedge.Confirming it is resolution and not extraction: the reference rows are present and correct in
unresolved_refs, with the receiver intact andstatus = failed— the extractor recordspkg_alias.func, resolution just never matches it.Measured on a 991-file Python project that aliases module imports throughout: a target with 6 call sites across 5 enclosing functions went from 0 callers to 5, another went 2 → 4, and the index gained ~824 edges. Unaliased and class-based resolution were unchanged, and the full suite's failing-test set was byte-identical to unmodified
main.PR: #1453