From 984c7cfc2e6691480d0c2b104ac6a9dfd478bc5e Mon Sep 17 00:00:00 2001 From: Dvir Dukhan <12258836+DvirDukhan@users.noreply.github.com> Date: Tue, 9 Jun 2026 16:01:57 +0300 Subject: [PATCH] Revert FalkorDBLite backend + MCP context benchmark from #706 PR #706 (`naseem/falkordblite-mcp-latest`) was branched from the wrong base: it sat on top of the #701/#702 MCP work and added three unrelated commits that rode into `staging` through the #702 merge rather than a dedicated, reviewed #706 merge. This reverts only those three accidental commits, leaving all #701/#702 MCP work intact: - 8ec9744 Add MCP context benchmark - 84ee21a Document FalkorDBLite configuration - eefaa40 Add FalkorDBLite backend support No staging code references the reverted api/db.py / FalkorDBLite paths or the removed benchmark; the touched api modules compile cleanly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .env.template | 9 -- README.md | 16 +-- api/cli.py | 12 -- api/db.py | 157 --------------------- api/git_utils/git_graph.py | 18 ++- api/graph.py | 43 ++++-- api/info.py | 19 ++- api/llm.py | 6 +- api/migrations/per_branch.py | 9 +- benchmarks/mcp_context_benchmark.py | 203 ---------------------------- pyproject.toml | 3 - uv.lock | 37 +---- 12 files changed, 75 insertions(+), 457 deletions(-) delete mode 100644 api/db.py delete mode 100644 benchmarks/mcp_context_benchmark.py diff --git a/.env.template b/.env.template index 4d63e8ee..b5047c95 100644 --- a/.env.template +++ b/.env.template @@ -2,15 +2,6 @@ FALKORDB_HOST=localhost FALKORDB_PORT=6379 -# Database backend. Use "lite" to run against an embedded FalkorDBLite -# instance instead of an external FalkorDB host/port. -CODE_GRAPH_DB_BACKEND=falkordb -FALKORDB_LITE_PATH=~/.cache/code-graph/falkordblite.rdb -# Optional: expose FalkorDBLite on localhost for host/port-only integrations -# such as GraphRAG chat. Structural CodeGraph/MCP tools do not need this. -# FALKORDB_LITE_HOST=127.0.0.1 -# FALKORDB_LITE_PORT=6379 - # Optional FalkorDB authentication FALKORDB_USERNAME= FALKORDB_PASSWORD= diff --git a/README.md b/README.md index e2445816..50dbc4ec 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ code-graph/ - Python `>=3.12,<3.14` - Node.js 20+ - [`uv`](https://docs.astral.sh/uv/) -- A FalkorDB instance (local/cloud) or the optional FalkorDBLite backend +- A FalkorDB instance (local or cloud) ### 1. Start FalkorDB @@ -68,16 +68,6 @@ code-graph/ docker run -p 6379:6379 -it --rm falkordb/falkordb ``` -**Option C:** Use embedded FalkorDBLite: - -```bash -uv sync --extra light -export CODE_GRAPH_DB_BACKEND=lite -export FALKORDB_LITE_PATH=~/.cache/code-graph/falkordblite.rdb -``` - -FalkorDBLite runs a local embedded server over a private Unix socket by default. Set `FALKORDB_LITE_PORT` only when a host/port-only integration, such as GraphRAG chat, must connect to the embedded database. - ### 2. Configure environment variables Copy the template and adjust it for your setup: @@ -88,14 +78,10 @@ cp .env.template .env | Variable | Description | Required | Default | |----------|-------------|----------|---------| -| `CODE_GRAPH_DB_BACKEND` | Database backend: `falkordb` or `lite` | No | `falkordb` | | `FALKORDB_HOST` | FalkorDB hostname | No | `localhost` | | `FALKORDB_PORT` | FalkorDB port | No | `6379` | | `FALKORDB_USERNAME` | Optional FalkorDB username | No | empty | | `FALKORDB_PASSWORD` | Optional FalkorDB password | No | empty | -| `FALKORDB_LITE_PATH` | FalkorDBLite database file path | No | `~/.cache/code-graph/falkordblite.rdb` | -| `FALKORDB_LITE_HOST` | Host used when exposing FalkorDBLite over TCP | No | `127.0.0.1` | -| `FALKORDB_LITE_PORT` | Optional TCP port for FalkorDBLite host/port clients | No | empty | | `SECRET_TOKEN` | Token checked by protected endpoints | No | empty | | `CODE_GRAPH_PUBLIC` | Set `1` to skip auth on read-only endpoints | No | `0` | | `ALLOWED_ANALYSIS_DIR` | Root path allowed for `/api/analyze_folder` | No | repository root | diff --git a/api/cli.py b/api/cli.py index 28b1333b..bf9ed4d6 100644 --- a/api/cli.py +++ b/api/cli.py @@ -62,18 +62,6 @@ def _check_connection(host: str, port: int) -> bool: def ensure_db() -> None: """Ensure FalkorDB is running, auto-starting a Docker container if needed.""" - from .db import create_falkordb, is_lite_backend - - if is_lite_backend(): - try: - db = create_falkordb() - db.connection.ping() - except Exception as e: - _json_error(f"Failed to initialize FalkorDBLite: {e}") - _stderr("FalkorDBLite embedded backend is ready") - _json_out({"status": "ok", "backend": "lite"}) - return - host = os.getenv("FALKORDB_HOST", "localhost") try: port = int(os.getenv("FALKORDB_PORT", "6379")) diff --git a/api/db.py b/api/db.py deleted file mode 100644 index 8729b097..00000000 --- a/api/db.py +++ /dev/null @@ -1,157 +0,0 @@ -"""Database backend selection for CodeGraph. - -The default backend is a regular FalkorDB server addressed by host/port. -Set ``CODE_GRAPH_DB_BACKEND=lite`` to use FalkorDBLite's embedded server. -""" - -from __future__ import annotations - -import os -from pathlib import Path -from typing import Any - - -LITE_BACKENDS = {"lite", "falkordblite"} -DEFAULT_LITE_PATH = "~/.cache/code-graph/falkordblite.rdb" - - -def is_lite_backend() -> bool: - """Return whether CodeGraph should use the embedded FalkorDBLite backend.""" - backend = os.getenv("CODE_GRAPH_DB_BACKEND", "falkordb").strip().lower() - return backend in LITE_BACKENDS - - -def _lite_db_path() -> str: - path = Path(os.getenv("FALKORDB_LITE_PATH", DEFAULT_LITE_PATH)).expanduser() - path.parent.mkdir(parents=True, exist_ok=True) - return str(path) - - -def _lite_serverconfig() -> dict[str, str]: - """Return FalkorDBLite server config from env. - - FalkorDBLite defaults to a private Unix socket. Supplying - ``FALKORDB_LITE_PORT`` additionally exposes a local TCP port, which is - useful for libraries that only accept host/port connection settings. - """ - port = os.getenv("FALKORDB_LITE_PORT") - if not port: - return {} - return { - "bind": os.getenv("FALKORDB_LITE_HOST", "127.0.0.1"), - "port": port, - } - - -def create_falkordb() -> Any: - """Create a sync FalkorDB client for the configured backend.""" - if is_lite_backend(): - try: - from redislite.falkordb_client import FalkorDB as LiteFalkorDB - except ImportError as e: - raise RuntimeError( - "CODE_GRAPH_DB_BACKEND=lite requires the optional " - "`falkordblite` dependency. Install with " - "`uv sync --extra light` or `pip install 'falkordb-code-graph[light]'`." - ) from e - - return LiteFalkorDB(_lite_db_path(), serverconfig=_lite_serverconfig()) - - from falkordb import FalkorDB - - return FalkorDB( - host=os.getenv("FALKORDB_HOST", "localhost"), - port=os.getenv("FALKORDB_PORT", 6379), - username=os.getenv("FALKORDB_USERNAME", None), - password=os.getenv("FALKORDB_PASSWORD", None), - ) - - -def create_async_falkordb() -> Any: - """Create an async FalkorDB client for the configured backend.""" - if is_lite_backend(): - try: - from redislite.async_falkordb_client import AsyncFalkorDB as LiteAsyncFalkorDB - except ImportError as e: - raise RuntimeError( - "CODE_GRAPH_DB_BACKEND=lite requires the optional " - "`falkordblite` dependency. Install with " - "`uv sync --extra light` or `pip install 'falkordb-code-graph[light]'`." - ) from e - - client = LiteAsyncFalkorDB(_lite_db_path(), serverconfig=_lite_serverconfig()) - if not hasattr(client, "aclose"): - client.aclose = client.close - return client - - from falkordb.asyncio import FalkorDB as AsyncFalkorDB - - return AsyncFalkorDB( - host=os.getenv("FALKORDB_HOST", "localhost"), - port=int(os.getenv("FALKORDB_PORT", 6379)), - username=os.getenv("FALKORDB_USERNAME", None), - password=os.getenv("FALKORDB_PASSWORD", None), - ) - - -def create_redis_connection() -> Any: - """Create a sync Redis-compatible connection for metadata operations.""" - if is_lite_backend(): - return create_falkordb().connection - - import redis - - return redis.Redis( - host=os.getenv("FALKORDB_HOST", "localhost"), - port=int(os.getenv("FALKORDB_PORT", "6379")), - username=os.getenv("FALKORDB_USERNAME"), - password=os.getenv("FALKORDB_PASSWORD"), - decode_responses=True, - ) - - -def create_async_redis_connection() -> Any: - """Create an async Redis-compatible connection for metadata operations.""" - if is_lite_backend(): - return create_async_falkordb().connection - - import redis.asyncio as aioredis - - return aioredis.Redis( - host=os.getenv("FALKORDB_HOST", "localhost"), - port=int(os.getenv("FALKORDB_PORT", "6379")), - username=os.getenv("FALKORDB_USERNAME"), - password=os.getenv("FALKORDB_PASSWORD"), - decode_responses=True, - ) - - -def graphrag_connection_kwargs() -> dict[str, Any]: - """Return host/port kwargs for GraphRAG SDK constructors. - - GraphRAG SDK accepts host/port only. FalkorDBLite's private Unix socket is - therefore usable for structural tools but not for GraphRAG unless a local - TCP port is explicitly enabled with ``FALKORDB_LITE_PORT``. - """ - if is_lite_backend(): - port = os.getenv("FALKORDB_LITE_PORT") - if not port: - raise RuntimeError( - "GraphRAG requires host/port access. When using " - "CODE_GRAPH_DB_BACKEND=lite, set FALKORDB_LITE_PORT to expose " - "the embedded FalkorDBLite instance on localhost." - ) - create_falkordb() - return { - "host": os.getenv("FALKORDB_LITE_HOST", "127.0.0.1"), - "port": int(port), - "username": None, - "password": None, - } - - return { - "host": os.getenv("FALKORDB_HOST", "localhost"), - "port": int(os.getenv("FALKORDB_PORT", 6379)), - "username": os.getenv("FALKORDB_USERNAME", None), - "password": os.getenv("FALKORDB_PASSWORD", None), - } diff --git a/api/git_utils/git_graph.py b/api/git_utils/git_graph.py index ec41eec0..52de8da9 100644 --- a/api/git_utils/git_graph.py +++ b/api/git_utils/git_graph.py @@ -1,8 +1,9 @@ +import os import logging -from falkordb import Node +from falkordb import FalkorDB, Node +from falkordb.asyncio import FalkorDB as AsyncFalkorDB from typing import List, Optional -from api.db import create_async_falkordb, create_falkordb from pygit2 import Commit # Configure logging @@ -18,7 +19,10 @@ class GitGraph(): def __init__(self, name: str): - self.db = create_falkordb() + self.db = FalkorDB(host=os.getenv('FALKORDB_HOST', 'localhost'), + port=os.getenv('FALKORDB_PORT', 6379), + username=os.getenv('FALKORDB_USERNAME', None), + password=os.getenv('FALKORDB_PASSWORD', None)) self.g = self.db.select_graph(name) @@ -178,7 +182,12 @@ class AsyncGitGraph: """Async read-only git graph for endpoint use.""" def __init__(self, name: str): - self.db = create_async_falkordb() + self.db = AsyncFalkorDB( + host=os.getenv('FALKORDB_HOST', 'localhost'), + port=int(os.getenv('FALKORDB_PORT', 6379)), + username=os.getenv('FALKORDB_USERNAME', None), + password=os.getenv('FALKORDB_PASSWORD', None), + ) self.g = self.db.select_graph(name) def _commit_from_node(self, node: Node) -> dict: @@ -196,3 +205,4 @@ async def list_commits(self) -> List[dict]: async def close(self) -> None: await self.db.aclose() + diff --git a/api/graph.py b/api/graph.py index ffa208fb..7d14f957 100644 --- a/api/graph.py +++ b/api/graph.py @@ -1,9 +1,10 @@ +import os import re import time +from .entities import * from typing import Optional -from falkordb import Path, Node, QueryResult -from .db import create_async_falkordb, create_falkordb -from .entities import File, encode_edge, encode_node +from falkordb import FalkorDB, Path, Node, QueryResult +from falkordb.asyncio import FalkorDB as AsyncFalkorDB # Configure the logger import logging @@ -61,7 +62,10 @@ def parse_graph_name(graph_name: str) -> Optional[tuple[str, str]]: def graph_exists(name: str): - db = create_falkordb() + db = FalkorDB(host=os.getenv('FALKORDB_HOST', 'localhost'), + port=os.getenv('FALKORDB_PORT', 6379), + username=os.getenv('FALKORDB_USERNAME', None), + password=os.getenv('FALKORDB_PASSWORD', None)) return name in db.list_graphs() @@ -82,7 +86,10 @@ def get_repos() -> list[dict]: single graph until the migration is run. """ - db = create_falkordb() + db = FalkorDB(host=os.getenv('FALKORDB_HOST', 'localhost'), + port=os.getenv('FALKORDB_PORT', 6379), + username=os.getenv('FALKORDB_USERNAME', None), + password=os.getenv('FALKORDB_PASSWORD', None)) repos = [] for g in db.list_graphs(): @@ -133,7 +140,10 @@ def __init__(self, name: str, branch: Optional[str] = None) -> None: self.branch = branch or DEFAULT_BRANCH self.name = compose_graph_name(self.project, self.branch) - self.db = create_falkordb() + self.db = FalkorDB(host=os.getenv('FALKORDB_HOST', 'localhost'), + port=os.getenv('FALKORDB_PORT', 6379), + username=os.getenv('FALKORDB_USERNAME', None), + password=os.getenv('FALKORDB_PASSWORD', None)) self.g = self.db.select_graph(self.name) # Initialize the backlog as disabled by default @@ -170,7 +180,10 @@ def from_raw_name(cls, raw_name: str) -> "Graph": obj.branch = DEFAULT_BRANCH else: obj.project, obj.branch = parsed - obj.db = create_falkordb() + obj.db = FalkorDB(host=os.getenv('FALKORDB_HOST', 'localhost'), + port=os.getenv('FALKORDB_PORT', 6379), + username=os.getenv('FALKORDB_USERNAME', None), + password=os.getenv('FALKORDB_PASSWORD', None)) obj.g = obj.db.select_graph(raw_name) obj.backlog = None return obj @@ -284,7 +297,7 @@ def _query(self, q: str, params: Optional[dict] = None) -> QueryResult: return result_set - def get_sub_graph(self, limit: int) -> dict: + def get_sub_graph(self, l: int) -> dict: q = """MATCH (src) OPTIONAL MATCH (src)-[e]->(dest) @@ -293,7 +306,7 @@ def get_sub_graph(self, limit: int) -> dict: sub_graph = {'nodes': [], 'edges': [] } - result_set = self._query(q, {'limit': limit}).result_set + result_set = self._query(q, {'limit': l}).result_set for row in result_set: src = row[0] e = row[1] @@ -579,7 +592,7 @@ def set_file_coverage(self, path: str, name: str, ext: str, coverage: float) -> params = {'path': path, 'name': name, 'ext': ext, 'coverage': coverage} - self._query(q, params) + res = self._query(q, params) def connect_entities(self, relation: str, src_id: int, dest_id: int, properties: dict = {}) -> None: """ @@ -776,9 +789,14 @@ def unreachable_entities(self, lbl: Optional[str], rel: Optional[str]) -> list[d # Async helpers and read-only async graph wrapper # --------------------------------------------------------------------------- -def _async_db(): +def _async_db() -> AsyncFalkorDB: """Create an async FalkorDB connection using environment config.""" - return create_async_falkordb() + return AsyncFalkorDB( + host=os.getenv('FALKORDB_HOST', 'localhost'), + port=int(os.getenv('FALKORDB_PORT', 6379)), + username=os.getenv('FALKORDB_USERNAME', None), + password=os.getenv('FALKORDB_PASSWORD', None), + ) async def async_graph_exists(name: str) -> bool: @@ -934,3 +952,4 @@ async def stats(self) -> dict: async def close(self) -> None: await self.db.aclose() + diff --git a/api/info.py b/api/info.py index 75e5ef28..f2173fda 100644 --- a/api/info.py +++ b/api/info.py @@ -1,9 +1,9 @@ +import os import redis import redis.asyncio as aioredis import logging from typing import Optional, Dict -from .db import create_async_redis_connection, create_redis_connection from .graph import DEFAULT_BRANCH # Configure logging @@ -39,7 +39,13 @@ def get_redis_connection() -> redis.Redis: redis.Redis: A Redis connection object. """ try: - return create_redis_connection() + return redis.Redis( + host = os.getenv('FALKORDB_HOST', "localhost"), + port = int(os.getenv('FALKORDB_PORT', "6379")), + username = os.getenv('FALKORDB_USERNAME'), + password = os.getenv('FALKORDB_PASSWORD'), + decode_responses = True # To ensure string responses + ) except Exception as e: logging.error(f"Error connecting to Redis: {e}") raise @@ -154,7 +160,13 @@ def get_repo_info(repo_name: str, branch: Optional[str] = None) -> Optional[Dict # --------------------------------------------------------------------------- async def async_get_redis_connection() -> aioredis.Redis: - return create_async_redis_connection() + return aioredis.Redis( + host=os.getenv('FALKORDB_HOST', "localhost"), + port=int(os.getenv('FALKORDB_PORT', "6379")), + username=os.getenv('FALKORDB_USERNAME'), + password=os.getenv('FALKORDB_PASSWORD'), + decode_responses=True, + ) async def async_get_repo_info(repo_name: str, branch: Optional[str] = None) -> Optional[Dict[str, str]]: @@ -175,3 +187,4 @@ async def async_get_repo_info(repo_name: str, branch: Optional[str] = None) -> O except Exception as e: logging.error(f"Error retrieving repo info for '{repo_name}': {e}") raise + diff --git a/api/llm.py b/api/llm.py index 0e64188a..25bb5d3d 100644 --- a/api/llm.py +++ b/api/llm.py @@ -3,7 +3,6 @@ import logging from typing import Optional -from .db import graphrag_connection_kwargs from graphrag_sdk.models.litellm import LiteModel from graphrag_sdk import ( Ontology, @@ -256,7 +255,10 @@ def _create_kg_agent(repo_name: str, branch: Optional[str] = None): name=graph_name, ontology=ontology, model_config=KnowledgeGraphModelConfig.with_model(model), - **graphrag_connection_kwargs(), + host=os.getenv('FALKORDB_HOST', 'localhost'), + port=os.getenv('FALKORDB_PORT', 6379), + username=os.getenv('FALKORDB_USERNAME', None), + password=os.getenv('FALKORDB_PASSWORD', None), cypher_system_instruction=CYPHER_GEN_SYSTEM, qa_system_instruction=GRAPH_QA_SYSTEM, cypher_gen_prompt=CYPHER_GEN_PROMPT, diff --git a/api/migrations/per_branch.py b/api/migrations/per_branch.py index d70ee5b0..b263369e 100644 --- a/api/migrations/per_branch.py +++ b/api/migrations/per_branch.py @@ -20,11 +20,11 @@ from __future__ import annotations import logging +import os from typing import Iterable from falkordb import FalkorDB -from ..db import create_falkordb from ..graph import ( DEFAULT_BRANCH, compose_graph_name, @@ -37,7 +37,12 @@ def _connect() -> FalkorDB: - return create_falkordb() + return FalkorDB( + host=os.getenv("FALKORDB_HOST", "localhost"), + port=os.getenv("FALKORDB_PORT", 6379), + username=os.getenv("FALKORDB_USERNAME", None), + password=os.getenv("FALKORDB_PASSWORD", None), + ) def _legacy_graphs(all_graphs: Iterable[str]) -> list[str]: diff --git a/benchmarks/mcp_context_benchmark.py b/benchmarks/mcp_context_benchmark.py deleted file mode 100644 index e04e33b2..00000000 --- a/benchmarks/mcp_context_benchmark.py +++ /dev/null @@ -1,203 +0,0 @@ -"""Estimate context/token savings from CodeGraph MCP-style lookups. - -This benchmark is intentionally deterministic: it does not call an LLM and -does not require provider API keys. It compares the approximate context size an -agent would send when answering 100 code-navigation questions in two modes: - -* graph: compact structured context from CodeGraph MCP tools -* plain: raw source-file context from the likely file an agent would inspect - -Run after indexing the repo/folder, for example: - - CODE_GRAPH_DB_BACKEND=lite FALKORDB_LITE_PATH=/tmp/code-graph-lite.rdb \ - uv run --extra light cgraph index api --repo code-graph-api --branch local-test - - CODE_GRAPH_DB_BACKEND=lite FALKORDB_LITE_PATH=/tmp/code-graph-lite.rdb \ - uv run --extra light python benchmarks/mcp_context_benchmark.py \ - --project code-graph-api --branch local-test --root api -""" - -from __future__ import annotations - -import argparse -import asyncio -import json -import logging -from dataclasses import dataclass -from pathlib import Path -from typing import Any - -logging.disable(logging.CRITICAL) - -from api.mcp.tools.structural import find_symbol, get_file_neighbors, get_neighbors # noqa: E402 - - -@dataclass(frozen=True) -class Target: - subject: str - symbol: str - file: str - - -@dataclass(frozen=True) -class Question: - text: str - target: Target - - -TARGETS = [ - Target("Graph class ownership and methods", "Graph", "api/graph.py"), - Target("AsyncGraphQuery neighbor reads", "AsyncGraphQuery", "api/graph.py"), - Target("regular-vs-Lite backend selection", "create_falkordb", "api/db.py"), - Target("GraphRAG host/port configuration", "graphrag_connection_kwargs", "api/db.py"), - Target("MCP repository indexing", "index_repo", "api/mcp/tools/structural.py"), - Target("symbol resolution before traversal", "find_symbol", "api/mcp/tools/structural.py"), - Target("single-hop graph traversal", "get_neighbors", "api/mcp/tools/structural.py"), - Target("project source analysis orchestration", "Project", "api/project.py"), - Target("local folder analyzer pipeline", "SourceAnalyzer", "api/analyzers/source_analyzer.py"), - Target("C# analyzer restore behavior", "CSharpAnalyzer", "api/analyzers/csharp/analyzer.py"), -] - -QUESTION_TEMPLATES = [ - "What does {subject} do?", - "Which symbols are directly related to {subject}?", - "What would likely be impacted if {subject} changed?", - "Which callers or incoming relationships does {subject} have?", - "Which dependencies or outgoing relationships does {subject} have?", - "Where is {subject} defined and what is its local context?", - "What methods or child symbols does {subject} define?", - "How should an agent navigate from {subject} to related code?", - "What source file should be inspected for {subject}?", - "Summarize {subject} using only relevant structural context.", -] - -QUESTIONS = [ - Question(template.format(subject=target.subject), target) - for target in TARGETS - for template in QUESTION_TEMPLATES -] - - -def estimate_tokens(text: str) -> int: - """Cheap token approximation for relative comparisons.""" - return max(1, len(text) // 4) - - -def compact_json(data: Any) -> str: - return json.dumps(data, ensure_ascii=False, sort_keys=True, default=str) - - -def read_plain_context(root: Path, file_hint: str) -> str: - path = root.parent / file_hint if file_hint.startswith(f"{root.name}/") else root / file_hint - if not path.exists(): - path = root.parent / file_hint - return path.read_text(errors="replace") - - -async def graph_context(question: Question, project: str, branch: str | None) -> dict[str, Any]: - symbols = await find_symbol( - name=question.target.symbol, - project=project, - branch=branch, - file=question.target.file, - limit=3, - ) - payload: dict[str, Any] = {"symbols": symbols} - if not symbols: - return payload - - symbol_id = symbols[0]["symbol_id"] - payload["defines_out"] = await get_neighbors( - symbol_id=symbol_id, - project=project, - branch=branch, - relation="DEFINES", - direction="OUT", - limit=30, - ) - payload["calls_in"] = await get_neighbors( - symbol_id=symbol_id, - project=project, - branch=branch, - relation="CALLS", - direction="IN", - limit=15, - ) - payload["calls_out"] = await get_neighbors( - symbol_id=symbol_id, - project=project, - branch=branch, - relation="CALLS", - direction="OUT", - limit=15, - ) - payload["file_neighbors"] = await get_file_neighbors( - file=question.target.file, - project=project, - branch=branch, - limit=20, - ) - return payload - - -async def run(project: str, branch: str | None, root: Path) -> None: - rows = [] - graph_total = 0 - plain_total = 0 - graph_contexts: dict[Target, str] = {} - - for question in QUESTIONS: - if question.target not in graph_contexts: - graph_contexts[question.target] = compact_json( - await graph_context(question, project, branch) - ) - graph_text = graph_contexts[question.target] - plain_text = read_plain_context(root, question.target.file) - graph_tokens = estimate_tokens(graph_text) - plain_tokens = estimate_tokens(plain_text) - graph_total += graph_tokens - plain_total += plain_tokens - rows.append( - { - "question": question.text, - "symbol": question.target.symbol, - "file": question.target.file, - "graph_tokens": graph_tokens, - "plain_tokens": plain_tokens, - "delta_tokens": plain_tokens - graph_tokens, - "reduction_pct": round((1 - graph_tokens / plain_tokens) * 100, 1) - if plain_tokens - else 0, - } - ) - - summary = { - "project": project, - "branch": branch, - "questions": len(QUESTIONS), - "graph_tokens": graph_total, - "plain_tokens": plain_total, - "delta_tokens": plain_total - graph_total, - "reduction_pct": round((1 - graph_total / plain_total) * 100, 1) - if plain_total - else 0, - } - print(json.dumps({"summary": summary, "rows": rows}, indent=2)) - - -def main() -> None: - parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument("--project", required=True, help="Indexed CodeGraph project name") - parser.add_argument("--branch", default=None, help="Indexed branch name") - parser.add_argument( - "--root", - type=Path, - default=Path("api"), - help="Source root used for the plain-context baseline", - ) - args = parser.parse_args() - asyncio.run(run(args.project, args.branch, args.root.resolve())) - - -if __name__ == "__main__": - main() diff --git a/pyproject.toml b/pyproject.toml index a41f999f..ad8d120a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,9 +31,6 @@ cgraph = "api.cli:app" cgraph-mcp = "api.mcp.server:main" [project.optional-dependencies] -light = [ - "falkordblite>=0.10.0,<1.0.0", -] test = [ "pytest>=9.0.2,<10.0.0", "ruff>=0.11.0,<1.0.0", diff --git a/uv.lock b/uv.lock index d9a2d15a..de4f5a05 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 2 +revision = 3 requires-python = ">=3.12, <3.14" [[package]] @@ -372,9 +372,6 @@ dependencies = [ ] [package.optional-dependencies] -light = [ - { name = "falkordblite" }, -] test = [ { name = "anyio" }, { name = "httpx" }, @@ -394,7 +391,6 @@ requires-dist = [ { name = "anyio", marker = "extra == 'test'", specifier = ">=4.0,<5.0" }, { name = "falkordb", specifier = ">=1.1.3,<2.0.0" }, { name = "falkordb-multilspy", specifier = ">=0.1.0,<1.0.0" }, - { name = "falkordblite", marker = "extra == 'light'", specifier = ">=0.10.0,<1.0.0" }, { name = "fastapi", specifier = ">=0.115.0,<1.0.0" }, { name = "graphrag-sdk", specifier = ">=0.8.1,<0.9.0" }, { name = "httpx", marker = "extra == 'test'", specifier = ">=0.28.0,<1.0.0" }, @@ -415,7 +411,7 @@ requires-dist = [ { name = "uvicorn", extras = ["standard"], specifier = ">=0.34.0,<1.0.0" }, { name = "validators", specifier = ">=0.35.0,<0.36.0" }, ] -provides-extras = ["light", "test"] +provides-extras = ["test"] [package.metadata.requires-dev] dev = [ @@ -439,26 +435,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/84/15/97032c229a031b29795c2d0a74646bc24b1bdbb71930c44d1d37ef9d98c6/falkordb_multilspy-0.1.0-py3-none-any.whl", hash = "sha256:3429f11a83c4fbf06c2b8f0078b8a257e0870da9d50ebd964c6de2ad33164f57", size = 129555, upload-time = "2026-03-23T14:29:32.936Z" }, ] -[[package]] -name = "falkordblite" -version = "0.10.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "falkordb" }, - { name = "psutil" }, - { name = "redis" }, - { name = "setuptools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/70/03/afbb6e0f03c302aa9b64a38e1a2c43664f86921f0dcbf03ec9e31da06ac6/falkordblite-0.10.0.tar.gz", hash = "sha256:65a72abafd30711f699c15571df6959edb8901605053ce940ccdd837832e709b", size = 23675620, upload-time = "2026-05-02T13:12:29.429Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/43/39d8cf13964784447676d24f0cefa3bdc99c10e647c71e6a4172d302dcac/falkordblite-0.10.0-cp312-cp312-macosx_10_13_x86_64.macosx_15_0_arm64.whl", hash = "sha256:741fda166170513db1815d5369870e47d44da2e9b85320fddbc50c88a7338d51", size = 17752268, upload-time = "2026-05-02T13:11:57.906Z" }, - { url = "https://files.pythonhosted.org/packages/61/17/3ec180ca7c2e79a0c3e9912ac04b446e733745cd944845fe4306be016efd/falkordblite-0.10.0-cp312-cp312-manylinux_2_39_aarch64.whl", hash = "sha256:bfe1f47ae03decdc0ad111ec82606bac82ee6cdd7fea04cbcff1283608cc2d2e", size = 34579293, upload-time = "2026-05-02T13:12:01.601Z" }, - { url = "https://files.pythonhosted.org/packages/8a/5e/d343c5249bd24c6614e8c1b718a73bb9f68beb2cf90464bc51c9436d25af/falkordblite-0.10.0-cp312-cp312-manylinux_2_39_x86_64.whl", hash = "sha256:ee9659bd0c7cdf0c2532977f2ec8bf1d1ab01cb3b6776e50c67046481f3162c7", size = 36154066, upload-time = "2026-05-02T13:12:05.435Z" }, - { url = "https://files.pythonhosted.org/packages/f6/1e/18612dc9e75e5c02a281a49d97b56b5504eb5907f971c68e8a5801033f36/falkordblite-0.10.0-cp313-cp313-macosx_10_13_x86_64.macosx_15_0_arm64.whl", hash = "sha256:54a051cbc0bb25b30e65f46ddb1b63149a80bb7004c97615d39c491d492a6d9b", size = 17752240, upload-time = "2026-05-02T13:12:08.837Z" }, - { url = "https://files.pythonhosted.org/packages/29/b3/63f9b1168c4d1abbee4418a0a165496f57cd4b93a261cb481272ce353890/falkordblite-0.10.0-cp313-cp313-manylinux_2_39_aarch64.whl", hash = "sha256:ce1bd408ebaafc3dbdf39e860888382aae69de2b3c949dadfd132779130b99b2", size = 34579294, upload-time = "2026-05-02T13:12:12.069Z" }, - { url = "https://files.pythonhosted.org/packages/2e/cf/d792cf46292f7756f96727916e77ebf9821371e22bff65ed8e90db6b80b9/falkordblite-0.10.0-cp313-cp313-manylinux_2_39_x86_64.whl", hash = "sha256:7d52a3665f6de30cbffc5809a1c6d722492c95bf8dae4a7ee108ca58da939b25", size = 36154069, upload-time = "2026-05-02T13:12:15.742Z" }, -] - [[package]] name = "fastapi" version = "0.135.1" @@ -1639,15 +1615,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fe/4e/cd76eca6db6115604b7626668e891c9dd03330384082e33662fb0f113614/ruff-0.15.5-py3-none-win_arm64.whl", hash = "sha256:b498d1c60d2fe5c10c45ec3f698901065772730b411f164ae270bb6bfcc4740b", size = 10965572, upload-time = "2026-03-05T20:06:16.984Z" }, ] -[[package]] -name = "setuptools" -version = "82.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4f/db/cfac1baf10650ab4d1c111714410d2fbb77ac5a616db26775db562c8fab2/setuptools-82.0.1.tar.gz", hash = "sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9", size = 1152316, upload-time = "2026-03-09T12:47:17.221Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl", hash = "sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb", size = 1006223, upload-time = "2026-03-09T12:47:15.026Z" }, -] - [[package]] name = "shellingham" version = "1.5.4"