LangChain integration for Apache AGE (graph) + pgvector (vector) on PostgreSQL
Project description
langchain-age
LangChain integration for Apache AGE (graph) + pgvector (vector) on PostgreSQL.
Drop-in replacement for langchain-neo4j — same API, runs on PostgreSQL instead of Neo4j.
from langchain_age import AGEGraph, AGEVector, AGEGraphCypherQAChain
v0.1.1 — Initial stable release. The public API (
AGEGraph,AGEVector,AGEGraphCypherQAChain) is considered stable. Breaking changes in 0.1.x will be avoided where possible.
Tested with
- Python 3.10–3.14
- PostgreSQL 18 + Apache AGE 1.7.0 + pgvector 0.8.2
Known limitations
- AGE does not support parameterised Cypher (
$param) —langchain-ageprovidesmogrify-based safe value escaping as a workaround - The
[graph]extra installsapache-age-pythonfrom GitHub (the PyPI version is outdated and psycopg2-based) - Async methods use
run_in_executorwrapping (not nativepsycopg.AsyncConnectionyet)
Installation
Three install modes depending on what you need:
# Vector only (pgvector)
pip install "langchain-age[vector]"
# Graph + Vector (everything)
pip install "langchain-age[all]"
pip install "apache-age-python @ git+https://github.com/apache/age.git#subdirectory=drivers/python"
Note: The Apache AGE Python driver must be installed separately from GitHub. The PyPI version (0.0.7) is outdated and uses psycopg2. The GitHub version uses psycopg3. This is the official SDK maintained by the Apache AGE project.
Quick Start
1. Start the database
cd docker
docker compose up -d
Single container: PostgreSQL 18 + Apache AGE 1.7.0 + pgvector.
2. Graph mode
from langchain_age import AGEGraph
graph = AGEGraph(
"host=localhost port=5433 dbname=langchain_age user=langchain password=langchain",
graph_name="my_graph",
)
graph.query("CREATE (:Person {name: 'Alice', age: 30})")
results = graph.query("MATCH (n:Person) RETURN n.name AS name")
# [{'name': 'Alice'}]
3. Vector mode
from langchain_age import AGEVector, DistanceStrategy
store = AGEVector(
connection_string="host=localhost port=5433 dbname=langchain_age user=langchain password=langchain",
embedding_function=my_embeddings,
collection_name="docs",
distance_strategy=DistanceStrategy.COSINE,
)
store.add_texts(["Apache AGE adds Cypher to PostgreSQL."])
results = store.similarity_search("graph database", k=5)
4. Graph + Vector (GraphRAG)
# Vectorise existing graph nodes
store = AGEVector.from_existing_graph(
embedding=my_embeddings,
connection_string="...",
graph_name="my_graph",
node_label="Document",
text_node_properties=["title", "content"],
)
5. Cypher QA Chain
from langchain_age import AGEGraph, AGEGraphCypherQAChain
from langchain_openai import ChatOpenAI
chain = AGEGraphCypherQAChain.from_llm(
ChatOpenAI(model="gpt-4o-mini"),
graph=AGEGraph("...", "movies"),
allow_dangerous_requests=True,
)
answer = chain.run("Which movies did Tom Hanks act in?")
6. Long-term Memory & Checkpointing
Uses the same PostgreSQL instance via langgraph-checkpoint-postgres:
from langgraph.store.postgres import PostgresStore
with PostgresStore.from_conn_string("host=localhost port=5433 ...") as store:
store.setup()
store.put(("users", "123"), "prefs", {"theme": "dark"})
AGE graph tables, pgvector tables, and LangGraph store tables coexist in the same database.
Features
| Component | Class | Description |
|---|---|---|
| Graph | AGEGraph |
GraphStore backed by Apache AGE. Cypher execution, schema introspection, GraphDocument upserts. |
| Vector | AGEVector |
VectorStore backed by pgvector. Cosine/L2/IP distance, HNSW & IVFFlat indexes, hybrid search (vector + full-text via RRF), MMR. |
| Chain | AGEGraphCypherQAChain |
LLM generates Cypher, executes against AGE, returns natural-language answer. |
Security
- SQL identifier validation at construction (
validate_sql_identifier) - Cypher identifier backtick-quoting for all 25 AGE reserved words (
escape_cypher_identifier) - OpenCypher-standard string escaping with
''doubling (escape_cypher_string) allow_dangerous_requestsgate on the QA chain- Double-quoted SQL table/index names throughout
langchain-neo4j API Compatibility
| Feature | langchain-neo4j | langchain-age |
|---|---|---|
from_existing_graph() |
Neo4jVector.from_existing_graph() |
AGEVector.from_existing_graph() |
from_existing_index() |
Neo4jVector.from_existing_index() |
AGEVector.from_existing_index() |
similarity_search_with_relevance_scores() |
Yes | Yes |
as_retriever() |
Yes | Yes |
| Hybrid search | Lucene full-text | PostgreSQL tsvector + RRF |
include_types / exclude_types |
Yes | Yes |
add_graph_documents() |
Yes | Yes |
| Context manager | Yes | Yes |
| Batch insert | UNWIND ... IN TRANSACTIONS OF 1000 ROWS |
executemany with batch_size=1000 |
AGE vs Neo4j
| Neo4j | Apache AGE | |
|---|---|---|
| Cypher execution | Bolt protocol | SQL-wrapped: SELECT * FROM cypher(...) |
| Connection | neo4j:// |
PostgreSQL DSN |
| Vector search | Native index | pgvector extension |
| APOC | Available | Not available |
| Data types | Native graph | agtype (JSON superset) |
| Parameterised Cypher | Yes ($param) |
mogrify-based (%s placeholders) |
langchain-age handles SQL wrapping automatically — you write plain Cypher.
Documentation
| Language | Getting Started | Tutorial | API Reference |
|---|---|---|---|
| English | getting-started.md | tutorial.md | api-reference.md |
| Korean | getting-started.md | tutorial.md | api-reference.md |
Notebooks
| Notebook | Description |
|---|---|
| 01_graph.ipynb | AGEGraph: Cypher CRUD, schema, GraphDocument |
| 02_vector.ipynb | AGEVector: similarity, hybrid, MMR, filters, HNSW |
| 03_graph_vector.ipynb | GraphRAG: from_existing_graph, QA chain |
01 requires no API key. 02–03 use OpenAI via getpass.
Running Tests
# Unit tests (no DB required) — 65 tests
pytest tests/unit/
# Integration tests (requires Docker container) — 53 tests
export LANGCHAIN_AGE_TEST_DSN="host=localhost port=5433 dbname=langchain_age user=langchain password=langchain"
pytest tests/integration/
Project Structure
langchain-age/
├── docker/ # PG18 + AGE + pgvector container
│ ├── Dockerfile
│ ├── docker-compose.yml
│ └── init/01_init_extensions.sql
├── langchain_age/
│ ├── __init__.py # Lazy imports (3-mode support)
│ ├── graphs/
│ │ └── age_graph.py # AGEGraph (GraphStore)
│ ├── vectorstores/
│ │ └── age_vector.py # AGEVector (VectorStore)
│ ├── chains/
│ │ └── graph_cypher_qa_chain.py # AGEGraphCypherQAChain
│ └── utils/
│ ├── agtype.py # Vertex/Edge/Path → dict conversion
│ └── cypher.py # SQL wrapping, escaping, validation
├── tests/
│ ├── conftest.py # Auto-skip integration when DSN unset
│ ├── unit/ # 65 tests, no DB
│ └── integration/ # 53 tests, live DB
├── pyproject.toml
├── LICENSE # MIT
├── CHANGELOG.md # Version history
└── .github/workflows/ci.yml # Lint + unit (3.10–3.13) + integration
Python Support
Tested on Python 3.10, 3.11, 3.12, 3.13, 3.14.
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file langchain_age-0.1.1.tar.gz.
File metadata
- Download URL: langchain_age-0.1.1.tar.gz
- Upload date:
- Size: 59.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.0 {"installer":{"name":"uv","version":"0.11.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a6d304ec2597a7d75d97c5ed5cab750d6163e7f91325d63bc1e7a79a721d2b0d
|
|
| MD5 |
978ba53a198cb118510a201a8e475c8f
|
|
| BLAKE2b-256 |
3bcf72fcab6c7a667e4a728d5427e6b94e8d4dd8994eddb4671b340863a268bb
|
File details
Details for the file langchain_age-0.1.1-py3-none-any.whl.
File metadata
- Download URL: langchain_age-0.1.1-py3-none-any.whl
- Upload date:
- Size: 34.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.0 {"installer":{"name":"uv","version":"0.11.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f1bc45d1af3f2bd6afa405509c398c9567312d19ed569692f70f883f801353bc
|
|
| MD5 |
00b730b001cf81f842ed02a1f30d2f47
|
|
| BLAKE2b-256 |
11ce02a5a24559f9c9e62ae607483c38b09ef88e1476e1b9ba3245b09a22fa58
|