Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ dist/
htmlcov/
.idea/
.env
.env.*
!.env.example
docs/build/
.vscode/
.python-version
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

### Added

- Added `examples/SETUP.md` and `examples/.env.example`, documenting what the examples need in order to run — extras, API keys and services — and which providers are free or have a local equivalent.
- The `examples` extra now declares `python-dotenv` and `requests`. Fourteen examples call `load_dotenv()` and `tools_retriever_example.py` calls a web API with `requests`, but neither package was declared, so both resolved only transitively.
- `AnthropicLLM` now supports structured output via the `response_format` argument, accepting a Pydantic model or an Anthropic `output_config` dict, alongside `OpenAILLM` and `VertexAILLM`.
- Added `neo4j_graphrag.llm.utils.split_http_client_kwargs`, a shared helper that routes a constructor's `http_client` kwarg to whichever of the sync/async SDK clients it matches. `AnthropicLLM`, `OpenAILLM`, and `AzureOpenAILLM` now all use this single implementation instead of three separately maintained copies of the same logic. Custom subclasses that construct their own SDK clients can call it to get the same behavior; it is exported from `neo4j_graphrag.llm` for that purpose.
- Added `BaseAnthropicLLM`, a new base class holding all of `AnthropicLLM`'s shared message-building, schema-conversion, and response-parsing logic, mirroring `BaseOpenAILLM`. Both `BaseAnthropicLLM` and `BaseOpenAILLM` are now exported from `neo4j_graphrag.llm` as documented, supported extension points for subclassing to reach custom Anthropic/OpenAI-compatible endpoints.
Expand Down
64 changes: 64 additions & 0 deletions examples/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Template for the examples' configuration.
#
# cp examples/.env.example .env
#
# `.env` at the repo root is gitignored; this template is the only env file that
# is tracked. Never put a real credential in this file, and never commit `.env`.
#
# Credential lines are commented out on purpose: an empty `FOO=` would blank out
# a variable you already exported when this file is sourced. Uncomment a line
# only when you are putting a real value on it.
#
# 14 examples call load_dotenv(); the rest read the environment directly, so
# export the file before running one:
#
# set -a; source .env; set +a


# --- Neo4j ------------------------------------------------------------------
# The local database, as started by tests/e2e/docker-compose.yml. Examples that
# use the public demo database hardcode its URI and ignore these.
NEO4J_URI=bolt://localhost:7687
NEO4J_USER=neo4j
NEO4J_PASSWORD=password


# --- API keys ---------------------------------------------------------------

# OpenAI. No free tier. https://platform.openai.com/api-keys
# OPENAI_API_KEY=

# Google Gemini via AI Studio. Free, no credit card required.
# https://aistudio.google.com/apikey
# GOOGLE_API_KEY=

# Cohere. Free trial key: 1,000 calls/month, not for production.
# https://dashboard.cohere.com/api-keys
# CO_API_KEY=

# Mistral AI. Free "Experiment" tier; phone verification, no card.
# https://console.mistral.ai/api-keys
# MISTRAL_API_KEY=

# Anthropic. No free tier - the account needs purchased credits.
# https://console.anthropic.com/settings/keys
# ANTHROPIC_API_KEY=


# --- Cloud providers --------------------------------------------------------

# Google Vertex AI. Credentials come from application-default login, not from
# here; this only selects the project.
# gcloud auth application-default login
# gcloud auth application-default set-quota-project <project>
# The quota project is required for VertexAIEmbeddings but not for VertexAILLM.
# GOOGLE_CLOUD_PROJECT=

# AWS Bedrock. Credentials come from the standard AWS chain (~/.aws/credentials,
# SSO, or AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY); the examples hardcode the
# region. Anthropic models on Bedrock also need a one-time use-case form in the
# Bedrock console.
# If your login wrote a named profile rather than the default (SSO usually
# does), name it here - otherwise boto3 reports "Unable to locate credentials"
# even though the login succeeded.
# AWS_PROFILE=
5 changes: 3 additions & 2 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Examples Index

> **New here?** [SETUP.md](SETUP.md) covers what you need in order to run these -
> extras, API keys and services - and which providers are free.

This folder contains examples usage for the different features
supported by the `neo4j-graphrag` package:

Expand Down Expand Up @@ -119,8 +122,6 @@ are listed in [the last section of this file](#customize).
- [Custom](./customize/build_graph/components/loaders/custom_loader.py)
- Text Splitter:
- [Fixed size splitter](./customize/build_graph/components/splitters/fixed_size_splitter.py)
- [Splitter from LangChain](./customize/build_graph/components/splitters/langhchain_splitter.py)
- [Splitter from LLamaIndex](./customize/build_graph/components/splitters/llamaindex_splitter.py)
- [Custom](./customize/build_graph/components/splitters/custom_splitter.py)
- [Chunk embedder]()
- Schema Builder:
Expand Down
134 changes: 134 additions & 0 deletions examples/SETUP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# Running the examples

[README.md](README.md) indexes what the examples demonstrate. This file covers what you need
installed and configured to actually run them.

## Quick start

```bash
uv sync --all-extras --group dev
cp examples/.env.example .env # then add an OPENAI_API_KEY
docker compose -f tests/e2e/docker-compose.yml up -d --wait
set -a; source .env; set +a
python examples/question_answering/graphrag.py
```

That covers about two thirds of the examples. The rest need another provider's key, a vector
store, or a local runtime — see below.

## Credentials

**Never commit a credential to this repository.** Keys belong in `.env` at the repo root, which is
gitignored. `examples/.env.example` is a template of placeholders and is the only env file that is
tracked.

Only 14 examples call `load_dotenv()`. The rest read the environment directly, so export the file
before running one:

```bash
set -a; source .env; set +a
```

Two examples expect a key edited directly into their source rather than read from the
environment: `customize/embeddings/azure_openai_embeddings.py`, and the Pinecone examples. If you
edit them, do not commit the edit — for Pinecone, use Pinecone Local instead, which ignores keys
entirely.

## Running an example

Run examples from the repo root — several resolve data files relative to it.

**Three examples need `examples/data` on the import path.** They load a pre-computed vector as a
bare module (`from embedding_avatar import ...`), which does not resolve from the repo root:

```bash
PYTHONPATH=examples/data python examples/retrieve/similarity_search_for_vector.py
PYTHONPATH=examples/data python examples/customize/retrievers/external/qdrant/qdrant_vector_search.py
PYTHONPATH=examples/data python examples/customize/retrievers/external/weaviate/weaviate_vector_search.py
```

The Ollama examples take the model name as an argument, since it depends on what you have pulled:

```bash
ollama pull llama3.2
python examples/customize/llms/ollama_llm.py llama3.2
```

## Services

`tests/e2e/docker-compose.yml` starts everything the examples can talk to locally. The vector
stores sit behind a `vectordb` profile:

```bash
docker compose -f tests/e2e/docker-compose.yml up -d --wait # Neo4j + APOC
docker compose -f tests/e2e/docker-compose.yml --profile vectordb up -d --wait # + vector stores
```

17 examples instead use the public read-only demo database at `demo.neo4jlabs.com`. Those need no
local setup, just network access — and because it is read-only, the two examples that write
message history to it fail with `Forbidden`.

Some retriever examples assume a vector or fulltext index already exists on the local database.
`examples/database_operations/` has scripts that create them.

The vector-store examples need their store populated first:

```bash
uv run python -m tests.e2e.weaviate_e2e.populate_dbs
uv run python -m tests.e2e.qdrant_e2e.populate_dbs
```

## Providers: what is free, and what has a local equivalent

| Provider | Free without a credit card? | Local / Docker equivalent |
|---|---|---|
| **Google Gemini** (AI Studio) | **Yes.** Free tier on the Flash models; Pro is paid-only. A new key cannot reach every model the API lists — `gemini-flash-latest` is the safe choice. Prompts may be used to improve Google's products | — |
| **Cohere** | **Yes.** Trial key, rate-limited and not for production | — |
| **Mistral** | **Yes.** "Experiment" tier. Phone verification, no card | — |
| **Ollama** | **Yes.** Entirely local | `brew install ollama` |
| **Weaviate / Qdrant** | n/a | Docker, in the `vectordb` profile |
| **Pinecone** | Hosted free "Starter" tier exists | **Pinecone Local** — an in-memory emulator in the `vectordb` profile. Ignores API keys, keeps nothing after it stops |
| **sentence-transformers / spaCy** | n/a | Local model download (spaCy `en_core_web_lg` is ~560 MB) |
| **OpenAI** | No free tier | — |
| **Anthropic** | No free tier; the account needs purchased credits | Claude is also reachable through Bedrock |
| **Vertex AI** | No standing free tier; new GCP accounts get trial credits | Gemini via AI Studio reaches the same model family for free |
| **AWS Bedrock** | No standing free tier | — |
| **Azure OpenAI** | No free tier; needs a deployed resource | — |

So every non-OpenAI provider has a free or local path.

## Cloud providers

- **Vertex AI** authenticates through `gcloud auth application-default login`, not an API key.
`gcloud auth application-default set-quota-project <project>` is required for
`VertexAIEmbeddings` but not for `VertexAILLM`.
- **Bedrock** enables serverless models by default, but Anthropic models need a one-time use-case
form submitted from the Bedrock console before the first call. Credentials come from the standard
AWS chain — if you use named profiles (SSO commonly writes them), export `AWS_PROFILE=<name>` or
boto3 will report "Unable to locate credentials" despite a successful login.
- **Azure OpenAI** needs a deployed resource; the example hardcodes its endpoint and key.

If you work at Neo4j, an Aura dev environment already has these cloud accounts provisioned, and
`omni` will print their coordinates. Vertex AI is a managed API, so it works while the environment
is scaled down.

## Python extras

Each provider is behind an extra in `pyproject.toml` — `pip install "neo4j-graphrag[openai]"`, or
`uv sync --all-extras` to get everything at once.

Note that examples import library symbols (`OpenAILLM`) rather than provider SDKs, so which extra
an example needs is not visible from its imports alone.

## Known issues in the examples

These are properties of the examples, not of your setup.

- `customize/retrievers/text2cypher_custom_prompt.py` declares `(:User)-[:REVIEWED]->(:Movie)` in
its schema, which the `recommendations` database does not have.
- `customize/llms/llm_with_neo4j_message_history.py` and
`question_answering/graphrag_with_neo4j_message_history.py` write message history to the
**read-only** demo database, so they fail with `Forbidden` as written.
- `customize/embeddings/azure_openai_embeddings.py` reads no environment variables. Its endpoint,
key and API version are placeholders in the file itself (`api_key="<my key>"`), so it has to be
edited before it will run — and that edit must not be committed.
20 changes: 19 additions & 1 deletion examples/customize/embeddings/ollama_embeddings.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,29 @@
"""This example demonstrate how to embed a text into a vector
using a local model served by Ollama.

The model is a command-line argument, because which models you have depends on
what you have pulled locally. Pull an embedding model first, then name it:

ollama pull nomic-embed-text
python examples/customize/embeddings/ollama_embeddings.py nomic-embed-text
"""

import argparse

from neo4j_graphrag.embeddings import OllamaEmbeddings

parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"model",
nargs="?",
default="nomic-embed-text",
help="an embedding model you have pulled with `ollama pull` "
"(default: %(default)s)",
)
args = parser.parse_args()

embeder = OllamaEmbeddings(
model="<model_name>",
model=args.model,
# host="...", # if using a remote server
)
res = embeder.embed_query("my question")
Expand Down
19 changes: 18 additions & 1 deletion examples/customize/llms/ollama_llm.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,28 @@
"""This example demonstrate how to invoke an LLM using a local model
served by Ollama.

The model is a command-line argument, because which models you have depends on
what you have pulled locally. Pull one first, then name it:

ollama pull llama3.2
python examples/customize/llms/ollama_llm.py llama3.2
"""

import argparse

from neo4j_graphrag.llm import LLMResponse, OllamaLLM

parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"model",
nargs="?",
default="llama3.2",
help="name of a model you have pulled with `ollama pull` (default: %(default)s)",
)
args = parser.parse_args()

with OllamaLLM(
model_name="<model_name>",
model_name=args.model,
# model_params={"options": {"temperature": 0}, "format": "json"},
# host="...", # if using a remote server
) as llm:
Expand Down
22 changes: 18 additions & 4 deletions examples/customize/llms/ollama_tool_calls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@

To run this example:
1. Make sure you have `ollama serve` running
2. Run: python examples/tool_calls/ollama_tool_calls.py
2. Pull a model that supports tool calling: `ollama pull llama3.2`
3. Run: python examples/customize/llms/ollama_tool_calls.py llama3.2

The model is a command-line argument because not every local model supports
tool calling - check the model's page in the Ollama library before using it.
"""

import argparse
import asyncio
import json
from typing import Dict, Any
Expand Down Expand Up @@ -61,9 +66,9 @@ def process_tool_calls(response: ToolCallResponse) -> Dict[str, Any]:
return results[0] if results else {}


async def main() -> None:
async def main(model: str) -> None:
async with OllamaLLM(
model_name="mistral:latest", model_params={"options": {"temperature": 0}}
model_name=model, model_params={"options": {"temperature": 0}}
) as llm:
# Example text containing information about a person
text = "Stella Hane is a 35-year-old software engineer who loves coding."
Expand Down Expand Up @@ -91,5 +96,14 @@ async def main() -> None:


if __name__ == "__main__":
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"model",
nargs="?",
default="llama3.2",
help="a tool-calling model you have pulled with `ollama pull` "
"(default: %(default)s)",
)
args = parser.parse_args()
# Run the async main function
asyncio.run(main())
asyncio.run(main(args.model))
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ You will need both a Pinecone vector database and a Neo4j database to use this r
Update `NEO4J_AUTH`, `NEO4J_URL`, and `PC_API_KEY` variables in the `tests/e2e/pinecone_e2e/populate_dbs.py` script then run this from the project root to write test data to both dbs.

```
uv run python -m tests/e2e/pinecone_e2e/populate_dbs.py
uv run python -m tests.e2e.pinecone_e2e.populate_dbs
```

### Install Pinecone client
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ docker compose -f tests/e2e/docker-compose.yml --profile vectordb up -d --wait
Run this from the project root to write data to both dbs.

```
uv run python -m tests/e2e/weaviate_e2e/populate_dbs.py
uv run python -m tests.e2e.weaviate_e2e.populate_dbs
```

### Install Weaviate client
Expand Down
5 changes: 5 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ experimental = [
examples = [
"langchain-openai>=0.2.2,<2.0.0",
"langchain-huggingface>=0.1.0,<2.0.0",
# 14 examples call load_dotenv(); without this it resolves only by accident,
# transitively through another extra.
"python-dotenv>=1.0.0,<2.0.0",
# tools_retriever_example.py calls the open-meteo API with it.
"requests>=2.31.0,<3.0.0",
]
# NOTE: spaCy currently has a Python 3.14 import-time failure via its pydantic v1 path
# (see https://github.com/explosion/spaCy/issues/13895). Until upstream resolves this,
Expand Down
4 changes: 4 additions & 0 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading