code-graph-rag
repository·main·Indexed 25 days ago
https://github.com/vitali87/code-graph-ragA tool for multi-language monorepos that parses codebases into structural knowledge graphs using Tree-sitter and Memgraph. It enables advanced Retrieval-Augmented Generation (RAG) to query, understand, and edit code based on AST structure. The library supports polyglot ingestion across 14 languages and provides an extensible system to add language support via ast-grep patterns. Version 0.0.517 includes comprehensive evaluation suites for structural containment, call attribution, inheritance, and retrieval accuracy.
What's inside code-graph-rag
- Code-Graph-RAG is a Retrieval-Augmented Generation (RAG) system designed for monorepos. It uses Tree-sitter to parse multi-language codebases and builds a knowledge graph in Memgraph. This allows users to perform natural language querying of codebase structures, relationships, and code snippets, as well as perform AST-based code editing and optimization.
Optimize `should_skip_path` using string operations
mainThe
should_skip_pathfunction is slow because it usespathlib.Path.relative_to(), which creates intermediate objects on every call. Replacingpathlibwith string operations provides significant speedups.Fix: Convert paths to strings at the boundary of
should_skip_pathand usestr.removeprefix(),str.split("/"), andsetmembership testing instead ofPath.relative_to()andPath.parts.Limitations of FLOWS_TO Analysis
mainThe
FLOWS_TOanalysis is designed to be conservative and intra-procedural. Users should be aware of the following technical ceilings:- Intra-procedural: Value flow is tracked within a function body plus one level of argument/return hand-off. It is not path-sensitive; a 'kill' on one branch of an
if/elsedrops taint conservatively for both. - Single-pass return propagation: A callee whose body is processed after its caller (common in cross-file analysis) may not yet be known to return a tainted value.
- No SSA-level precision: The analysis uses direct I/O calls from the registry; it does not use
Parameternodes or SSA (Static Single Assignment) precision. - Language Support: The source/sink registry covers Python, JavaScript, TypeScript (including TSX), Go, Java, Rust, C, C++, and C#. Languages not in the registry will not emit I/O or flow edges until a table is added.
- Intra-procedural: Value flow is tracked within a function body plus one level of argument/return hand-off. It is not path-sensitive; a 'kill' on one branch of an
Reduce logging overhead by suppressing debug logs in production
mainProcessing
debug()calls via Loguru consumes significant CPU time (approx. 5.9%) even when debug output is not displayed.Optimization Tasks:
- Set the Loguru level to
INFOorWARNINGduringGraphUpdater.run(). - Wrap debug calls in guards:
if logger.level <= DEBUG:. - Use lazy evaluation for expensive format strings:
logger.opt(lazy=True).debug(lambda: ...).
- Set the Loguru level to
Optimize Call Resolution with Rust String Processing
mainMove call resolution logic into the Rust AST processing extension.
Warning: Do NOT implement this as a standalone optimization. Call resolution is deeply interleaved with trie lookups, import map lookups, and AST node access. Attempting to extract only string processing would require marshalling massive amounts of context (import maps, trie state, class inheritance) across the FFI boundary for every call, resulting in negative net gains.
Correct Strategy: Bundle this logic with the Rust AST extension so that call resolution happens entirely on the Rust side, eliminating FFI overhead during the resolution pass.
Understand .cgrignore pattern syntax rules
mainPatterns in
.cgrignorefollow standard gitignore conventions:*: Matches within a single path segment.**: Matches across multiple path segments (cross-segment).?: Matches a single character.- Bare name (
vendor): Matches a file or directory with that name at any depth. - Slash-anchored (
docs/*.md,/generated): Patterns containing a slash are anchored to the repository root. - Trailing slash (
build/): Matches directories only. - Negation (
!bin/keep.py): Lines starting with!un-ignore paths that would otherwise be skipped by default exclusions. Note that explicit excludes always take precedence over un-ignores. - Comments: Lines starting with
#are ignored; blank lines are also ignored.
Understand the Taint Analysis mental model
mainThe data-flow analysis uses a taint analysis model to track how values move through code:
- Source: Where a value enters the program from the outside world (e.g., reading an environment variable or a file). A value from a source is considered tainted.
- Propagation: How the 'taint' travels through the program via assignments, function arguments, and return values.
- Sink: Where a tainted value leaves the program (e.g., writing to a file, a socket, or standard output).
By representing these as graph edges, you can perform reachability queries such as: "Does any value from this specific source reach that specific sink?"
Metavariable conventions for ast-grep patterns
mainWhen writing patterns in your YAML configuration, you must follow specific metavariable naming conventions to ensure the parser correctly identifies entities:
- Definition patterns (
functions,classes): You must capture the name using the exact metavariable$NAME. - Import patterns: You must capture the imported path using the exact metavariable
$PATH. Note that surrounding quotes in the source code are automatically stripped from the captured value.
- Definition patterns (
Optimize `FunctionRegistryTrie.find_ending_with` performance
mainThe
FunctionRegistryTrie.find_ending_with()method currently suffers from a significant performance bottleneck because it falls back to a linear scan when the_simple_name_lookupindex misses. This can be fixed by ensuring a complete suffix index is built.Fix: Build a complete suffix index in
FunctionRegistryTrieby populating_simple_name_lookupfor every insert. Ensure all insertion code paths (including__setitem__) update the index to eliminate the linear scan fallback.Pattern ordering in ast-grep configurations
mainPatterns in the
functions,classes, andimportslists are evaluated in the order they are defined. The first pattern to match a source line claims it.To avoid incorrect matches, always place more specific patterns before more general ones.
Example: If you have
def self.build, the patterndef self.$NAMEshould come beforedef $NAME. Ifdef $NAMEwere first, it would incorrectly captureselfas the function name instead ofbuild.Improve AST traversal efficiency via `build_local_variable_type_map` caching
mainThe
build_local_variable_type_mapfunction causes redundant AST traversals (approx. 8.3% CPU usage) when multiple functions in the same file trigger independent traversals.Optimization Task: Implement memoization for this function. Use a cache key composed of
(file_path, function_start_line, function_end_line)to return previously computed results. Ensure the cache is invalidated when files change via the existing incremental update system.Understand C retrieval limitations (Preprocessor Gap)
mainWhen evaluating C retrieval, be aware of the gap between
cgr(usingtree-sitter) and thelibclangoracle caused by the C preprocessor:- False Positives:
cgrmay emit calls located inside inactive conditional branches (e.g., code inside#ifdefblocks that are not defined).libclangcorrectly compiles these out. - False Negatives:
cgrmay miss calls that only exist after macro expansion (e.g., macros that expand to function calls likejv_object_foreachor bison/flex API macros).
- False Positives: