graphiti-core 0.4.1__tar.gz → 0.4.3__tar.gz
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.
Potentially problematic release.
This version of graphiti-core might be problematic. Click here for more details.
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/PKG-INFO +1 -1
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/cross_encoder/bge_reranker_client.py +1 -2
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/cross_encoder/client.py +3 -4
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/edges.py +60 -5
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/embedder/client.py +3 -3
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/embedder/openai.py +2 -2
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/embedder/voyage.py +3 -3
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/graphiti.py +14 -10
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/helpers.py +1 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/nodes.py +73 -5
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/prompts/lib.py +5 -1
- graphiti_core-0.4.3/graphiti_core/prompts/prompt_helpers.py +1 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/search/search_utils.py +1 -1
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/utils/maintenance/edge_operations.py +6 -7
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/utils/maintenance/temporal_operations.py +1 -2
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/pyproject.toml +1 -1
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/LICENSE +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/README.md +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/__init__.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/cross_encoder/__init__.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/cross_encoder/openai_reranker_client.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/embedder/__init__.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/errors.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/llm_client/__init__.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/llm_client/anthropic_client.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/llm_client/client.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/llm_client/config.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/llm_client/errors.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/llm_client/groq_client.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/llm_client/openai_client.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/llm_client/utils.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/models/__init__.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/models/edges/__init__.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/models/edges/edge_db_queries.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/models/nodes/__init__.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/models/nodes/node_db_queries.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/prompts/__init__.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/prompts/dedupe_edges.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/prompts/dedupe_nodes.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/prompts/eval.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/prompts/extract_edge_dates.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/prompts/extract_edges.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/prompts/extract_nodes.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/prompts/invalidate_edges.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/prompts/models.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/prompts/summarize_nodes.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/py.typed +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/search/__init__.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/search/search.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/search/search_config.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/search/search_config_recipes.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/utils/__init__.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/utils/bulk_utils.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/utils/maintenance/__init__.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/utils/maintenance/community_operations.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/utils/maintenance/graph_data_operations.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/utils/maintenance/node_operations.py +0 -0
- {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/utils/maintenance/utils.py +0 -0
{graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/cross_encoder/bge_reranker_client.py
RENAMED
|
@@ -15,7 +15,6 @@ limitations under the License.
|
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
17
|
import asyncio
|
|
18
|
-
from typing import List, Tuple
|
|
19
18
|
|
|
20
19
|
from sentence_transformers import CrossEncoder
|
|
21
20
|
|
|
@@ -26,7 +25,7 @@ class BGERerankerClient(CrossEncoderClient):
|
|
|
26
25
|
def __init__(self):
|
|
27
26
|
self.model = CrossEncoder('BAAI/bge-reranker-v2-m3')
|
|
28
27
|
|
|
29
|
-
async def rank(self, query: str, passages:
|
|
28
|
+
async def rank(self, query: str, passages: list[str]) -> list[tuple[str, float]]:
|
|
30
29
|
if not passages:
|
|
31
30
|
return []
|
|
32
31
|
|
|
@@ -15,7 +15,6 @@ limitations under the License.
|
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
17
|
from abc import ABC, abstractmethod
|
|
18
|
-
from typing import List, Tuple
|
|
19
18
|
|
|
20
19
|
|
|
21
20
|
class CrossEncoderClient(ABC):
|
|
@@ -26,16 +25,16 @@ class CrossEncoderClient(ABC):
|
|
|
26
25
|
"""
|
|
27
26
|
|
|
28
27
|
@abstractmethod
|
|
29
|
-
async def rank(self, query: str, passages:
|
|
28
|
+
async def rank(self, query: str, passages: list[str]) -> list[tuple[str, float]]:
|
|
30
29
|
"""
|
|
31
30
|
Rank the given passages based on their relevance to the query.
|
|
32
31
|
|
|
33
32
|
Args:
|
|
34
33
|
query (str): The query string.
|
|
35
|
-
passages (
|
|
34
|
+
passages (list[str]): A list of passages to rank.
|
|
36
35
|
|
|
37
36
|
Returns:
|
|
38
|
-
List[
|
|
37
|
+
List[tuple[str, float]]: A list of tuples containing the passage and its score,
|
|
39
38
|
sorted in descending order of relevance.
|
|
40
39
|
"""
|
|
41
40
|
pass
|
|
@@ -23,10 +23,11 @@ from uuid import uuid4
|
|
|
23
23
|
|
|
24
24
|
from neo4j import AsyncDriver
|
|
25
25
|
from pydantic import BaseModel, Field
|
|
26
|
+
from typing_extensions import LiteralString
|
|
26
27
|
|
|
27
28
|
from graphiti_core.embedder import EmbedderClient
|
|
28
29
|
from graphiti_core.errors import EdgeNotFoundError, GroupsEdgesNotFoundError
|
|
29
|
-
from graphiti_core.helpers import DEFAULT_DATABASE, parse_db_date
|
|
30
|
+
from graphiti_core.helpers import DEFAULT_DATABASE, DEFAULT_PAGE_LIMIT, parse_db_date
|
|
30
31
|
from graphiti_core.models.edges.edge_db_queries import (
|
|
31
32
|
COMMUNITY_EDGE_SAVE,
|
|
32
33
|
ENTITY_EDGE_SAVE,
|
|
@@ -50,7 +51,7 @@ class Edge(BaseModel, ABC):
|
|
|
50
51
|
async def delete(self, driver: AsyncDriver):
|
|
51
52
|
result = await driver.execute_query(
|
|
52
53
|
"""
|
|
53
|
-
MATCH (n)-[e {uuid: $uuid}]->(m)
|
|
54
|
+
MATCH (n)-[e:MENTIONS|RELATES_TO|HAS_MEMBER {uuid: $uuid}]->(m)
|
|
54
55
|
DELETE e
|
|
55
56
|
""",
|
|
56
57
|
uuid=self.uuid,
|
|
@@ -103,6 +104,7 @@ class EpisodicEdge(Edge):
|
|
|
103
104
|
""",
|
|
104
105
|
uuid=uuid,
|
|
105
106
|
database_=DEFAULT_DATABASE,
|
|
107
|
+
routing_='r',
|
|
106
108
|
)
|
|
107
109
|
|
|
108
110
|
edges = [get_episodic_edge_from_record(record) for record in records]
|
|
@@ -126,6 +128,7 @@ class EpisodicEdge(Edge):
|
|
|
126
128
|
""",
|
|
127
129
|
uuids=uuids,
|
|
128
130
|
database_=DEFAULT_DATABASE,
|
|
131
|
+
routing_='r',
|
|
129
132
|
)
|
|
130
133
|
|
|
131
134
|
edges = [get_episodic_edge_from_record(record) for record in records]
|
|
@@ -135,20 +138,36 @@ class EpisodicEdge(Edge):
|
|
|
135
138
|
return edges
|
|
136
139
|
|
|
137
140
|
@classmethod
|
|
138
|
-
async def get_by_group_ids(
|
|
141
|
+
async def get_by_group_ids(
|
|
142
|
+
cls,
|
|
143
|
+
driver: AsyncDriver,
|
|
144
|
+
group_ids: list[str],
|
|
145
|
+
limit: int = DEFAULT_PAGE_LIMIT,
|
|
146
|
+
created_at: datetime | None = None,
|
|
147
|
+
):
|
|
148
|
+
cursor_query: LiteralString = 'AND e.created_at < $created_at' if created_at else ''
|
|
149
|
+
|
|
139
150
|
records, _, _ = await driver.execute_query(
|
|
140
151
|
"""
|
|
141
152
|
MATCH (n:Episodic)-[e:MENTIONS]->(m:Entity)
|
|
142
153
|
WHERE e.group_id IN $group_ids
|
|
154
|
+
"""
|
|
155
|
+
+ cursor_query
|
|
156
|
+
+ """
|
|
143
157
|
RETURN
|
|
144
158
|
e.uuid As uuid,
|
|
145
159
|
e.group_id AS group_id,
|
|
146
160
|
n.uuid AS source_node_uuid,
|
|
147
161
|
m.uuid AS target_node_uuid,
|
|
148
162
|
e.created_at AS created_at
|
|
163
|
+
ORDER BY e.uuid DESC
|
|
164
|
+
LIMIT $limit
|
|
149
165
|
""",
|
|
150
166
|
group_ids=group_ids,
|
|
167
|
+
created_at=created_at,
|
|
168
|
+
limit=limit,
|
|
151
169
|
database_=DEFAULT_DATABASE,
|
|
170
|
+
routing_='r',
|
|
152
171
|
)
|
|
153
172
|
|
|
154
173
|
edges = [get_episodic_edge_from_record(record) for record in records]
|
|
@@ -230,6 +249,7 @@ class EntityEdge(Edge):
|
|
|
230
249
|
""",
|
|
231
250
|
uuid=uuid,
|
|
232
251
|
database_=DEFAULT_DATABASE,
|
|
252
|
+
routing_='r',
|
|
233
253
|
)
|
|
234
254
|
|
|
235
255
|
edges = [get_entity_edge_from_record(record) for record in records]
|
|
@@ -260,6 +280,7 @@ class EntityEdge(Edge):
|
|
|
260
280
|
""",
|
|
261
281
|
uuids=uuids,
|
|
262
282
|
database_=DEFAULT_DATABASE,
|
|
283
|
+
routing_='r',
|
|
263
284
|
)
|
|
264
285
|
|
|
265
286
|
edges = [get_entity_edge_from_record(record) for record in records]
|
|
@@ -269,11 +290,22 @@ class EntityEdge(Edge):
|
|
|
269
290
|
return edges
|
|
270
291
|
|
|
271
292
|
@classmethod
|
|
272
|
-
async def get_by_group_ids(
|
|
293
|
+
async def get_by_group_ids(
|
|
294
|
+
cls,
|
|
295
|
+
driver: AsyncDriver,
|
|
296
|
+
group_ids: list[str],
|
|
297
|
+
limit: int = DEFAULT_PAGE_LIMIT,
|
|
298
|
+
created_at: datetime | None = None,
|
|
299
|
+
):
|
|
300
|
+
cursor_query: LiteralString = 'AND e.created_at < $created_at' if created_at else ''
|
|
301
|
+
|
|
273
302
|
records, _, _ = await driver.execute_query(
|
|
274
303
|
"""
|
|
275
304
|
MATCH (n:Entity)-[e:RELATES_TO]->(m:Entity)
|
|
276
305
|
WHERE e.group_id IN $group_ids
|
|
306
|
+
"""
|
|
307
|
+
+ cursor_query
|
|
308
|
+
+ """
|
|
277
309
|
RETURN
|
|
278
310
|
e.uuid AS uuid,
|
|
279
311
|
n.uuid AS source_node_uuid,
|
|
@@ -287,9 +319,14 @@ class EntityEdge(Edge):
|
|
|
287
319
|
e.expired_at AS expired_at,
|
|
288
320
|
e.valid_at AS valid_at,
|
|
289
321
|
e.invalid_at AS invalid_at
|
|
322
|
+
ORDER BY e.uuid DESC
|
|
323
|
+
LIMIT $limit
|
|
290
324
|
""",
|
|
291
325
|
group_ids=group_ids,
|
|
326
|
+
created_at=created_at,
|
|
327
|
+
limit=limit,
|
|
292
328
|
database_=DEFAULT_DATABASE,
|
|
329
|
+
routing_='r',
|
|
293
330
|
)
|
|
294
331
|
|
|
295
332
|
edges = [get_entity_edge_from_record(record) for record in records]
|
|
@@ -329,6 +366,7 @@ class CommunityEdge(Edge):
|
|
|
329
366
|
""",
|
|
330
367
|
uuid=uuid,
|
|
331
368
|
database_=DEFAULT_DATABASE,
|
|
369
|
+
routing_='r',
|
|
332
370
|
)
|
|
333
371
|
|
|
334
372
|
edges = [get_community_edge_from_record(record) for record in records]
|
|
@@ -350,6 +388,7 @@ class CommunityEdge(Edge):
|
|
|
350
388
|
""",
|
|
351
389
|
uuids=uuids,
|
|
352
390
|
database_=DEFAULT_DATABASE,
|
|
391
|
+
routing_='r',
|
|
353
392
|
)
|
|
354
393
|
|
|
355
394
|
edges = [get_community_edge_from_record(record) for record in records]
|
|
@@ -357,20 +396,36 @@ class CommunityEdge(Edge):
|
|
|
357
396
|
return edges
|
|
358
397
|
|
|
359
398
|
@classmethod
|
|
360
|
-
async def get_by_group_ids(
|
|
399
|
+
async def get_by_group_ids(
|
|
400
|
+
cls,
|
|
401
|
+
driver: AsyncDriver,
|
|
402
|
+
group_ids: list[str],
|
|
403
|
+
limit: int = DEFAULT_PAGE_LIMIT,
|
|
404
|
+
created_at: datetime | None = None,
|
|
405
|
+
):
|
|
406
|
+
cursor_query: LiteralString = 'AND e.created_at < $created_at' if created_at else ''
|
|
407
|
+
|
|
361
408
|
records, _, _ = await driver.execute_query(
|
|
362
409
|
"""
|
|
363
410
|
MATCH (n:Community)-[e:HAS_MEMBER]->(m:Entity | Community)
|
|
364
411
|
WHERE e.group_id IN $group_ids
|
|
412
|
+
"""
|
|
413
|
+
+ cursor_query
|
|
414
|
+
+ """
|
|
365
415
|
RETURN
|
|
366
416
|
e.uuid As uuid,
|
|
367
417
|
e.group_id AS group_id,
|
|
368
418
|
n.uuid AS source_node_uuid,
|
|
369
419
|
m.uuid AS target_node_uuid,
|
|
370
420
|
e.created_at AS created_at
|
|
421
|
+
ORDER BY e.uuid DESC
|
|
422
|
+
LIMIT $limit
|
|
371
423
|
""",
|
|
372
424
|
group_ids=group_ids,
|
|
425
|
+
created_at=created_at,
|
|
426
|
+
limit=limit,
|
|
373
427
|
database_=DEFAULT_DATABASE,
|
|
428
|
+
routing_='r',
|
|
374
429
|
)
|
|
375
430
|
|
|
376
431
|
edges = [get_community_edge_from_record(record) for record in records]
|
|
@@ -15,7 +15,7 @@ limitations under the License.
|
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
17
|
from abc import ABC, abstractmethod
|
|
18
|
-
from
|
|
18
|
+
from collections.abc import Iterable
|
|
19
19
|
|
|
20
20
|
from pydantic import BaseModel, Field
|
|
21
21
|
|
|
@@ -23,12 +23,12 @@ EMBEDDING_DIM = 1024
|
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
class EmbedderConfig(BaseModel):
|
|
26
|
-
embedding_dim:
|
|
26
|
+
embedding_dim: int = Field(default=EMBEDDING_DIM, frozen=True)
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
class EmbedderClient(ABC):
|
|
30
30
|
@abstractmethod
|
|
31
31
|
async def create(
|
|
32
|
-
self, input_data: str |
|
|
32
|
+
self, input_data: str | list[str] | Iterable[int] | Iterable[Iterable[int]]
|
|
33
33
|
) -> list[float]:
|
|
34
34
|
pass
|
|
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
|
-
from
|
|
17
|
+
from collections.abc import Iterable
|
|
18
18
|
|
|
19
19
|
from openai import AsyncOpenAI
|
|
20
20
|
from openai.types import EmbeddingModel
|
|
@@ -42,7 +42,7 @@ class OpenAIEmbedder(EmbedderClient):
|
|
|
42
42
|
self.client = AsyncOpenAI(api_key=config.api_key, base_url=config.base_url)
|
|
43
43
|
|
|
44
44
|
async def create(
|
|
45
|
-
self, input_data: str |
|
|
45
|
+
self, input_data: str | list[str] | Iterable[int] | Iterable[Iterable[int]]
|
|
46
46
|
) -> list[float]:
|
|
47
47
|
result = await self.client.embeddings.create(
|
|
48
48
|
input=input_data, model=self.config.embedding_model
|
|
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
|
-
from
|
|
17
|
+
from collections.abc import Iterable
|
|
18
18
|
|
|
19
19
|
import voyageai # type: ignore
|
|
20
20
|
from pydantic import Field
|
|
@@ -41,11 +41,11 @@ class VoyageAIEmbedder(EmbedderClient):
|
|
|
41
41
|
self.client = voyageai.AsyncClient(api_key=config.api_key)
|
|
42
42
|
|
|
43
43
|
async def create(
|
|
44
|
-
self, input_data: str |
|
|
44
|
+
self, input_data: str | list[str] | Iterable[int] | Iterable[Iterable[int]]
|
|
45
45
|
) -> list[float]:
|
|
46
46
|
if isinstance(input_data, str):
|
|
47
47
|
input_list = [input_data]
|
|
48
|
-
elif isinstance(input_data,
|
|
48
|
+
elif isinstance(input_data, list):
|
|
49
49
|
input_list = [str(i) for i in input_data if i]
|
|
50
50
|
else:
|
|
51
51
|
input_list = [str(i) for i in input_data if i is not None]
|
|
@@ -318,17 +318,21 @@ class Graphiti:
|
|
|
318
318
|
previous_episodes = await self.retrieve_episodes(
|
|
319
319
|
reference_time, last_n=RELEVANT_SCHEMA_LIMIT, group_ids=[group_id]
|
|
320
320
|
)
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
321
|
+
|
|
322
|
+
episode = (
|
|
323
|
+
await EpisodicNode.get_by_uuid(self.driver, uuid)
|
|
324
|
+
if uuid is not None
|
|
325
|
+
else EpisodicNode(
|
|
326
|
+
name=name,
|
|
327
|
+
group_id=group_id,
|
|
328
|
+
labels=[],
|
|
329
|
+
source=source,
|
|
330
|
+
content=episode_body,
|
|
331
|
+
source_description=source_description,
|
|
332
|
+
created_at=now,
|
|
333
|
+
valid_at=reference_time,
|
|
334
|
+
)
|
|
330
335
|
)
|
|
331
|
-
episode.uuid = uuid if uuid is not None else episode.uuid
|
|
332
336
|
|
|
333
337
|
# Extract entities as nodes
|
|
334
338
|
|
|
@@ -26,6 +26,7 @@ load_dotenv()
|
|
|
26
26
|
DEFAULT_DATABASE = os.getenv('DEFAULT_DATABASE', None)
|
|
27
27
|
USE_PARALLEL_RUNTIME = bool(os.getenv('USE_PARALLEL_RUNTIME', False))
|
|
28
28
|
MAX_REFLEXION_ITERATIONS = 2
|
|
29
|
+
DEFAULT_PAGE_LIMIT = 20
|
|
29
30
|
|
|
30
31
|
|
|
31
32
|
def parse_db_date(neo_date: neo4j_time.DateTime | None) -> datetime | None:
|
|
@@ -24,10 +24,11 @@ from uuid import uuid4
|
|
|
24
24
|
|
|
25
25
|
from neo4j import AsyncDriver
|
|
26
26
|
from pydantic import BaseModel, Field
|
|
27
|
+
from typing_extensions import LiteralString
|
|
27
28
|
|
|
28
29
|
from graphiti_core.embedder import EmbedderClient
|
|
29
30
|
from graphiti_core.errors import NodeNotFoundError
|
|
30
|
-
from graphiti_core.helpers import DEFAULT_DATABASE
|
|
31
|
+
from graphiti_core.helpers import DEFAULT_DATABASE, DEFAULT_PAGE_LIMIT
|
|
31
32
|
from graphiti_core.models.nodes.node_db_queries import (
|
|
32
33
|
COMMUNITY_NODE_SAVE,
|
|
33
34
|
ENTITY_NODE_SAVE,
|
|
@@ -86,7 +87,7 @@ class Node(BaseModel, ABC):
|
|
|
86
87
|
async def delete(self, driver: AsyncDriver):
|
|
87
88
|
result = await driver.execute_query(
|
|
88
89
|
"""
|
|
89
|
-
MATCH (n {uuid: $uuid})
|
|
90
|
+
MATCH (n:Entity|Episodic|Community {uuid: $uuid})
|
|
90
91
|
DETACH DELETE n
|
|
91
92
|
""",
|
|
92
93
|
uuid=self.uuid,
|
|
@@ -105,6 +106,19 @@ class Node(BaseModel, ABC):
|
|
|
105
106
|
return self.uuid == other.uuid
|
|
106
107
|
return False
|
|
107
108
|
|
|
109
|
+
@classmethod
|
|
110
|
+
async def delete_by_group_id(cls, driver: AsyncDriver, group_id: str):
|
|
111
|
+
await driver.execute_query(
|
|
112
|
+
"""
|
|
113
|
+
MATCH (n:Entity|Episodic|Community {group_id: $group_id})
|
|
114
|
+
DETACH DELETE n
|
|
115
|
+
""",
|
|
116
|
+
group_id=group_id,
|
|
117
|
+
database_=DEFAULT_DATABASE,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
return 'SUCCESS'
|
|
121
|
+
|
|
108
122
|
@classmethod
|
|
109
123
|
async def get_by_uuid(cls, driver: AsyncDriver, uuid: str): ...
|
|
110
124
|
|
|
@@ -159,6 +173,7 @@ class EpisodicNode(Node):
|
|
|
159
173
|
""",
|
|
160
174
|
uuid=uuid,
|
|
161
175
|
database_=DEFAULT_DATABASE,
|
|
176
|
+
routing_='r',
|
|
162
177
|
)
|
|
163
178
|
|
|
164
179
|
episodes = [get_episodic_node_from_record(record) for record in records]
|
|
@@ -185,6 +200,7 @@ class EpisodicNode(Node):
|
|
|
185
200
|
""",
|
|
186
201
|
uuids=uuids,
|
|
187
202
|
database_=DEFAULT_DATABASE,
|
|
203
|
+
routing_='r',
|
|
188
204
|
)
|
|
189
205
|
|
|
190
206
|
episodes = [get_episodic_node_from_record(record) for record in records]
|
|
@@ -192,10 +208,21 @@ class EpisodicNode(Node):
|
|
|
192
208
|
return episodes
|
|
193
209
|
|
|
194
210
|
@classmethod
|
|
195
|
-
async def get_by_group_ids(
|
|
211
|
+
async def get_by_group_ids(
|
|
212
|
+
cls,
|
|
213
|
+
driver: AsyncDriver,
|
|
214
|
+
group_ids: list[str],
|
|
215
|
+
limit: int = DEFAULT_PAGE_LIMIT,
|
|
216
|
+
created_at: datetime | None = None,
|
|
217
|
+
):
|
|
218
|
+
cursor_query: LiteralString = 'AND e.created_at < $created_at' if created_at else ''
|
|
219
|
+
|
|
196
220
|
records, _, _ = await driver.execute_query(
|
|
197
221
|
"""
|
|
198
222
|
MATCH (e:Episodic) WHERE e.group_id IN $group_ids
|
|
223
|
+
"""
|
|
224
|
+
+ cursor_query
|
|
225
|
+
+ """
|
|
199
226
|
RETURN DISTINCT
|
|
200
227
|
e.content AS content,
|
|
201
228
|
e.created_at AS created_at,
|
|
@@ -205,9 +232,14 @@ class EpisodicNode(Node):
|
|
|
205
232
|
e.group_id AS group_id,
|
|
206
233
|
e.source_description AS source_description,
|
|
207
234
|
e.source AS source
|
|
235
|
+
ORDER BY e.uuid DESC
|
|
236
|
+
LIMIT $limit
|
|
208
237
|
""",
|
|
209
238
|
group_ids=group_ids,
|
|
239
|
+
created_at=created_at,
|
|
240
|
+
limit=limit,
|
|
210
241
|
database_=DEFAULT_DATABASE,
|
|
242
|
+
routing_='r',
|
|
211
243
|
)
|
|
212
244
|
|
|
213
245
|
episodes = [get_episodic_node_from_record(record) for record in records]
|
|
@@ -259,6 +291,7 @@ class EntityNode(Node):
|
|
|
259
291
|
""",
|
|
260
292
|
uuid=uuid,
|
|
261
293
|
database_=DEFAULT_DATABASE,
|
|
294
|
+
routing_='r',
|
|
262
295
|
)
|
|
263
296
|
|
|
264
297
|
nodes = [get_entity_node_from_record(record) for record in records]
|
|
@@ -283,6 +316,7 @@ class EntityNode(Node):
|
|
|
283
316
|
""",
|
|
284
317
|
uuids=uuids,
|
|
285
318
|
database_=DEFAULT_DATABASE,
|
|
319
|
+
routing_='r',
|
|
286
320
|
)
|
|
287
321
|
|
|
288
322
|
nodes = [get_entity_node_from_record(record) for record in records]
|
|
@@ -290,10 +324,21 @@ class EntityNode(Node):
|
|
|
290
324
|
return nodes
|
|
291
325
|
|
|
292
326
|
@classmethod
|
|
293
|
-
async def get_by_group_ids(
|
|
327
|
+
async def get_by_group_ids(
|
|
328
|
+
cls,
|
|
329
|
+
driver: AsyncDriver,
|
|
330
|
+
group_ids: list[str],
|
|
331
|
+
limit: int = DEFAULT_PAGE_LIMIT,
|
|
332
|
+
created_at: datetime | None = None,
|
|
333
|
+
):
|
|
334
|
+
cursor_query: LiteralString = 'AND n.created_at < $created_at' if created_at else ''
|
|
335
|
+
|
|
294
336
|
records, _, _ = await driver.execute_query(
|
|
295
337
|
"""
|
|
296
338
|
MATCH (n:Entity) WHERE n.group_id IN $group_ids
|
|
339
|
+
"""
|
|
340
|
+
+ cursor_query
|
|
341
|
+
+ """
|
|
297
342
|
RETURN
|
|
298
343
|
n.uuid As uuid,
|
|
299
344
|
n.name AS name,
|
|
@@ -301,9 +346,14 @@ class EntityNode(Node):
|
|
|
301
346
|
n.group_id AS group_id,
|
|
302
347
|
n.created_at AS created_at,
|
|
303
348
|
n.summary AS summary
|
|
349
|
+
ORDER BY n.uuid DESC
|
|
350
|
+
LIMIT $limit
|
|
304
351
|
""",
|
|
305
352
|
group_ids=group_ids,
|
|
353
|
+
created_at=created_at,
|
|
354
|
+
limit=limit,
|
|
306
355
|
database_=DEFAULT_DATABASE,
|
|
356
|
+
routing_='r',
|
|
307
357
|
)
|
|
308
358
|
|
|
309
359
|
nodes = [get_entity_node_from_record(record) for record in records]
|
|
@@ -355,6 +405,7 @@ class CommunityNode(Node):
|
|
|
355
405
|
""",
|
|
356
406
|
uuid=uuid,
|
|
357
407
|
database_=DEFAULT_DATABASE,
|
|
408
|
+
routing_='r',
|
|
358
409
|
)
|
|
359
410
|
|
|
360
411
|
nodes = [get_community_node_from_record(record) for record in records]
|
|
@@ -379,6 +430,7 @@ class CommunityNode(Node):
|
|
|
379
430
|
""",
|
|
380
431
|
uuids=uuids,
|
|
381
432
|
database_=DEFAULT_DATABASE,
|
|
433
|
+
routing_='r',
|
|
382
434
|
)
|
|
383
435
|
|
|
384
436
|
communities = [get_community_node_from_record(record) for record in records]
|
|
@@ -386,10 +438,21 @@ class CommunityNode(Node):
|
|
|
386
438
|
return communities
|
|
387
439
|
|
|
388
440
|
@classmethod
|
|
389
|
-
async def get_by_group_ids(
|
|
441
|
+
async def get_by_group_ids(
|
|
442
|
+
cls,
|
|
443
|
+
driver: AsyncDriver,
|
|
444
|
+
group_ids: list[str],
|
|
445
|
+
limit: int = DEFAULT_PAGE_LIMIT,
|
|
446
|
+
created_at: datetime | None = None,
|
|
447
|
+
):
|
|
448
|
+
cursor_query: LiteralString = 'AND n.created_at < $created_at' if created_at else ''
|
|
449
|
+
|
|
390
450
|
records, _, _ = await driver.execute_query(
|
|
391
451
|
"""
|
|
392
452
|
MATCH (n:Community) WHERE n.group_id IN $group_ids
|
|
453
|
+
"""
|
|
454
|
+
+ cursor_query
|
|
455
|
+
+ """
|
|
393
456
|
RETURN
|
|
394
457
|
n.uuid As uuid,
|
|
395
458
|
n.name AS name,
|
|
@@ -397,9 +460,14 @@ class CommunityNode(Node):
|
|
|
397
460
|
n.group_id AS group_id,
|
|
398
461
|
n.created_at AS created_at,
|
|
399
462
|
n.summary AS summary
|
|
463
|
+
ORDER BY n.uuid DESC
|
|
464
|
+
LIMIT $limit
|
|
400
465
|
""",
|
|
401
466
|
group_ids=group_ids,
|
|
467
|
+
created_at=created_at,
|
|
468
|
+
limit=limit,
|
|
402
469
|
database_=DEFAULT_DATABASE,
|
|
470
|
+
routing_='r',
|
|
403
471
|
)
|
|
404
472
|
|
|
405
473
|
communities = [get_community_node_from_record(record) for record in records]
|
|
@@ -74,6 +74,7 @@ from .invalidate_edges import (
|
|
|
74
74
|
versions as invalidate_edges_versions,
|
|
75
75
|
)
|
|
76
76
|
from .models import Message, PromptFunction
|
|
77
|
+
from .prompt_helpers import DO_NOT_ESCAPE_UNICODE
|
|
77
78
|
from .summarize_nodes import Prompt as SummarizeNodesPrompt
|
|
78
79
|
from .summarize_nodes import Versions as SummarizeNodesVersions
|
|
79
80
|
from .summarize_nodes import versions as summarize_nodes_versions
|
|
@@ -106,7 +107,10 @@ class VersionWrapper:
|
|
|
106
107
|
self.func = func
|
|
107
108
|
|
|
108
109
|
def __call__(self, context: dict[str, Any]) -> list[Message]:
|
|
109
|
-
|
|
110
|
+
messages = self.func(context)
|
|
111
|
+
for message in messages:
|
|
112
|
+
message.content += DO_NOT_ESCAPE_UNICODE if message.role == 'system' else ''
|
|
113
|
+
return messages
|
|
110
114
|
|
|
111
115
|
|
|
112
116
|
class PromptTypeWrapper:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
DO_NOT_ESCAPE_UNICODE = '\nDo not escape unicode characters.\n'
|
{graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/utils/maintenance/edge_operations.py
RENAMED
|
@@ -18,7 +18,6 @@ import asyncio
|
|
|
18
18
|
import logging
|
|
19
19
|
from datetime import datetime, timezone
|
|
20
20
|
from time import time
|
|
21
|
-
from typing import List
|
|
22
21
|
|
|
23
22
|
from graphiti_core.edges import CommunityEdge, EntityEdge, EpisodicEdge
|
|
24
23
|
from graphiti_core.helpers import MAX_REFLEXION_ITERATIONS
|
|
@@ -34,11 +33,11 @@ logger = logging.getLogger(__name__)
|
|
|
34
33
|
|
|
35
34
|
|
|
36
35
|
def build_episodic_edges(
|
|
37
|
-
entity_nodes:
|
|
36
|
+
entity_nodes: list[EntityNode],
|
|
38
37
|
episode: EpisodicNode,
|
|
39
38
|
created_at: datetime,
|
|
40
|
-
) ->
|
|
41
|
-
edges:
|
|
39
|
+
) -> list[EpisodicEdge]:
|
|
40
|
+
edges: list[EpisodicEdge] = [
|
|
42
41
|
EpisodicEdge(
|
|
43
42
|
source_node_uuid=episode.uuid,
|
|
44
43
|
target_node_uuid=node.uuid,
|
|
@@ -52,11 +51,11 @@ def build_episodic_edges(
|
|
|
52
51
|
|
|
53
52
|
|
|
54
53
|
def build_community_edges(
|
|
55
|
-
entity_nodes:
|
|
54
|
+
entity_nodes: list[EntityNode],
|
|
56
55
|
community_node: CommunityNode,
|
|
57
56
|
created_at: datetime,
|
|
58
|
-
) ->
|
|
59
|
-
edges:
|
|
57
|
+
) -> list[CommunityEdge]:
|
|
58
|
+
edges: list[CommunityEdge] = [
|
|
60
59
|
CommunityEdge(
|
|
61
60
|
source_node_uuid=community_node.uuid,
|
|
62
61
|
target_node_uuid=node.uuid,
|
{graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/utils/maintenance/temporal_operations.py
RENAMED
|
@@ -17,7 +17,6 @@ limitations under the License.
|
|
|
17
17
|
import logging
|
|
18
18
|
from datetime import datetime
|
|
19
19
|
from time import time
|
|
20
|
-
from typing import List
|
|
21
20
|
|
|
22
21
|
from graphiti_core.edges import EntityEdge
|
|
23
22
|
from graphiti_core.llm_client import LLMClient
|
|
@@ -31,7 +30,7 @@ async def extract_edge_dates(
|
|
|
31
30
|
llm_client: LLMClient,
|
|
32
31
|
edge: EntityEdge,
|
|
33
32
|
current_episode: EpisodicNode,
|
|
34
|
-
previous_episodes:
|
|
33
|
+
previous_episodes: list[EpisodicNode],
|
|
35
34
|
) -> tuple[datetime | None, datetime | None]:
|
|
36
35
|
context = {
|
|
37
36
|
'edge_fact': edge.fact,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/cross_encoder/openai_reranker_client.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/utils/maintenance/community_operations.py
RENAMED
|
File without changes
|
{graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/utils/maintenance/graph_data_operations.py
RENAMED
|
File without changes
|
{graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/utils/maintenance/node_operations.py
RENAMED
|
File without changes
|
|
File without changes
|