Neko1313/graphlens

GitHub: Neko1313/graphlens

graphlens 是一个多语言代码分析框架,将 Python、TypeScript、Go、Rust 等源码解析为统一的图 IR,用于依赖分析、代码导航和跨语言边界追踪。

Stars: 4 | Forks: 0

graphlens

一个可扩展的多语言代码分析框架,它解析源码项目,将其结构规范化为共享的图 IR,并将其公开以用于依赖分析、导航和代码智能工具。

[![PyPI](https://img.shields.io/pypi/v/graphlens?color=blue)](https://pypi.org/project/graphlens/) [![Python](https://img.shields.io/pypi/pyversions/graphlens)](https://pypi.org/project/graphlens/) [![License](https://img.shields.io/github/license/Neko1313/graphlens)](LICENSE) [![CI](https://img.shields.io/github/actions/workflow/status/Neko1313/graphlens/ci.yml?label=CI)](https://github.com/Neko1313/graphlens/actions) [![codecov](https://codecov.io/gh/Neko1313/graphlens/graph/badge.svg?token=n3oRe180jg)](https://codecov.io/gh/Neko1313/graphlens) [文档](https://Neko1313.github.io/graphlens/) · [代码库](https://github.com/Neko1313/graphlens) · [问题](https://github.com/Neko1313/graphlens/issues)
## 架构 ``` Repository → Language Adapter → GraphLens (IR) → Graph Backend ``` | 层级 | 职责 | |---|---| | **语言适配器** | 解析源文件,生成 `GraphLens` | | **GraphLens** | 类型化节点 + 有向关系(即 IR) | | **图后端** | 持久化或查询图(Neo4j,内存等) | 适配器是**纯粹的数据生产者** —— 它们从不写入任何后端。图是唯一的输出。 ## 为什么使用图 IR? - **语言无关** —— 为 Python、TypeScript、Go、Rust、PHP 等提供统一的共享模型 - **基于插件的适配器** —— 每种语言都是一个独立的包,通过 Python entry points 注册 - **由 tree-sitter 驱动** —— 所有适配器均使用 tree-sitter 进行 CST 解析和获取精确的跨度位置,并结合具备类型感知的解析器(Python 使用 ty,TypeScript 使用 TypeScript Compiler API,Go 使用 gopls,Rust 使用 rust-analyzer,PHP 使用 phpactor) - **具备跨语言感知能力** —— 适配器会生成语言无关的 `BOUNDARY` 端口(HTTP、queues、gRPC、Temporal);`graphlens-link` 能够将一种语言中的消费者与另一种语言中的提供者连接起来 - **支持 Monorepo** —— `can_handle()` 和 `find_*_roots()` 能够正确处理多语言代码库 - **确定性的节点 ID** —— `project::kind::qualified_name` 的 SHA-256 哈希值 → 在多次重新扫描中保持稳定 ## 基准测试 大型真实项目的分析吞吐量,每次发布时自动刷新 —— 在已发布的 Docker 镜像中对每个项目进行一次冷启动运行 (因此这些数据完全反映了用户实际使用的工具链)。参见 [`benchmarks/`](benchmarks/) 以在本地重现或添加项目。 _最后一次运行:**2026-06-22 12:00 UTC** · 镜像 `latest` · 运行环境 `Linux x86_64` · 单次冷启动运行,仅供参考。_ | 项目 | 语言 | Commit | LOC | 文件 | 节点 | 关系 | 耗时 | 峰值 RSS | KLOC/s | 解析器 | 已解析 | |---|---|---|--:|--:|--:|--:|--:|--:|--:|:--|--:| | [apache/superset](https://github.com/apache/superset) | python | `c83fb2b` | 399 519 | 1 886 | 156 252 | 379 813 | 145.6s | 2,057 MB | 2.7 | ok | 84% of 281 667 (79s) | | [colinhacks/zod](https://github.com/colinhacks/zod) | typescript | `1fb56a5` | 74 194 | 404 | 8 741 | 25 258 | 18.6s | 645 MB | 4.0 | ok | 91% of 15 771 (15s) | | [gin-gonic/gin](https://github.com/gin-gonic/gin) | go | `73726dc` | 23 672 | 98 | 7 227 | 11 882 | 13.5s | 2,074 MB | 1.8 | ok | 100% of 8 920 (12s) | | [casdoor/casdoor](https://github.com/casdoor/casdoor) | go | `696bcf0` | 86 898 | 458 | 14 987 | 28 276 | 131.7s | 14,678 MB | 0.7 | ok | 100% of 19 421 (128s) | | [gohugoio/hugo](https://github.com/gohugoio/hugo) | go | `4d22555` | 224 821 | 897 | 34 809 | 72 225 | 109.9s | 9,412 MB | 2.0 | ok | 99% of 49 013 (103s) | | [BurntSushi/ripgrep](https://github.com/BurntSushi/ripgrep) | rust | `4649aa9` | 50 275 | 98 | 5 365 | 15 087 | 18.7s | 1,530 MB | 2.7 | ok | 99% of 11 435 (1s) | | [tokio-rs/axum](https://github.com/tokio-rs/axum) | rust | `c59208c` | 43 653 | 296 | 8 093 | 14 799 | 84.6s | 4,441 MB | 0.5 | ok | 88% of 9 662 (1s) | | [astral-sh/ruff](https://github.com/astral-sh/ruff) | rust | `6686f63` | 687 409 | 1 870 | 69 708 | 217 127 | 249.1s | 8,628 MB | 2.8 | ok | 100% of 155 276 (13s) | | **总计** | | | **1 590 441** | | **305 182** | | **771.5s** | | **2.1** | | **91% of 551 165** | 峰值 RSS 通过 `cgroup.v2` 测量(包含 LSP 解析器子进程在内的整个进程树)。KLOC/s = 每秒分析的千行代码数。由 [`benchmarks/run_benchmarks.py`](benchmarks/run_benchmarks.py) 生成。 ## 文档 完整的产品文档位于 **** (由 [`website/`](website/) 中的 Docusaurus 构建): - [入门指南](https://Neko1313.github.io/graphlens/docs/getting-started/installation) — 安装、快速开始、核心概念 - [进阶指南](https://Neko1313.github.io/graphlens/docs/guides/library-api) — 库 API、CLI、查询、可视化、Neo4j、跨语言、MCP - [CI 集成](https://Neko1313.github.io/graphlens/docs/ci-integration/overview) — 严格模式、GitHub Actions、Docker、本地钩子 - [适配器](https://Neko1313.github.io/graphlens/docs/adapters/overview) — Python、TypeScript、Go、Rust、PHP 以及如何编写自己的适配器 - [图模型](https://Neko1313.github.io/graphlens/docs/graph-model/nodes) — 节点、关系、边界、序列化 - [API 参考](https://Neko1313.github.io/graphlens/docs/api-reference/graphlens) — 精确的函数签名 要在本地运行文档:`cd website && pnpm install && pnpm start`。 ## 安装 ``` # Core library only (models, contracts, registry) pip install graphlens # Core + Python adapter pip install "graphlens[python]" # Core + TypeScript adapter pip install "graphlens[typescript]" # Core + Go / Rust / PHP adapters pip install "graphlens[go]" pip install "graphlens[rust]" pip install "graphlens[php]" # CLI (graphlens analyze / visualize / query / neo4j) pip install "graphlens-cli[python]" # with Python adapter pip install "graphlens-cli[all]" # Python + TS + Go + Rust + PHP + Neo4j ``` 使用 uv: ``` uv add graphlens uv add "graphlens[python]" uv add "graphlens[typescript]" uv add "graphlens-cli[all]" ``` ### Docker(预装所有适配器 + 工具链) 用于 CI 时,发布的镜像捆绑了 CLI 以及每个适配器**和**其解析器所驱动的工具链 (ty、Node、Go + gopls、Rust + rust-analyzer、 PHP + phpactor) —— 无需本地设置,并且是获取 Go、Rust 和 PHP 适配器(未发布到 PyPI)的官方支持方式。将你的项目挂载 到 `/workspace`: ``` docker run --rm -v "$PWD:/workspace" ghcr.io/neko1313/graphlens \ analyze /workspace --output /workspace/graph.json ``` 该镜像会在每次发布时发布到 GitHub Container Registry (包含 `:latest` 以及 `:X.Y.Z` / `:X.Y` 版本标签)。 ## 快速开始 ``` from pathlib import Path from graphlens import adapter_registry # Load and instantiate the Python adapter adapter = adapter_registry.load("python")() # Analyze a project — returns a GraphLens graph = adapter.analyze(Path("./my-project")) print(f"Nodes: {len(graph.nodes)}") print(f"Relations: {len(graph.relations)}") # Inspect nodes by kind from graphlens import NodeKind modules = [n for n in graph.nodes.values() if n.kind == NodeKind.MODULE] classes = [n for n in graph.nodes.values() if n.kind == NodeKind.CLASS] # Check the resolver actually ran (don't trust a silently degraded graph) from graphlens import RESOLVER_STATUS_KEY assert graph.metadata[RESOLVER_STATUS_KEY] == "ok" # Query the graph (indexed lookups, no manual scanning) fn = next(n for n in graph.nodes.values() if n.name == "my_function") callers = graph.callers(fn.id) # who calls it callees = graph.callees(fn.id) # what it calls near = graph.neighbors(fn.id, depth=2) # 2-hop neighbourhood # Serialize for pipelines / agents (round-trippable JSON), then reload text = graph.to_json(indent=2) graph2 = type(graph).from_json(text) # Diff two scans (e.g. before/after a change) diff = old_graph.diff(graph) print(diff.added_nodes, diff.removed_relations, diff.is_empty) ``` ## CLI (`graphlens-cli`) 安装 `graphlens-cli` 以获取 `graphlens` 入口点: ``` # Print node/relation statistics graphlens analyze graphlens analyze ~/myrepo --lang python,typescript,go,rust # Serialize the graph to JSON (CI indexing step); --strict fails on a # degraded resolver so a pipeline never feeds agents an incomplete graph graphlens analyze ~/myrepo --output graph.json graphlens analyze ~/myrepo --format json graphlens analyze ~/myrepo --strict # Query a saved graph (callers | callees | references | neighbors) graphlens query my_function --graph graph.json --op callers graphlens query MyClass.method --graph graph.json --op neighbors --depth 2 # Interactive HTML graph viewer (opens in browser) graphlens visualize graphlens visualize ~/myrepo --lang python --show-external --max-nodes 500 graphlens visualize . --output graph.html --no-open # Export to Neo4j graphlens neo4j --uri bolt://localhost:7687 --user neo4j --password secret graphlens neo4j . --wipe --batch-size 200 # Serve the graph to agents over the Model Context Protocol (needs the # optional `mcp` extra: pip install "graphlens-cli[mcp]") graphlens mcp --graph graph.json ``` ### `mcp` — Model Context Protocol server 将保存的图作为 MCP 工具公开给 LLM 代理:`graph_stats`、 `find_nodes`、`callers`、`callees`、`references`、`neighbors`、 `boundaries` 和 `communicates_with`。使用 `mcp` 额外依赖安装,并 将其指向由 `graphlens analyze --output` 生成的 JSON 图。 ### `visualize` — 交互式 HTML 图查看器 生成一个由 vis.js 驱动的、自包含的 HTML 文件,并在浏览器中打开它。 | 标志 | 描述 | |---|---| | `--lang auto\|python\|typescript\|python,typescript` | 要使用的适配器(默认:自动检测所有) | | `--show-external` | 包含标准库 / 第三方外部符号节点 | | `--show-structure` | 添加 `CONTAINS` / `DECLARES` 结构边 | | `--max-nodes N` | 剪除度数低于 N 的节点(默认:1500) | | `--output PATH` | 将 HTML 写入 PATH,而不是 `graph-.html` | | `--no-open` | 不自动打开浏览器 | **点击行为** —— 点击任意节点以查看其信息面板。对于 `FUNCTION` 和 `METHOD` 节点,面板上有一个 **“显示调用者”** 按钮,可将 图切换为聚焦模式:仅显示选中的节点以及调用或引用它的每一个节点, 调用者列表会显示在侧边栏中。点击空白区域或 **← 返回** 以返回完整图。 ### `neo4j` — 导出到 Neo4j 使用 `UNWIND … MERGE` Cypher(无需 APOC)。每个节点都会获得一个 `:Code` 标签以及一个特定类型的标签(`:Function`、`:ExternalSymbol` 等)。 关系会按类型分组创建。安装可选的 `neo4j` 额外依赖: ``` pip install "graphlens-cli[neo4j]" ``` ## 图模型 ### 节点类型 | 类型 | 描述 | |---|---| | `PROJECT` | 根项目节点 | | `MODULE` | Python/TS/… 模块(目录或文件) | | `FILE` | 源文件 | | `CLASS` | 类声明 | | `FUNCTION` | 顶级函数 | | `METHOD` | 类中的方法 | | `PARAMETER` | 函数/方法参数 | | `VARIABLE` | 模块级或局部变量 | | `ATTRIBUTE` | 类属性 | | `TYPE_ALIAS` | 类型别名声明 | | `IMPORT` | 导入语句 | | `DEPENDENCY` | 声明的包依赖 | | `EXTERNAL_SYMBOL` | 外部符号(标准库、第三方或未知);包含 `metadata["origin"]` | | `BOUNDARY` | 跨语言接口端口(HTTP 路由、队列 topic、gRPC 方法、Temporal activity);共享的 ID 可合并跨语言匹配的服务器/客户端 | ### 关系类型 | 类型 | 描述 | |---|---| | `CONTAINS` | 结构包含(项目 → 模块 → 文件 → 类) | | `DECLARES` | 声明(文件声明函数,类声明方法) | | `IMPORTS` | 导入边(文件 → 导入节点) | | `RESOLVES_TO` | 导入被解析为模块或外部符号 | | `CALLS` | 函数/方法调用(解析为声明节点) | | `REFERENCES` | 值引用(作为值使用的变量/属性) | | `INHERITS_FROM` | 类继承(解析为声明节点) | | `HAS_TYPE` | 类型标注/推断边(函数/参数/变量 → 类或外部) | | `DEPENDS_ON` | 包依赖 | | `EXPOSES` | 服务器/提供者暴露一个 `BOUNDARY`(例如 HTTP 路由处理程序) | | `CONSUMES` | 客户端/消费者消费一个 `BOUNDARY`(例如 HTTP 调用) | | `COMMUNICATES_WITH` | 消费者 → 提供者,由 `graphlens-link` 根据匹配的 `EXPOSES`/`CONSUMES` 添加 | ### 跨语言边界 适配器会为服务暴露或消费的接口生成 `BOUNDARY` 端口 —— HTTP/REST 路由和客户端、消息队列 topic、gRPC 方法 Temporal activity。每个端口都有一个语言无关的 ID (`make_boundary_id(mechanism, key)`),因此 Python FastAPI 路由和 访问相同路径的 TypeScript `fetch` 调用在它们的图被合并时会折叠到 **一个** `BOUNDARY` 节点上。然后,`graphlens-link` 包会将 `CONSUMES` 与 `EXPOSES` 配对成 `COMMUNICATES_WITH` 边: ``` from graphlens_link import link_graph merged = python_graph.merge(ts_graph, allow_shared=True) result = link_graph(merged) # adds COMMUNICATES_WITH edges ``` 有关 Python 服务器 ↔ TypeScript 客户端的详细演示,请参见 `examples/demo_cross_language.py`。 ## 适配器插件系统 语言适配器通过 Python entry points 自行注册 —— 无需更改核心代码: ``` # packages/graphlens-python/pyproject.toml [project.entry-points."graphlens.adapters"] python = "graphlens_python:PythonAdapter" ``` 注册表会在运行时自动发现已安装的适配器: ``` from graphlens import adapter_registry adapter_registry.available() # ["python", ...] adapter_cls = adapter_registry.load("python") adapter = adapter_cls() ``` 适配器也可以手动注册(对测试很有用): ``` adapter_registry.register("python", MyPythonAdapter) ``` ## 实现适配器 继承 `LanguageAdapter` 并实现四个方法: ``` from pathlib import Path from graphlens import GraphLens, LanguageAdapter class MyLangAdapter(LanguageAdapter): def language(self) -> str: return "mylang" def file_extensions(self) -> set[str]: return {".ml", ".mli"} def can_handle(self, project_root: Path) -> bool: return (project_root / "dune-project").exists() def analyze( self, project_root: Path, files: list[Path] | None = None ) -> GraphLens: graph = GraphLens() files = files or self.collect_files(project_root) # ... parse and populate graph ... return graph ``` 在 `pyproject.toml` 中注册,核心注册表就会自动发现它。 ## 项目结构 ``` graphlens/ ← uv workspace root (core library) src/graphlens/ ← models, contracts, registry, exceptions, utils packages/ graphlens-python/ ← Python adapter (tree-sitter + ty) graphlens-typescript/ ← TypeScript adapter (tree-sitter + Compiler API) graphlens-go/ ← Go adapter (tree-sitter + gopls) graphlens-rust/ ← Rust adapter (tree-sitter + rust-analyzer) graphlens-php/ ← PHP adapter (tree-sitter + phpactor) graphlens-link/ ← cross-language linker (COMMUNICATES_WITH) graphlens-cli/ ← CLI (typer): analyze, query, visualize, neo4j, mcp tests/ ← core tests (100% coverage) examples/ ← standalone usage examples ``` ## 开发 需要 Python 3.13+、[uv](https://docs.astral.sh/uv/)、[task](https://taskfile.dev/)。 ``` task install # uv sync --all-groups task lint # ruff + ty + bandit for all packages task tests # all tests with coverage ``` 各个包的任务: ``` task core:lint task core:test task python:lint task python:test task typescript:lint task typescript:test task cli:lint task cli:test ``` ## 许可证 MIT
标签:Neo4j, odt, WebSocket, 云安全监控, 代码分析, 依赖分析, 凭证管理, 可视化界面, 抽象语法树, 日志审计, 请求拦截, 调用图, 逆向工具, 静态分析