Code Indexing
SmartMemory can parse and index entire code repositories, turning source code into searchable knowledge graph nodes. Classes, functions, routes, tests, and their relationships become first-class entities that you can query alongside your other memories.
How It Works
ingest_code() performs a multi-pass analysis of your repository:
- File collection -- walks the directory tree, collecting files by language.
- AST parsing -- extracts code entities (classes, functions, routes, tests) and relationships (imports, calls, inheritance) from each file.
- Cross-file resolution -- builds a symbol table to resolve import bindings across modules, so
CALLSandIMPORTSedges point to the correct target nodes. - Graph writing -- creates nodes and edges in the knowledge graph using bulk operations.
- Embedding generation -- generates vector embeddings for each entity so code symbols appear in
search()results. - Pattern seeding -- promotes class and function names into the EntityRuler pattern set, so future
ingest()calls over natural-language text recognise references to your codebase.
Basic Usage
from smartmemory import SmartMemory
memory = SmartMemory()
# Index a Python repository
result = memory.ingest_code(
directory="/path/to/my-project",
repo="my-project",
)
print(f"Parsed {result.files_parsed} files")
print(f"Created {result.entities_created} entities, {result.edges_created} edges")
print(f"Generated {result.embeddings_generated} embeddings")
print(f"Completed in {result.elapsed_seconds}s")Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
directory | str | required | Absolute path to the repository root. |
repo | str | required | Short repository identifier (e.g. "my-service"). Used as a namespace prefix on all generated node IDs. |
languages | list[str] | ["python"] | Languages to index. Pass ["python", "typescript"] to include .ts, .tsx, .js, and .jsx files. |
commit_hash | str | None | None | Git commit SHA to attach. Auto-detected via git rev-parse HEAD when None. Pass "" to suppress auto-detection. |
exclude_dirs | list[str] | None | None | Directory names to skip (e.g. ["node_modules", ".venv"]). Defaults include __pycache__, .git, .venv, venv, node_modules, .tox, .mypy_cache, .pytest_cache. |
Multi-Language Example
result = memory.ingest_code(
directory="/path/to/fullstack-app",
repo="fullstack-app",
languages=["python", "typescript"],
exclude_dirs=["node_modules", "dist", ".venv"],
)Entity Types
The parser extracts five entity types from source code:
| Entity Type | Description | Example |
|---|---|---|
module | A source file (one per parsed file) | smartmemory/code/parser.py |
class | Class definition | class CodeParser |
function | Function or method definition | def parse_file(...) |
route | FastAPI/Express route handler | @router.get("/health") |
test | Test function (pytest test_ prefix) | def test_index_python_repo() |
Each entity is assigned a deterministic ID in the format code::{repo}::{file_path}::{name}, making re-indexing idempotent.
Node Properties
Every code entity node carries these properties in the knowledge graph:
| Property | Type | Always Present | Description |
|---|---|---|---|
item_id | str | yes | Deterministic node ID (code::repo::path::name) |
name | str | yes | Entity name |
entity_type | str | yes | One of: module, class, function, route, test |
file_path | str | yes | Relative path from the repository root |
line_number | int | yes | Line number where the entity is defined |
repo | str | yes | Repository identifier |
memory_type | str | yes | Always "code" |
docstring | str | if present | First 500 characters of the entity's docstring |
decorators | str | if present | Comma-separated decorator names |
bases | str | if present | Comma-separated base class names |
http_method | str | routes only | HTTP method (GET, POST, etc.) |
http_path | str | routes only | Route path (/health, /memory/{id}) |
commit_hash | str | if provided | Git commit SHA at index time |
indexed_at | str | yes | ISO timestamp of when the entity was indexed |
Relationship Edges
The indexer creates five types of edges between code entity nodes:
| Edge Type | Meaning | Example |
|---|---|---|
DEFINES | A module defines a class/function | parser.py --DEFINES--> CodeParser |
IMPORTS | A file imports a symbol from another module | indexer.py --IMPORTS--> CodeEntity |
CALLS | A function calls another function | index() --CALLS--> parse_file() |
INHERITS | A class extends another class | SqliteStore --INHERITS--> BaseStore |
TESTS | A test function tests a specific entity | test_parse_file() --TESTS--> parse_file() |
Cross-file CALLS and IMPORTS edges are resolved through a two-pass symbol table that maps local import names to their target entity IDs.
Searching Code Entities
Indexed code entities are embedded and appear in standard search() results alongside other memory types:
# Find code related to "authentication middleware"
results = memory.search("authentication middleware", top_k=10)
for item in results:
if item.memory_type == "code":
print(f"{item.entity_type}: {item.name} ({item.file_path}:{item.line_number})")Because code entities share the same vector space as all other memories, a search for "JWT token validation" can return both code entities (the actual implementation) and semantic memories (design decisions, documentation notes) in a single result set.
Incremental Updates
For adding or updating individual code entities without re-indexing an entire repository, use ingest_structured() with the code_entity schema:
memory.ingest_structured(
{
"name": "AuthMiddleware",
"entity_type": "class",
"file_path": "middleware/auth.py",
"line_number": 42,
"repo": "my-service",
"docstring": "JWT authentication middleware for FastAPI.",
"bases": ["BaseHTTPMiddleware"],
},
schema="code_entity",
)This uses the CodeEntityHandler, which applies the INDEXED ingestion strategy with embeddings enabled. It is useful when:
- A CI pipeline detects changed files and needs to update only those entities.
- An external tool (linter, IDE plugin) produces structured code metadata.
- You want to register code entities from languages the parser does not yet support.
Required Fields
The code_entity schema requires: name, entity_type, file_path, line_number, and repo. The entity_type must be one of: module, class, function, route, test.
Languages Supported
| Language | Status | File Extensions | Notes |
|---|---|---|---|
| Python | Full support | .py | AST-based parsing. Extracts classes, functions, FastAPI routes, pytest tests, imports, calls, inheritance. |
| TypeScript | Experimental | .ts, .tsx, .js, .jsx | Basic entity extraction. Pass languages=["python", "typescript"] to enable. |
IndexResult
The ingest_code() method returns an IndexResult dataclass:
@dataclass
class IndexResult:
repo: str
files_parsed: int = 0
files_skipped: int = 0
entities_created: int = 0
edges_created: int = 0
errors: list[str] = field(default_factory=list)
elapsed_seconds: float = 0.0
embeddings_generated: int = 0
commit_hash: str = ""Check result.errors after indexing to see any files that failed to parse (syntax errors, encoding issues). Failed files are skipped without aborting the run.
Pattern Seeding
After indexing, seed_patterns_from_code() runs automatically. It extracts {name: entity_type} pairs from the indexed entities and merges them into the workspace's EntityRuler pattern set.
This means that after you index a codebase, future ingest() calls over natural-language text will recognise references to your code symbols. For example, if you index a project containing class SmartGraph, then ingesting the text "The SmartGraph module handles all FalkorDB queries" will correctly identify SmartGraph as a named entity of type class.