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.

Files changed (58) hide show
  1. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/PKG-INFO +1 -1
  2. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/cross_encoder/bge_reranker_client.py +1 -2
  3. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/cross_encoder/client.py +3 -4
  4. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/edges.py +60 -5
  5. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/embedder/client.py +3 -3
  6. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/embedder/openai.py +2 -2
  7. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/embedder/voyage.py +3 -3
  8. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/graphiti.py +14 -10
  9. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/helpers.py +1 -0
  10. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/nodes.py +73 -5
  11. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/prompts/lib.py +5 -1
  12. graphiti_core-0.4.3/graphiti_core/prompts/prompt_helpers.py +1 -0
  13. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/search/search_utils.py +1 -1
  14. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/utils/maintenance/edge_operations.py +6 -7
  15. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/utils/maintenance/temporal_operations.py +1 -2
  16. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/pyproject.toml +1 -1
  17. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/LICENSE +0 -0
  18. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/README.md +0 -0
  19. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/__init__.py +0 -0
  20. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/cross_encoder/__init__.py +0 -0
  21. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/cross_encoder/openai_reranker_client.py +0 -0
  22. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/embedder/__init__.py +0 -0
  23. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/errors.py +0 -0
  24. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/llm_client/__init__.py +0 -0
  25. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/llm_client/anthropic_client.py +0 -0
  26. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/llm_client/client.py +0 -0
  27. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/llm_client/config.py +0 -0
  28. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/llm_client/errors.py +0 -0
  29. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/llm_client/groq_client.py +0 -0
  30. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/llm_client/openai_client.py +0 -0
  31. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/llm_client/utils.py +0 -0
  32. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/models/__init__.py +0 -0
  33. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/models/edges/__init__.py +0 -0
  34. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/models/edges/edge_db_queries.py +0 -0
  35. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/models/nodes/__init__.py +0 -0
  36. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/models/nodes/node_db_queries.py +0 -0
  37. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/prompts/__init__.py +0 -0
  38. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/prompts/dedupe_edges.py +0 -0
  39. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/prompts/dedupe_nodes.py +0 -0
  40. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/prompts/eval.py +0 -0
  41. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/prompts/extract_edge_dates.py +0 -0
  42. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/prompts/extract_edges.py +0 -0
  43. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/prompts/extract_nodes.py +0 -0
  44. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/prompts/invalidate_edges.py +0 -0
  45. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/prompts/models.py +0 -0
  46. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/prompts/summarize_nodes.py +0 -0
  47. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/py.typed +0 -0
  48. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/search/__init__.py +0 -0
  49. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/search/search.py +0 -0
  50. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/search/search_config.py +0 -0
  51. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/search/search_config_recipes.py +0 -0
  52. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/utils/__init__.py +0 -0
  53. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/utils/bulk_utils.py +0 -0
  54. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/utils/maintenance/__init__.py +0 -0
  55. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/utils/maintenance/community_operations.py +0 -0
  56. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/utils/maintenance/graph_data_operations.py +0 -0
  57. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/utils/maintenance/node_operations.py +0 -0
  58. {graphiti_core-0.4.1 → graphiti_core-0.4.3}/graphiti_core/utils/maintenance/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: graphiti-core
3
- Version: 0.4.1
3
+ Version: 0.4.3
4
4
  Summary: A temporal graph building library
5
5
  License: Apache-2.0
6
6
  Author: Paul Paliychuk
@@ -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: List[str]) -> List[Tuple[str, float]]:
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: List[str]) -> List[Tuple[str, float]]:
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 (List[str]): A list of passages to rank.
34
+ passages (list[str]): A list of passages to rank.
36
35
 
37
36
  Returns:
38
- List[Tuple[str, float]]: A list of tuples containing the passage and its score,
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(cls, driver: AsyncDriver, group_ids: list[str]):
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(cls, driver: AsyncDriver, group_ids: list[str]):
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(cls, driver: AsyncDriver, group_ids: list[str]):
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 typing import Iterable, List, Literal
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: Literal[1024] = Field(default=EMBEDDING_DIM, frozen=True)
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 | List[str] | Iterable[int] | Iterable[Iterable[int]]
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 typing import Iterable, List
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 | List[str] | Iterable[int] | Iterable[Iterable[int]]
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 typing import Iterable, List
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 | List[str] | Iterable[int] | Iterable[Iterable[int]]
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, List):
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
- episode = EpisodicNode(
322
- name=name,
323
- group_id=group_id,
324
- labels=[],
325
- source=source,
326
- content=episode_body,
327
- source_description=source_description,
328
- created_at=now,
329
- valid_at=reference_time,
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(cls, driver: AsyncDriver, group_ids: list[str]):
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(cls, driver: AsyncDriver, group_ids: list[str]):
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(cls, driver: AsyncDriver, group_ids: list[str]):
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
- return self.func(context)
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'
@@ -40,7 +40,7 @@ from graphiti_core.nodes import (
40
40
 
41
41
  logger = logging.getLogger(__name__)
42
42
 
43
- RELEVANT_SCHEMA_LIMIT = 3
43
+ RELEVANT_SCHEMA_LIMIT = 10
44
44
  DEFAULT_MIN_SCORE = 0.6
45
45
  DEFAULT_MMR_LAMBDA = 0.5
46
46
  MAX_SEARCH_DEPTH = 3
@@ -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: List[EntityNode],
36
+ entity_nodes: list[EntityNode],
38
37
  episode: EpisodicNode,
39
38
  created_at: datetime,
40
- ) -> List[EpisodicEdge]:
41
- edges: List[EpisodicEdge] = [
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: List[EntityNode],
54
+ entity_nodes: list[EntityNode],
56
55
  community_node: CommunityNode,
57
56
  created_at: datetime,
58
- ) -> List[CommunityEdge]:
59
- edges: List[CommunityEdge] = [
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,
@@ -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: List[EpisodicNode],
33
+ previous_episodes: list[EpisodicNode],
35
34
  ) -> tuple[datetime | None, datetime | None]:
36
35
  context = {
37
36
  'edge_fact': edge.fact,
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "graphiti-core"
3
- version = "0.4.1"
3
+ version = "0.4.3"
4
4
  description = "A temporal graph building library"
5
5
  authors = [
6
6
  "Paul Paliychuk <paul@getzep.com>",
File without changes
File without changes