Natural-language queries (Text2Cypher)
Tip
On a JenaGraph use Natural-language queries in SPARQL (Text2SPARQL), or Natural-language queries on any backend (Text2Query) to pick
the right translator for any backend.
Optional module
Install: only on
Neo4jGraph:pip install "cvcdocdb[graphrag]"(neo4j-graphrag, Python >= 3.10). Nothing extra onNetworkXGraphorMemgraphGraph.Import:
from cvcdocdb import Text2Cypher
cvcdocdb.text2cypher lets you ask questions in natural language to a
cvcdocdb graph. An LLM translates the question to Cypher and the query is run
on the graph. The API is the same for every backend, so a script doesn’t
change when you switch from NetworkXGraph to Neo4jGraph or back.
The LLM is passed as a handle that already carries its own configuration (API key, endpoint, model…):
a plain
prompt: str -> strcallable, e.g. a wrapper around an in-house LLM client, orany object with an
invoke(prompt)method returning an answer with a.contentstring (e.g. theneo4j_graphragLLM classes).
Quick-start
from cvcdocdb import Text2Cypher, Text2CypherError
def ask(graph, question, llm):
"""Works unchanged with any backend."""
try:
result = Text2Cypher(graph, llm=llm).query(question)
except Text2CypherError as exc:
print("The LLM produced an unusable query:", exc)
return None
print(result.cypher) # the Cypher generated by the LLM
return result.records # e.g. [{'n': 42}], shaped like graph.query(cypher)
ask(graph, "Quantes persones viuen a Caldes d'Estrac el 1905?",
llm=lambda prompt: my_client.complete(prompt))
Backends
Neo4jGraph— delegates toneo4j_graphrag’sText2CypherRetriever, on the graph’s own driver and database (no Neo4j credentials passed again). Needs the optional dependency (Python >= 3.10):pip install cvcdocdb[graphrag]
retrieverexposes the underlying retriever, e.g. to plug it into aneo4j_graphragGraphRAGpipeline.MemgraphGraph— handled by cvcdocdb itself, with no extra dependency (neo4j_graphragdoesn’t work with Memgraph: its retriever runsCALL dbms.components()withoutYIELD, which Memgraph rejects). The query is checked for write clauses and run withgraph.query(cypher), and the schema is read with plain Cypher (Memgraph has nodb.schema.*).retrieverisNone.NetworkXGraph(or any backend whosequery()accepts Cypher) — handled by cvcdocdb itself, with no extra dependency; the query is run withgraph.query(cypher).NetworkXGraphevaluates the common read-only Cypher (node/relationship patterns,WHERE,WITH, aggregations,ORDER BY,SKIP/LIMIT…) with the same results as Neo4j — seecvcdocdb.nx_cypherfor the supported subset. If the LLM generates syntax outside it (variable-length paths,UNWIND,CASE…),Text2CypherErroris raised instead of returning wrong results.
Notes
Only read-only Cypher is executed; anything else raises
Text2CypherErrorbefore running. On Neo4j the query is planned withEXPLAINfirst; on other backends it is checked for write clauses (CREATE,MERGE,SET,DELETE…).The schema shown to the LLM is introspected from the graph (on Neo4j with built-in
db.schema.*procedures, so the APOC plugin is not required). It is the same text on every backend for the same graph: every label of a node (main and alternative), labels and properties in alphabetical order. Properties starting with_and cvcdocdb bookkeeping attributes are left out. Passschema=...to provide your own schema text,examples=[...]for few-shot examples, orcustom_prompt=...(with{schema},{examples}and{query_text}placeholders) to replace the prompt.
Natural-language questions over any cvcdocdb graph backend.
Text2Cypher gives the same API whatever the backend, so a script
doesn’t change when the graph does:
from cvcdocdb import Text2Cypher
t2c = Text2Cypher(graph, llm=my_llm) # graph: Neo4jGraph, NetworkXGraph...
result = t2c.query("Quantes persones viuen a Caldes d'Estrac?")
result.cypher # "MATCH (i:IndividuPadro)-[:VIU_A]->(l:LlocPadro ...) ..."
result.records # [{'n': 42}] — same shape as graph.query(cypher)
An LLM translates the question to Cypher, which is then run on the graph.
Only read-only Cypher is ever executed; anything else raises
Text2CypherError.
The LLM is a handle that already carries its own configuration (API key, endpoint, model…), so nothing else has to be passed:
a plain Python callable
prompt: str -> str(wrapped inCallableLLM), e.g. a wrapper around any in-house LLM client, orany object with an
invoke(prompt)method returning something with a.contentstring — e.g. theneo4j_graphragLLM classes.
Backends:
Neo4jGraph— delegates toneo4j_graphrag’sText2CypherRetrieveron the graph’s own driver and database (no Neo4j credentials passed again), which checks the query withEXPLAINbefore running it. Requires the optional dependency:pip install cvcdocdb[graphrag].MemgraphGraph— handled by cvcdocdb itself like the backends below (neo4j_graphrag’s retriever doesn’t work on Memgraph), with the schema read with plain Cypher. No extra dependency.Any other backend with a Cypher-capable
query()(e.g.NetworkXGraph) — handled by cvcdocdb itself, no extra dependency; the query is checked for write clauses and run withgraph.query(). OnNetworkXGraphthat evaluates the common read-only Cypher (patterns,WHERE,WITH, aggregations,ORDER BY…) with Neo4j semantics (seecvcdocdb.nx_cypher); a query using syntax it doesn’t support (variable-length paths,UNWIND,CASE…) raisesText2CypherErrorinstead of returning wrong results.
- class cvcdocdb.text2cypher.CallableLLM(fn, model_name='callable')[source]
Bases:
objectWrap a plain
prompt: str -> strcallable as an LLM handle.Accepts the call styles LLM clients (and
neo4j_graphrag) use: a prompt string, optionally withsystem_instruction/message_history, or a list of messages (dicts or objects withcontent); messages are flattened into a single prompt.- Parameters:
- class cvcdocdb.text2cypher.LLMResponse(content)[source]
Bases:
objectMinimal LLM answer: just its text
content.- Parameters:
content (str)
- class cvcdocdb.text2cypher.Text2Cypher(graph, llm, *, schema=None, examples=None, custom_prompt=None, database=None)[source]
Bases:
objectAsk natural-language questions to a cvcdocdb graph, whatever its backend.
Unless schema is given, the graph schema shown to the LLM is introspected from the graph (on Neo4j with built-in
db.schema.*procedures, so the APOC plugin is not needed). It is the same text on every backend for the same graph. Properties starting with_and cvcdocdb bookkeeping attributes are left out of it.- Parameters:
graph (Any) – The graph to query: a
Neo4jGraph, aMemgraphGraph, aNetworkXGraph, or any backend whosequery()accepts a Cypher string.llm (Any) – The LLM handle — a
prompt: str -> strcallable, or an object withinvoke(prompt)returning an answer with.content.schema (str | None) – Schema text for the prompt; introspected when omitted.
examples (List[str] | None) – Optional example question/Cypher pairs for the prompt.
custom_prompt (str | None) – Optional prompt template replacing
DEFAULT_PROMPT, with{schema},{examples}and{query_text}placeholders.database (str | None) – Neo4j only — database to query; defaults to the graph’s.
- retriever
On Neo4j, the underlying
neo4j_graphragText2CypherRetriever(e.g. to plug it into aGraphRAGpipeline);Noneon other backends.
- Raises:
TypeError – If graph has no
query()method, or llm is neither a callable nor an object withinvoke.ImportError – On a
Neo4jGraph, ifneo4j-graphragis missing.
- Parameters:
- query(question)[source]
Translate question to Cypher with the LLM, run it, return the rows.
- Raises:
Text2CypherError – If the generated Cypher is invalid or not read-only (nothing is executed in that case).
- Parameters:
question (str)
- Return type:
- exception cvcdocdb.text2cypher.Text2CypherError[source]
Bases:
Text2QueryErrorThe LLM’s answer could not be run: invalid or non-read-only Cypher.
- class cvcdocdb.text2cypher.Text2CypherResult(cypher, records, metadata=<factory>)[source]
Bases:
objectAnswer to a natural-language question.
- exception cvcdocdb.text2cypher.Text2QueryError[source]
Bases:
ExceptionBase class of the natural-language query errors (Text2Cypher, Text2SPARQL, Text2Query): the LLM’s answer could not be run.
- cvcdocdb.text2cypher.collect_schema(graph)[source]
(node_props, rel_props, relationships)of a graph, read through the commonGraphStoreAPI (seeformat_schema()for their shape). Every label of a node (main and alternative) is included, as on Neo4j.
- cvcdocdb.text2cypher.extract_cypher(text)[source]
Return the Cypher in an LLM answer (inside a code fence if there is one).