graphiti-core 0.12.0rc1__py3-none-any.whl → 0.24.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- graphiti_core/cross_encoder/bge_reranker_client.py +12 -2
- graphiti_core/cross_encoder/gemini_reranker_client.py +161 -0
- graphiti_core/cross_encoder/openai_reranker_client.py +7 -5
- graphiti_core/decorators.py +110 -0
- graphiti_core/driver/__init__.py +19 -0
- graphiti_core/driver/driver.py +124 -0
- graphiti_core/driver/falkordb_driver.py +362 -0
- graphiti_core/driver/graph_operations/graph_operations.py +191 -0
- graphiti_core/driver/kuzu_driver.py +182 -0
- graphiti_core/driver/neo4j_driver.py +117 -0
- graphiti_core/driver/neptune_driver.py +305 -0
- graphiti_core/driver/search_interface/search_interface.py +89 -0
- graphiti_core/edges.py +287 -172
- graphiti_core/embedder/azure_openai.py +71 -0
- graphiti_core/embedder/client.py +2 -1
- graphiti_core/embedder/gemini.py +116 -22
- graphiti_core/embedder/voyage.py +13 -2
- graphiti_core/errors.py +8 -0
- graphiti_core/graph_queries.py +162 -0
- graphiti_core/graphiti.py +705 -193
- graphiti_core/graphiti_types.py +4 -2
- graphiti_core/helpers.py +87 -10
- graphiti_core/llm_client/__init__.py +16 -0
- graphiti_core/llm_client/anthropic_client.py +159 -56
- graphiti_core/llm_client/azure_openai_client.py +115 -0
- graphiti_core/llm_client/client.py +98 -21
- graphiti_core/llm_client/config.py +1 -1
- graphiti_core/llm_client/gemini_client.py +290 -41
- graphiti_core/llm_client/groq_client.py +14 -3
- graphiti_core/llm_client/openai_base_client.py +261 -0
- graphiti_core/llm_client/openai_client.py +56 -132
- graphiti_core/llm_client/openai_generic_client.py +91 -56
- graphiti_core/models/edges/edge_db_queries.py +259 -35
- graphiti_core/models/nodes/node_db_queries.py +311 -32
- graphiti_core/nodes.py +420 -205
- graphiti_core/prompts/dedupe_edges.py +46 -32
- graphiti_core/prompts/dedupe_nodes.py +67 -42
- graphiti_core/prompts/eval.py +4 -4
- graphiti_core/prompts/extract_edges.py +27 -16
- graphiti_core/prompts/extract_nodes.py +74 -31
- graphiti_core/prompts/prompt_helpers.py +39 -0
- graphiti_core/prompts/snippets.py +29 -0
- graphiti_core/prompts/summarize_nodes.py +23 -25
- graphiti_core/search/search.py +158 -82
- graphiti_core/search/search_config.py +39 -4
- graphiti_core/search/search_filters.py +126 -35
- graphiti_core/search/search_helpers.py +5 -6
- graphiti_core/search/search_utils.py +1405 -485
- graphiti_core/telemetry/__init__.py +9 -0
- graphiti_core/telemetry/telemetry.py +117 -0
- graphiti_core/tracer.py +193 -0
- graphiti_core/utils/bulk_utils.py +364 -285
- graphiti_core/utils/datetime_utils.py +13 -0
- graphiti_core/utils/maintenance/community_operations.py +67 -49
- graphiti_core/utils/maintenance/dedup_helpers.py +262 -0
- graphiti_core/utils/maintenance/edge_operations.py +339 -197
- graphiti_core/utils/maintenance/graph_data_operations.py +50 -114
- graphiti_core/utils/maintenance/node_operations.py +319 -238
- graphiti_core/utils/maintenance/temporal_operations.py +11 -3
- graphiti_core/utils/ontology_utils/entity_types_utils.py +1 -1
- graphiti_core/utils/text_utils.py +53 -0
- graphiti_core-0.24.3.dist-info/METADATA +726 -0
- graphiti_core-0.24.3.dist-info/RECORD +86 -0
- {graphiti_core-0.12.0rc1.dist-info → graphiti_core-0.24.3.dist-info}/WHEEL +1 -1
- graphiti_core-0.12.0rc1.dist-info/METADATA +0 -350
- graphiti_core-0.12.0rc1.dist-info/RECORD +0 -66
- /graphiti_core/{utils/maintenance/utils.py → migrations/__init__.py} +0 -0
- {graphiti_core-0.12.0rc1.dist-info → graphiti_core-0.24.3.dist-info/licenses}/LICENSE +0 -0
|
@@ -15,96 +15,42 @@ limitations under the License.
|
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
17
|
import logging
|
|
18
|
-
from datetime import datetime
|
|
18
|
+
from datetime import datetime
|
|
19
19
|
|
|
20
|
-
from neo4j import AsyncDriver
|
|
21
20
|
from typing_extensions import LiteralString
|
|
22
21
|
|
|
23
|
-
from graphiti_core.
|
|
24
|
-
from graphiti_core.nodes import
|
|
22
|
+
from graphiti_core.driver.driver import GraphDriver, GraphProvider
|
|
23
|
+
from graphiti_core.models.nodes.node_db_queries import (
|
|
24
|
+
EPISODIC_NODE_RETURN,
|
|
25
|
+
EPISODIC_NODE_RETURN_NEPTUNE,
|
|
26
|
+
)
|
|
27
|
+
from graphiti_core.nodes import EpisodeType, EpisodicNode, get_episodic_node_from_record
|
|
25
28
|
|
|
26
29
|
EPISODE_WINDOW_LEN = 3
|
|
27
30
|
|
|
28
31
|
logger = logging.getLogger(__name__)
|
|
29
32
|
|
|
30
33
|
|
|
31
|
-
async def
|
|
32
|
-
|
|
33
|
-
records, _, _ = await driver.execute_query(
|
|
34
|
-
"""
|
|
35
|
-
SHOW INDEXES YIELD name
|
|
36
|
-
""",
|
|
37
|
-
database_=DEFAULT_DATABASE,
|
|
38
|
-
)
|
|
39
|
-
index_names = [record['name'] for record in records]
|
|
40
|
-
await semaphore_gather(
|
|
41
|
-
*[
|
|
42
|
-
driver.execute_query(
|
|
43
|
-
"""DROP INDEX $name""",
|
|
44
|
-
name=name,
|
|
45
|
-
database_=DEFAULT_DATABASE,
|
|
46
|
-
)
|
|
47
|
-
for name in index_names
|
|
48
|
-
]
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
range_indices: list[LiteralString] = [
|
|
52
|
-
'CREATE INDEX entity_uuid IF NOT EXISTS FOR (n:Entity) ON (n.uuid)',
|
|
53
|
-
'CREATE INDEX episode_uuid IF NOT EXISTS FOR (n:Episodic) ON (n.uuid)',
|
|
54
|
-
'CREATE INDEX community_uuid IF NOT EXISTS FOR (n:Community) ON (n.uuid)',
|
|
55
|
-
'CREATE INDEX relation_uuid IF NOT EXISTS FOR ()-[e:RELATES_TO]-() ON (e.uuid)',
|
|
56
|
-
'CREATE INDEX mention_uuid IF NOT EXISTS FOR ()-[e:MENTIONS]-() ON (e.uuid)',
|
|
57
|
-
'CREATE INDEX has_member_uuid IF NOT EXISTS FOR ()-[e:HAS_MEMBER]-() ON (e.uuid)',
|
|
58
|
-
'CREATE INDEX entity_group_id IF NOT EXISTS FOR (n:Entity) ON (n.group_id)',
|
|
59
|
-
'CREATE INDEX episode_group_id IF NOT EXISTS FOR (n:Episodic) ON (n.group_id)',
|
|
60
|
-
'CREATE INDEX relation_group_id IF NOT EXISTS FOR ()-[e:RELATES_TO]-() ON (e.group_id)',
|
|
61
|
-
'CREATE INDEX mention_group_id IF NOT EXISTS FOR ()-[e:MENTIONS]-() ON (e.group_id)',
|
|
62
|
-
'CREATE INDEX name_entity_index IF NOT EXISTS FOR (n:Entity) ON (n.name)',
|
|
63
|
-
'CREATE INDEX created_at_entity_index IF NOT EXISTS FOR (n:Entity) ON (n.created_at)',
|
|
64
|
-
'CREATE INDEX created_at_episodic_index IF NOT EXISTS FOR (n:Episodic) ON (n.created_at)',
|
|
65
|
-
'CREATE INDEX valid_at_episodic_index IF NOT EXISTS FOR (n:Episodic) ON (n.valid_at)',
|
|
66
|
-
'CREATE INDEX name_edge_index IF NOT EXISTS FOR ()-[e:RELATES_TO]-() ON (e.name)',
|
|
67
|
-
'CREATE INDEX created_at_edge_index IF NOT EXISTS FOR ()-[e:RELATES_TO]-() ON (e.created_at)',
|
|
68
|
-
'CREATE INDEX expired_at_edge_index IF NOT EXISTS FOR ()-[e:RELATES_TO]-() ON (e.expired_at)',
|
|
69
|
-
'CREATE INDEX valid_at_edge_index IF NOT EXISTS FOR ()-[e:RELATES_TO]-() ON (e.valid_at)',
|
|
70
|
-
'CREATE INDEX invalid_at_edge_index IF NOT EXISTS FOR ()-[e:RELATES_TO]-() ON (e.invalid_at)',
|
|
71
|
-
]
|
|
72
|
-
|
|
73
|
-
fulltext_indices: list[LiteralString] = [
|
|
74
|
-
"""CREATE FULLTEXT INDEX episode_content IF NOT EXISTS
|
|
75
|
-
FOR (e:Episodic) ON EACH [e.content, e.source, e.source_description, e.group_id]""",
|
|
76
|
-
"""CREATE FULLTEXT INDEX node_name_and_summary IF NOT EXISTS
|
|
77
|
-
FOR (n:Entity) ON EACH [n.name, n.summary, n.group_id]""",
|
|
78
|
-
"""CREATE FULLTEXT INDEX community_name IF NOT EXISTS
|
|
79
|
-
FOR (n:Community) ON EACH [n.name, n.group_id]""",
|
|
80
|
-
"""CREATE FULLTEXT INDEX edge_name_and_fact IF NOT EXISTS
|
|
81
|
-
FOR ()-[e:RELATES_TO]-() ON EACH [e.name, e.fact, e.group_id]""",
|
|
82
|
-
]
|
|
83
|
-
|
|
84
|
-
index_queries: list[LiteralString] = range_indices + fulltext_indices
|
|
85
|
-
|
|
86
|
-
await semaphore_gather(
|
|
87
|
-
*[
|
|
88
|
-
driver.execute_query(
|
|
89
|
-
query,
|
|
90
|
-
database_=DEFAULT_DATABASE,
|
|
91
|
-
)
|
|
92
|
-
for query in index_queries
|
|
93
|
-
]
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
async def clear_data(driver: AsyncDriver, group_ids: list[str] | None = None):
|
|
98
|
-
async with driver.session(database=DEFAULT_DATABASE) as session:
|
|
34
|
+
async def clear_data(driver: GraphDriver, group_ids: list[str] | None = None):
|
|
35
|
+
async with driver.session() as session:
|
|
99
36
|
|
|
100
37
|
async def delete_all(tx):
|
|
101
38
|
await tx.run('MATCH (n) DETACH DELETE n')
|
|
102
39
|
|
|
103
40
|
async def delete_group_ids(tx):
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
41
|
+
labels = ['Entity', 'Episodic', 'Community']
|
|
42
|
+
if driver.provider == GraphProvider.KUZU:
|
|
43
|
+
labels.append('RelatesToNode_')
|
|
44
|
+
|
|
45
|
+
for label in labels:
|
|
46
|
+
await tx.run(
|
|
47
|
+
f"""
|
|
48
|
+
MATCH (n:{label})
|
|
49
|
+
WHERE n.group_id IN $group_ids
|
|
50
|
+
DETACH DELETE n
|
|
51
|
+
""",
|
|
52
|
+
group_ids=group_ids,
|
|
53
|
+
)
|
|
108
54
|
|
|
109
55
|
if group_ids is None:
|
|
110
56
|
await session.execute_write(delete_all)
|
|
@@ -113,7 +59,7 @@ async def clear_data(driver: AsyncDriver, group_ids: list[str] | None = None):
|
|
|
113
59
|
|
|
114
60
|
|
|
115
61
|
async def retrieve_episodes(
|
|
116
|
-
driver:
|
|
62
|
+
driver: GraphDriver,
|
|
117
63
|
reference_time: datetime,
|
|
118
64
|
last_n: int = EPISODE_WINDOW_LEN,
|
|
119
65
|
group_ids: list[str] | None = None,
|
|
@@ -123,7 +69,7 @@ async def retrieve_episodes(
|
|
|
123
69
|
Retrieve the last n episodic nodes from the graph.
|
|
124
70
|
|
|
125
71
|
Args:
|
|
126
|
-
driver (
|
|
72
|
+
driver (Driver): The Neo4j driver instance.
|
|
127
73
|
reference_time (datetime): The reference time to filter episodes. Only episodes with a valid_at timestamp
|
|
128
74
|
less than or equal to this reference_time will be retrieved. This allows for
|
|
129
75
|
querying the graph's state at a specific point in time.
|
|
@@ -133,52 +79,42 @@ async def retrieve_episodes(
|
|
|
133
79
|
Returns:
|
|
134
80
|
list[EpisodicNode]: A list of EpisodicNode objects representing the retrieved episodes.
|
|
135
81
|
"""
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
82
|
+
|
|
83
|
+
query_params: dict = {}
|
|
84
|
+
query_filter = ''
|
|
85
|
+
if group_ids and len(group_ids) > 0:
|
|
86
|
+
query_filter += '\nAND e.group_id IN $group_ids'
|
|
87
|
+
query_params['group_ids'] = group_ids
|
|
88
|
+
|
|
89
|
+
if source is not None:
|
|
90
|
+
query_filter += '\nAND e.source = $source'
|
|
91
|
+
query_params['source'] = source.name
|
|
140
92
|
|
|
141
93
|
query: LiteralString = (
|
|
142
94
|
"""
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
+
|
|
95
|
+
MATCH (e:Episodic)
|
|
96
|
+
WHERE e.valid_at <= $reference_time
|
|
97
|
+
"""
|
|
98
|
+
+ query_filter
|
|
99
|
+
+ """
|
|
100
|
+
RETURN
|
|
101
|
+
"""
|
|
102
|
+
+ (
|
|
103
|
+
EPISODIC_NODE_RETURN_NEPTUNE
|
|
104
|
+
if driver.provider == GraphProvider.NEPTUNE
|
|
105
|
+
else EPISODIC_NODE_RETURN
|
|
106
|
+
)
|
|
147
107
|
+ """
|
|
148
|
-
RETURN e.content AS content,
|
|
149
|
-
e.created_at AS created_at,
|
|
150
|
-
e.valid_at AS valid_at,
|
|
151
|
-
e.uuid AS uuid,
|
|
152
|
-
e.group_id AS group_id,
|
|
153
|
-
e.name AS name,
|
|
154
|
-
e.source_description AS source_description,
|
|
155
|
-
e.source AS source
|
|
156
108
|
ORDER BY e.valid_at DESC
|
|
157
109
|
LIMIT $num_episodes
|
|
158
110
|
"""
|
|
159
111
|
)
|
|
160
|
-
|
|
161
|
-
result = await driver.execute_query(
|
|
112
|
+
result, _, _ = await driver.execute_query(
|
|
162
113
|
query,
|
|
163
114
|
reference_time=reference_time,
|
|
164
|
-
source=source.name if source is not None else None,
|
|
165
115
|
num_episodes=last_n,
|
|
166
|
-
|
|
167
|
-
database_=DEFAULT_DATABASE,
|
|
116
|
+
**query_params,
|
|
168
117
|
)
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
content=record['content'],
|
|
172
|
-
created_at=datetime.fromtimestamp(
|
|
173
|
-
record['created_at'].to_native().timestamp(), timezone.utc
|
|
174
|
-
),
|
|
175
|
-
valid_at=(record['valid_at'].to_native()),
|
|
176
|
-
uuid=record['uuid'],
|
|
177
|
-
group_id=record['group_id'],
|
|
178
|
-
source=EpisodeType.from_str(record['source']),
|
|
179
|
-
name=record['name'],
|
|
180
|
-
source_description=record['source_description'],
|
|
181
|
-
)
|
|
182
|
-
for record in result.records
|
|
183
|
-
]
|
|
118
|
+
|
|
119
|
+
episodes = [get_episodic_node_from_record(record) for record in result]
|
|
184
120
|
return list(reversed(episodes)) # Return in chronological order
|