Skip to content

Commit a9ec50a

Browse files
committed
Merge branch 'development' into release-3.8.9
2 parents 686ce03 + a76cb32 commit a9ec50a

File tree

7 files changed

+185
-7
lines changed

7 files changed

+185
-7
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from .basic import execute_query
2+
from langtrace_python_sdk import with_langtrace_root_span
3+
4+
5+
class Neo4jRunner:
6+
@with_langtrace_root_span("Neo4jRunner")
7+
def run(self):
8+
execute_query()
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import os
2+
from langtrace_python_sdk import langtrace
3+
from neo4j import GraphDatabase
4+
5+
langtrace.init()
6+
7+
def execute_query():
8+
driver = GraphDatabase.driver(
9+
os.getenv("NEO4J_URI"),
10+
auth=(os.getenv("NEO4J_USERNAME"), os.getenv("NEO4J_PASSWORD"))
11+
)
12+
13+
records, summary, keys = driver.execute_query(
14+
"MATCH (p:Person {age: $age}) RETURN p.name AS name",
15+
age=42,
16+
database_=os.getenv("NEO4J_DATABASE"),
17+
)
18+
19+
# Loop through results and do something with them
20+
for person in records:
21+
print(person)
22+
# Summary information
23+
print("The query `{query}` returned {records_count} records in {time} ms.".format(
24+
query=summary.query, records_count=len(records),
25+
time=summary.result_available_after,
26+
))
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import asyncio
2+
from .basic import search
3+
from langtrace_python_sdk import with_langtrace_root_span
4+
5+
6+
class Neo4jGraphRagRunner:
7+
@with_langtrace_root_span("Neo4jGraphRagRunner")
8+
def run(self):
9+
asyncio.run(search())
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import os
2+
from langtrace_python_sdk import langtrace
3+
from langtrace_python_sdk.utils.with_root_span import with_langtrace_root_span
4+
from neo4j import GraphDatabase
5+
from neo4j_graphrag.generation import GraphRAG
6+
from neo4j_graphrag.indexes import create_vector_index
7+
from neo4j_graphrag.llm import OpenAILLM as LLM
8+
from neo4j_graphrag.embeddings import OpenAIEmbeddings as Embeddings
9+
from neo4j_graphrag.retrievers import VectorRetriever
10+
from neo4j_graphrag.experimental.pipeline.kg_builder import SimpleKGPipeline
11+
12+
langtrace.init()
13+
14+
neo4j_driver = GraphDatabase.driver(os.getenv("NEO4J_URI"), auth=(os.getenv("NEO4J_USERNAME"), os.getenv("NEO4J_PASSWORD")))
15+
16+
ex_llm=LLM(
17+
model_name="gpt-4o-mini",
18+
model_params={
19+
"response_format": {"type": "json_object"},
20+
"temperature": 0
21+
})
22+
23+
embedder = Embeddings()
24+
25+
@with_langtrace_root_span("run_neo_rag")
26+
async def search():
27+
# 1. Build KG and Store in Neo4j Database
28+
kg_builder_pdf = SimpleKGPipeline(
29+
llm=ex_llm,
30+
driver=neo4j_driver,
31+
embedder=embedder,
32+
from_pdf=True
33+
)
34+
await kg_builder_pdf.run_async(file_path='src/examples/neo4j_graphrag_example/data/abramov.pdf')
35+
36+
create_vector_index(neo4j_driver, name="text_embeddings", label="Chunk",
37+
embedding_property="embedding", dimensions=1536, similarity_fn="cosine")
38+
39+
# 2. KG Retriever
40+
vector_retriever = VectorRetriever(
41+
neo4j_driver,
42+
index_name="text_embeddings",
43+
embedder=embedder
44+
)
45+
46+
# 3. GraphRAG Class
47+
llm = LLM(model_name="gpt-4o")
48+
rag = GraphRAG(llm=llm, retriever=vector_retriever)
49+
50+
# 4. Run
51+
response = rag.search("What did the author do in college?")
52+
print(response.answer)
45.8 KB
Binary file not shown.

src/langtrace_python_sdk/instrumentation/neo4j/patch.py

Lines changed: 75 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ def traced_method(wrapped, instance, args, kwargs):
4141
api = APIS[operation_name]
4242
service_provider = SERVICE_PROVIDERS.get("NEO4J", "neo4j")
4343
extra_attributes = baggage.get_baggage(LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY)
44-
4544
span_attributes = {
4645
"langtrace.sdk.name": "langtrace-python-sdk",
4746
"langtrace.service.name": service_provider,
@@ -50,7 +49,7 @@ def traced_method(wrapped, instance, args, kwargs):
5049
"langtrace.version": v(LANGTRACE_SDK_NAME),
5150
"db.system": "neo4j",
5251
"db.operation": api["OPERATION"],
53-
"db.query": json.dumps(kwargs),
52+
"db.query": json.dumps(args[0]) if args and len(args) > 0 else "",
5453
**(extra_attributes if extra_attributes is not None else {}),
5554
}
5655

@@ -70,6 +69,14 @@ def traced_method(wrapped, instance, args, kwargs):
7069

7170
try:
7271
result = wrapped(*args, **kwargs)
72+
73+
if isinstance(result, tuple) and len(result) == 3:
74+
records, result_summary, keys = result
75+
_set_result_attributes(span, records, result_summary, keys)
76+
else:
77+
res = json.dumps(result)
78+
set_span_attribute(span, "neo4j.result.query_response", res)
79+
7380
span.set_status(StatusCode.OK)
7481
return result
7582
except Exception as err:
@@ -85,14 +92,12 @@ def _set_execute_query_attributes(span, args, kwargs):
8592
query = args[0] if args else kwargs.get("query_", None)
8693
if query:
8794
if hasattr(query, "text"):
88-
set_span_attribute(span, "db.statement", query.text)
8995
set_span_attribute(span, "db.query", query.text)
9096
if hasattr(query, "metadata") and query.metadata:
9197
set_span_attribute(span, "db.query.metadata", json.dumps(query.metadata))
9298
if hasattr(query, "timeout") and query.timeout:
9399
set_span_attribute(span, "db.query.timeout", query.timeout)
94100
else:
95-
set_span_attribute(span, "db.statement", query)
96101
set_span_attribute(span, "db.query", query)
97102

98103
parameters = kwargs.get("parameters_", None)
@@ -104,8 +109,72 @@ def _set_execute_query_attributes(span, args, kwargs):
104109

105110
database = kwargs.get("database_", None)
106111
if database:
107-
set_span_attribute(span, "db.name", database)
112+
set_span_attribute(span, "neo4j.db.name", database)
108113

109114
routing = kwargs.get("routing_", None)
110115
if routing:
111-
set_span_attribute(span, "db.routing", str(routing))
116+
set_span_attribute(span, "neo4j.db.routing", str(routing))
117+
118+
119+
@silently_fail
120+
def _set_result_attributes(span, records, result_summary, keys):
121+
"""
122+
Set attributes related to the query result and summary
123+
"""
124+
if records is not None:
125+
record_count = len(records)
126+
set_span_attribute(span, "neo4j.result.record_count", record_count)
127+
if record_count > 0:
128+
set_span_attribute(span, "neo4j.result.records", json.dumps(records))
129+
130+
if keys is not None:
131+
set_span_attribute(span, "neo4j.result.keys", json.dumps(keys))
132+
133+
if result_summary:
134+
if hasattr(result_summary, "database") and result_summary.database:
135+
set_span_attribute(span, "neo4j.db.name", result_summary.database)
136+
137+
if hasattr(result_summary, "query_type") and result_summary.query_type:
138+
set_span_attribute(span, "neo4j.result.query_type", result_summary.query_type)
139+
140+
if hasattr(result_summary, "parameters") and result_summary.parameters:
141+
try:
142+
set_span_attribute(span, "neo4j.result.parameters", json.dumps(result_summary.parameters))
143+
except (TypeError, ValueError):
144+
pass
145+
146+
if hasattr(result_summary, "result_available_after") and result_summary.result_available_after is not None:
147+
set_span_attribute(span, "neo4j.result.available_after_ms", result_summary.result_available_after)
148+
149+
if hasattr(result_summary, "result_consumed_after") and result_summary.result_consumed_after is not None:
150+
set_span_attribute(span, "neo4j.result.consumed_after_ms", result_summary.result_consumed_after)
151+
152+
if hasattr(result_summary, "counters") and result_summary.counters:
153+
counters = result_summary.counters
154+
if hasattr(counters, "nodes_created") and counters.nodes_created:
155+
set_span_attribute(span, "neo4j.result.nodes_created", counters.nodes_created)
156+
157+
if hasattr(counters, "nodes_deleted") and counters.nodes_deleted:
158+
set_span_attribute(span, "neo4j.result.nodes_deleted", counters.nodes_deleted)
159+
160+
if hasattr(counters, "relationships_created") and counters.relationships_created:
161+
set_span_attribute(span, "neo4j.result.relationships_created", counters.relationships_created)
162+
163+
if hasattr(counters, "relationships_deleted") and counters.relationships_deleted:
164+
set_span_attribute(span, "neo4j.result.relationships_deleted", counters.relationships_deleted)
165+
166+
if hasattr(counters, "properties_set") and counters.properties_set:
167+
set_span_attribute(span, "neo4j.result.properties_set", counters.properties_set)
168+
169+
if hasattr(result_summary, "plan") and result_summary.plan:
170+
try:
171+
set_span_attribute(span, "neo4j.result.plan", json.dumps(result_summary.plan))
172+
except (TypeError, ValueError):
173+
pass
174+
175+
if hasattr(result_summary, "notifications") and result_summary.notifications:
176+
try:
177+
set_span_attribute(span, "neo4j.result.notification_count", len(result_summary.notifications))
178+
set_span_attribute(span, "neo4j.result.notifications", json.dumps(result_summary.notifications))
179+
except (AttributeError, TypeError):
180+
pass

src/run_example.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@
2424
"cerebras": False,
2525
"google_genai": False,
2626
"graphlit": False,
27-
"phidata": True,
27+
"phidata": False,
28+
"neo4j": True,
29+
"neo4jgraphrag": True,
2830
}
2931

3032
if ENABLED_EXAMPLES["anthropic"]:
@@ -158,3 +160,15 @@
158160

159161
print(Fore.BLUE + "Running PhiData example" + Fore.RESET)
160162
PhiDataRunner().run()
163+
164+
if ENABLED_EXAMPLES["neo4j"]:
165+
from examples.neo4j_example import Neo4jRunner
166+
167+
print(Fore.BLUE + "Running Neo4j example" + Fore.RESET)
168+
Neo4jRunner().run()
169+
170+
if ENABLED_EXAMPLES["neo4jgraphrag"]:
171+
from examples.neo4j_graphrag_example import Neo4jGraphRagRunner
172+
173+
print(Fore.BLUE + "Running Neo4jGraphRag example" + Fore.RESET)
174+
Neo4jGraphRagRunner().run()

0 commit comments

Comments
 (0)