graphiti-core 0.13.0__tar.gz → 0.13.1__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.13.0 → graphiti_core-0.13.1}/PKG-INFO +51 -2
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/README.md +49 -0
- graphiti_core-0.13.1/graphiti_core/llm_client/azure_openai_client.py +77 -0
- graphiti_core-0.13.0/graphiti_core/llm_client/openai_client.py → graphiti_core-0.13.1/graphiti_core/llm_client/openai_base_client.py +88 -62
- graphiti_core-0.13.1/graphiti_core/llm_client/openai_client.py +95 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/search/search_utils.py +11 -11
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/pyproject.toml +2 -2
- graphiti_core-0.13.0/graphiti_core/llm_client/azure_openai_client.py +0 -73
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/LICENSE +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/__init__.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/cross_encoder/__init__.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/cross_encoder/bge_reranker_client.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/cross_encoder/client.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/cross_encoder/openai_reranker_client.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/driver/__init__.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/driver/driver.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/driver/falkordb_driver.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/driver/neo4j_driver.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/edges.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/embedder/__init__.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/embedder/azure_openai.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/embedder/client.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/embedder/gemini.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/embedder/openai.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/embedder/voyage.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/errors.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/graph_queries.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/graphiti.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/graphiti_types.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/helpers.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/llm_client/__init__.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/llm_client/anthropic_client.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/llm_client/client.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/llm_client/config.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/llm_client/errors.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/llm_client/gemini_client.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/llm_client/groq_client.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/llm_client/openai_generic_client.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/llm_client/utils.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/models/__init__.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/models/edges/__init__.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/models/edges/edge_db_queries.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/models/nodes/__init__.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/models/nodes/node_db_queries.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/nodes.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/prompts/__init__.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/prompts/dedupe_edges.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/prompts/dedupe_nodes.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/prompts/eval.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/prompts/extract_edge_dates.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/prompts/extract_edges.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/prompts/extract_nodes.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/prompts/invalidate_edges.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/prompts/lib.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/prompts/models.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/prompts/prompt_helpers.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/prompts/summarize_nodes.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/py.typed +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/search/__init__.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/search/search.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/search/search_config.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/search/search_config_recipes.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/search/search_filters.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/search/search_helpers.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/utils/__init__.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/utils/bulk_utils.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/utils/datetime_utils.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/utils/maintenance/__init__.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/utils/maintenance/community_operations.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/utils/maintenance/edge_operations.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/utils/maintenance/graph_data_operations.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/utils/maintenance/node_operations.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/utils/maintenance/temporal_operations.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/utils/maintenance/utils.py +0 -0
- {graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/utils/ontology_utils/entity_types_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: graphiti-core
|
|
3
|
-
Version: 0.13.
|
|
3
|
+
Version: 0.13.1
|
|
4
4
|
Summary: A temporal graph building library
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Author: Paul Paliychuk
|
|
@@ -23,7 +23,7 @@ Requires-Dist: google-genai (>=1.8.0) ; extra == "google-genai"
|
|
|
23
23
|
Requires-Dist: groq (>=0.2.0) ; extra == "groq"
|
|
24
24
|
Requires-Dist: neo4j (>=5.26.0)
|
|
25
25
|
Requires-Dist: numpy (>=1.0.0)
|
|
26
|
-
Requires-Dist: openai (>=1.
|
|
26
|
+
Requires-Dist: openai (>=1.91.0)
|
|
27
27
|
Requires-Dist: pydantic (>=2.11.5)
|
|
28
28
|
Requires-Dist: python-dotenv (>=1.0.1)
|
|
29
29
|
Requires-Dist: tenacity (>=9.0.0)
|
|
@@ -331,6 +331,55 @@ graphiti = Graphiti(
|
|
|
331
331
|
# Now you can use Graphiti with Google Gemini
|
|
332
332
|
```
|
|
333
333
|
|
|
334
|
+
## Using Graphiti with Ollama (Local LLM)
|
|
335
|
+
|
|
336
|
+
Graphiti supports Ollama for running local LLMs and embedding models via Ollama's OpenAI-compatible API. This is ideal for privacy-focused applications or when you want to avoid API costs.
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
Install the models:
|
|
340
|
+
ollama pull deepseek-r1:7b # LLM
|
|
341
|
+
ollama pull nomic-embed-text # embeddings
|
|
342
|
+
|
|
343
|
+
```python
|
|
344
|
+
from graphiti_core import Graphiti
|
|
345
|
+
from graphiti_core.llm_client.config import LLMConfig
|
|
346
|
+
from graphiti_core.llm_client.openai_client import OpenAIClient
|
|
347
|
+
from graphiti_core.embedder.openai import OpenAIEmbedder, OpenAIEmbedderConfig
|
|
348
|
+
from graphiti_core.cross_encoder.openai_reranker_client import OpenAIRerankerClient
|
|
349
|
+
|
|
350
|
+
# Configure Ollama LLM client
|
|
351
|
+
llm_config = LLMConfig(
|
|
352
|
+
api_key="abc", # Ollama doesn't require a real API key
|
|
353
|
+
model="deepseek-r1:7b",
|
|
354
|
+
small_model="deepseek-r1:7b",
|
|
355
|
+
base_url="http://localhost:11434/v1", # Ollama provides this port
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
llm_client = OpenAIClient(config=llm_config)
|
|
359
|
+
|
|
360
|
+
# Initialize Graphiti with Ollama clients
|
|
361
|
+
graphiti = Graphiti(
|
|
362
|
+
"bolt://localhost:7687",
|
|
363
|
+
"neo4j",
|
|
364
|
+
"password",
|
|
365
|
+
llm_client=llm_client,
|
|
366
|
+
embedder=OpenAIEmbedder(
|
|
367
|
+
config=OpenAIEmbedderConfig(
|
|
368
|
+
api_key="abc",
|
|
369
|
+
embedding_model="nomic-embed-text",
|
|
370
|
+
embedding_dim=768,
|
|
371
|
+
base_url="http://localhost:11434/v1",
|
|
372
|
+
)
|
|
373
|
+
),
|
|
374
|
+
cross_encoder=OpenAIRerankerClient(client=llm_client, config=llm_config),
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
# Now you can use Graphiti with local Ollama models
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
Ensure Ollama is running (`ollama serve`) and that you have pulled the models you want to use.
|
|
381
|
+
|
|
382
|
+
|
|
334
383
|
## Documentation
|
|
335
384
|
|
|
336
385
|
- [Guides and API documentation](https://help.getzep.com/graphiti).
|
|
@@ -298,6 +298,55 @@ graphiti = Graphiti(
|
|
|
298
298
|
# Now you can use Graphiti with Google Gemini
|
|
299
299
|
```
|
|
300
300
|
|
|
301
|
+
## Using Graphiti with Ollama (Local LLM)
|
|
302
|
+
|
|
303
|
+
Graphiti supports Ollama for running local LLMs and embedding models via Ollama's OpenAI-compatible API. This is ideal for privacy-focused applications or when you want to avoid API costs.
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
Install the models:
|
|
307
|
+
ollama pull deepseek-r1:7b # LLM
|
|
308
|
+
ollama pull nomic-embed-text # embeddings
|
|
309
|
+
|
|
310
|
+
```python
|
|
311
|
+
from graphiti_core import Graphiti
|
|
312
|
+
from graphiti_core.llm_client.config import LLMConfig
|
|
313
|
+
from graphiti_core.llm_client.openai_client import OpenAIClient
|
|
314
|
+
from graphiti_core.embedder.openai import OpenAIEmbedder, OpenAIEmbedderConfig
|
|
315
|
+
from graphiti_core.cross_encoder.openai_reranker_client import OpenAIRerankerClient
|
|
316
|
+
|
|
317
|
+
# Configure Ollama LLM client
|
|
318
|
+
llm_config = LLMConfig(
|
|
319
|
+
api_key="abc", # Ollama doesn't require a real API key
|
|
320
|
+
model="deepseek-r1:7b",
|
|
321
|
+
small_model="deepseek-r1:7b",
|
|
322
|
+
base_url="http://localhost:11434/v1", # Ollama provides this port
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
llm_client = OpenAIClient(config=llm_config)
|
|
326
|
+
|
|
327
|
+
# Initialize Graphiti with Ollama clients
|
|
328
|
+
graphiti = Graphiti(
|
|
329
|
+
"bolt://localhost:7687",
|
|
330
|
+
"neo4j",
|
|
331
|
+
"password",
|
|
332
|
+
llm_client=llm_client,
|
|
333
|
+
embedder=OpenAIEmbedder(
|
|
334
|
+
config=OpenAIEmbedderConfig(
|
|
335
|
+
api_key="abc",
|
|
336
|
+
embedding_model="nomic-embed-text",
|
|
337
|
+
embedding_dim=768,
|
|
338
|
+
base_url="http://localhost:11434/v1",
|
|
339
|
+
)
|
|
340
|
+
),
|
|
341
|
+
cross_encoder=OpenAIRerankerClient(client=llm_client, config=llm_config),
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
# Now you can use Graphiti with local Ollama models
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
Ensure Ollama is running (`ollama serve`) and that you have pulled the models you want to use.
|
|
348
|
+
|
|
349
|
+
|
|
301
350
|
## Documentation
|
|
302
351
|
|
|
303
352
|
- [Guides and API documentation](https://help.getzep.com/graphiti).
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright 2024, Zep Software, Inc.
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import logging
|
|
18
|
+
from typing import ClassVar
|
|
19
|
+
|
|
20
|
+
from openai import AsyncAzureOpenAI
|
|
21
|
+
from openai.types.chat import ChatCompletionMessageParam
|
|
22
|
+
from pydantic import BaseModel
|
|
23
|
+
|
|
24
|
+
from .config import DEFAULT_MAX_TOKENS, LLMConfig
|
|
25
|
+
from .openai_base_client import BaseOpenAIClient
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class AzureOpenAILLMClient(BaseOpenAIClient):
|
|
31
|
+
"""Wrapper class for AsyncAzureOpenAI that implements the LLMClient interface."""
|
|
32
|
+
|
|
33
|
+
# Class-level constants
|
|
34
|
+
MAX_RETRIES: ClassVar[int] = 2
|
|
35
|
+
|
|
36
|
+
def __init__(
|
|
37
|
+
self,
|
|
38
|
+
azure_client: AsyncAzureOpenAI,
|
|
39
|
+
config: LLMConfig | None = None,
|
|
40
|
+
max_tokens: int = DEFAULT_MAX_TOKENS,
|
|
41
|
+
):
|
|
42
|
+
super().__init__(config, cache=False, max_tokens=max_tokens)
|
|
43
|
+
self.client = azure_client
|
|
44
|
+
|
|
45
|
+
async def _create_structured_completion(
|
|
46
|
+
self,
|
|
47
|
+
model: str,
|
|
48
|
+
messages: list[ChatCompletionMessageParam],
|
|
49
|
+
temperature: float | None,
|
|
50
|
+
max_tokens: int,
|
|
51
|
+
response_model: type[BaseModel],
|
|
52
|
+
):
|
|
53
|
+
"""Create a structured completion using Azure OpenAI's beta parse API."""
|
|
54
|
+
return await self.client.beta.chat.completions.parse(
|
|
55
|
+
model=model,
|
|
56
|
+
messages=messages,
|
|
57
|
+
temperature=temperature,
|
|
58
|
+
max_tokens=max_tokens,
|
|
59
|
+
response_format=response_model, # type: ignore
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
async def _create_completion(
|
|
63
|
+
self,
|
|
64
|
+
model: str,
|
|
65
|
+
messages: list[ChatCompletionMessageParam],
|
|
66
|
+
temperature: float | None,
|
|
67
|
+
max_tokens: int,
|
|
68
|
+
response_model: type[BaseModel] | None = None,
|
|
69
|
+
):
|
|
70
|
+
"""Create a regular completion with JSON format using Azure OpenAI."""
|
|
71
|
+
return await self.client.chat.completions.create(
|
|
72
|
+
model=model,
|
|
73
|
+
messages=messages,
|
|
74
|
+
temperature=temperature,
|
|
75
|
+
max_tokens=max_tokens,
|
|
76
|
+
response_format={'type': 'json_object'},
|
|
77
|
+
)
|
|
@@ -14,12 +14,13 @@ See the License for the specific language governing permissions and
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
|
+
import json
|
|
17
18
|
import logging
|
|
18
19
|
import typing
|
|
19
|
-
from
|
|
20
|
+
from abc import abstractmethod
|
|
21
|
+
from typing import Any, ClassVar
|
|
20
22
|
|
|
21
23
|
import openai
|
|
22
|
-
from openai import AsyncOpenAI
|
|
23
24
|
from openai.types.chat import ChatCompletionMessageParam
|
|
24
25
|
from pydantic import BaseModel
|
|
25
26
|
|
|
@@ -34,25 +35,12 @@ DEFAULT_MODEL = 'gpt-4.1-mini'
|
|
|
34
35
|
DEFAULT_SMALL_MODEL = 'gpt-4.1-nano'
|
|
35
36
|
|
|
36
37
|
|
|
37
|
-
class
|
|
38
|
+
class BaseOpenAIClient(LLMClient):
|
|
38
39
|
"""
|
|
39
|
-
|
|
40
|
+
Base client class for OpenAI-compatible APIs (OpenAI and Azure OpenAI).
|
|
40
41
|
|
|
41
|
-
This class
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
Attributes:
|
|
45
|
-
client (AsyncOpenAI): The OpenAI client used to interact with the API.
|
|
46
|
-
model (str): The model name to use for generating responses.
|
|
47
|
-
temperature (float): The temperature to use for generating responses.
|
|
48
|
-
max_tokens (int): The maximum number of tokens to generate in a response.
|
|
49
|
-
|
|
50
|
-
Methods:
|
|
51
|
-
__init__(config: LLMConfig | None = None, cache: bool = False, client: typing.Any = None):
|
|
52
|
-
Initializes the OpenAIClient with the provided configuration, cache setting, and client.
|
|
53
|
-
|
|
54
|
-
_generate_response(messages: list[Message]) -> dict[str, typing.Any]:
|
|
55
|
-
Generates a response from the language model based on the provided messages.
|
|
42
|
+
This class contains shared logic for both OpenAI and Azure OpenAI clients,
|
|
43
|
+
reducing code duplication while allowing for implementation-specific differences.
|
|
56
44
|
"""
|
|
57
45
|
|
|
58
46
|
# Class-level constants
|
|
@@ -62,41 +50,45 @@ class OpenAIClient(LLMClient):
|
|
|
62
50
|
self,
|
|
63
51
|
config: LLMConfig | None = None,
|
|
64
52
|
cache: bool = False,
|
|
65
|
-
client: typing.Any = None,
|
|
66
53
|
max_tokens: int = DEFAULT_MAX_TOKENS,
|
|
67
54
|
):
|
|
68
|
-
"""
|
|
69
|
-
Initialize the OpenAIClient with the provided configuration, cache setting, and client.
|
|
70
|
-
|
|
71
|
-
Args:
|
|
72
|
-
config (LLMConfig | None): The configuration for the LLM client, including API key, model, base URL, temperature, and max tokens.
|
|
73
|
-
cache (bool): Whether to use caching for responses. Defaults to False.
|
|
74
|
-
client (Any | None): An optional async client instance to use. If not provided, a new AsyncOpenAI client is created.
|
|
75
|
-
|
|
76
|
-
"""
|
|
77
|
-
# removed caching to simplify the `generate_response` override
|
|
78
55
|
if cache:
|
|
79
|
-
raise NotImplementedError('Caching is not implemented for OpenAI')
|
|
56
|
+
raise NotImplementedError('Caching is not implemented for OpenAI-based clients')
|
|
80
57
|
|
|
81
58
|
if config is None:
|
|
82
59
|
config = LLMConfig()
|
|
83
60
|
|
|
84
61
|
super().__init__(config, cache)
|
|
85
|
-
|
|
86
|
-
if client is None:
|
|
87
|
-
self.client = AsyncOpenAI(api_key=config.api_key, base_url=config.base_url)
|
|
88
|
-
else:
|
|
89
|
-
self.client = client
|
|
90
|
-
|
|
91
62
|
self.max_tokens = max_tokens
|
|
92
63
|
|
|
93
|
-
|
|
64
|
+
@abstractmethod
|
|
65
|
+
async def _create_completion(
|
|
94
66
|
self,
|
|
95
|
-
|
|
67
|
+
model: str,
|
|
68
|
+
messages: list[ChatCompletionMessageParam],
|
|
69
|
+
temperature: float | None,
|
|
70
|
+
max_tokens: int,
|
|
96
71
|
response_model: type[BaseModel] | None = None,
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
72
|
+
) -> Any:
|
|
73
|
+
"""Create a completion using the specific client implementation."""
|
|
74
|
+
pass
|
|
75
|
+
|
|
76
|
+
@abstractmethod
|
|
77
|
+
async def _create_structured_completion(
|
|
78
|
+
self,
|
|
79
|
+
model: str,
|
|
80
|
+
messages: list[ChatCompletionMessageParam],
|
|
81
|
+
temperature: float | None,
|
|
82
|
+
max_tokens: int,
|
|
83
|
+
response_model: type[BaseModel],
|
|
84
|
+
) -> Any:
|
|
85
|
+
"""Create a structured completion using the specific client implementation."""
|
|
86
|
+
pass
|
|
87
|
+
|
|
88
|
+
def _convert_messages_to_openai_format(
|
|
89
|
+
self, messages: list[Message]
|
|
90
|
+
) -> list[ChatCompletionMessageParam]:
|
|
91
|
+
"""Convert internal Message format to OpenAI ChatCompletionMessageParam format."""
|
|
100
92
|
openai_messages: list[ChatCompletionMessageParam] = []
|
|
101
93
|
for m in messages:
|
|
102
94
|
m.content = self._clean_input(m.content)
|
|
@@ -104,28 +96,61 @@ class OpenAIClient(LLMClient):
|
|
|
104
96
|
openai_messages.append({'role': 'user', 'content': m.content})
|
|
105
97
|
elif m.role == 'system':
|
|
106
98
|
openai_messages.append({'role': 'system', 'content': m.content})
|
|
99
|
+
return openai_messages
|
|
100
|
+
|
|
101
|
+
def _get_model_for_size(self, model_size: ModelSize) -> str:
|
|
102
|
+
"""Get the appropriate model name based on the requested size."""
|
|
103
|
+
if model_size == ModelSize.small:
|
|
104
|
+
return self.small_model or DEFAULT_SMALL_MODEL
|
|
105
|
+
else:
|
|
106
|
+
return self.model or DEFAULT_MODEL
|
|
107
|
+
|
|
108
|
+
def _handle_structured_response(self, response: Any) -> dict[str, Any]:
|
|
109
|
+
"""Handle structured response parsing and validation."""
|
|
110
|
+
response_object = response.choices[0].message
|
|
111
|
+
|
|
112
|
+
if response_object.parsed:
|
|
113
|
+
return response_object.parsed.model_dump()
|
|
114
|
+
elif response_object.refusal:
|
|
115
|
+
raise RefusalError(response_object.refusal)
|
|
116
|
+
else:
|
|
117
|
+
raise Exception(f'Invalid response from LLM: {response_object.model_dump()}')
|
|
118
|
+
|
|
119
|
+
def _handle_json_response(self, response: Any) -> dict[str, Any]:
|
|
120
|
+
"""Handle JSON response parsing."""
|
|
121
|
+
result = response.choices[0].message.content or '{}'
|
|
122
|
+
return json.loads(result)
|
|
123
|
+
|
|
124
|
+
async def _generate_response(
|
|
125
|
+
self,
|
|
126
|
+
messages: list[Message],
|
|
127
|
+
response_model: type[BaseModel] | None = None,
|
|
128
|
+
max_tokens: int = DEFAULT_MAX_TOKENS,
|
|
129
|
+
model_size: ModelSize = ModelSize.medium,
|
|
130
|
+
) -> dict[str, Any]:
|
|
131
|
+
"""Generate a response using the appropriate client implementation."""
|
|
132
|
+
openai_messages = self._convert_messages_to_openai_format(messages)
|
|
133
|
+
model = self._get_model_for_size(model_size)
|
|
134
|
+
|
|
107
135
|
try:
|
|
108
|
-
if
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
max_tokens=max_tokens or self.max_tokens,
|
|
118
|
-
response_format=response_model, # type: ignore
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
response_object = response.choices[0].message
|
|
122
|
-
|
|
123
|
-
if response_object.parsed:
|
|
124
|
-
return response_object.parsed.model_dump()
|
|
125
|
-
elif response_object.refusal:
|
|
126
|
-
raise RefusalError(response_object.refusal)
|
|
136
|
+
if response_model:
|
|
137
|
+
response = await self._create_structured_completion(
|
|
138
|
+
model=model,
|
|
139
|
+
messages=openai_messages,
|
|
140
|
+
temperature=self.temperature,
|
|
141
|
+
max_tokens=max_tokens or self.max_tokens,
|
|
142
|
+
response_model=response_model,
|
|
143
|
+
)
|
|
144
|
+
return self._handle_structured_response(response)
|
|
127
145
|
else:
|
|
128
|
-
|
|
146
|
+
response = await self._create_completion(
|
|
147
|
+
model=model,
|
|
148
|
+
messages=openai_messages,
|
|
149
|
+
temperature=self.temperature,
|
|
150
|
+
max_tokens=max_tokens or self.max_tokens,
|
|
151
|
+
)
|
|
152
|
+
return self._handle_json_response(response)
|
|
153
|
+
|
|
129
154
|
except openai.LengthFinishReasonError as e:
|
|
130
155
|
raise Exception(f'Output length exceeded max tokens {self.max_tokens}: {e}') from e
|
|
131
156
|
except openai.RateLimitError as e:
|
|
@@ -141,6 +166,7 @@ class OpenAIClient(LLMClient):
|
|
|
141
166
|
max_tokens: int | None = None,
|
|
142
167
|
model_size: ModelSize = ModelSize.medium,
|
|
143
168
|
) -> dict[str, typing.Any]:
|
|
169
|
+
"""Generate a response with retry logic and error handling."""
|
|
144
170
|
if max_tokens is None:
|
|
145
171
|
max_tokens = self.max_tokens
|
|
146
172
|
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright 2024, Zep Software, Inc.
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import typing
|
|
18
|
+
|
|
19
|
+
from openai import AsyncOpenAI
|
|
20
|
+
from openai.types.chat import ChatCompletionMessageParam
|
|
21
|
+
from pydantic import BaseModel
|
|
22
|
+
|
|
23
|
+
from .config import DEFAULT_MAX_TOKENS, LLMConfig
|
|
24
|
+
from .openai_base_client import BaseOpenAIClient
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class OpenAIClient(BaseOpenAIClient):
|
|
28
|
+
"""
|
|
29
|
+
OpenAIClient is a client class for interacting with OpenAI's language models.
|
|
30
|
+
|
|
31
|
+
This class extends the BaseOpenAIClient and provides OpenAI-specific implementation
|
|
32
|
+
for creating completions.
|
|
33
|
+
|
|
34
|
+
Attributes:
|
|
35
|
+
client (AsyncOpenAI): The OpenAI client used to interact with the API.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(
|
|
39
|
+
self,
|
|
40
|
+
config: LLMConfig | None = None,
|
|
41
|
+
cache: bool = False,
|
|
42
|
+
client: typing.Any = None,
|
|
43
|
+
max_tokens: int = DEFAULT_MAX_TOKENS,
|
|
44
|
+
):
|
|
45
|
+
"""
|
|
46
|
+
Initialize the OpenAIClient with the provided configuration, cache setting, and client.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
config (LLMConfig | None): The configuration for the LLM client, including API key, model, base URL, temperature, and max tokens.
|
|
50
|
+
cache (bool): Whether to use caching for responses. Defaults to False.
|
|
51
|
+
client (Any | None): An optional async client instance to use. If not provided, a new AsyncOpenAI client is created.
|
|
52
|
+
"""
|
|
53
|
+
super().__init__(config, cache, max_tokens)
|
|
54
|
+
|
|
55
|
+
if config is None:
|
|
56
|
+
config = LLMConfig()
|
|
57
|
+
|
|
58
|
+
if client is None:
|
|
59
|
+
self.client = AsyncOpenAI(api_key=config.api_key, base_url=config.base_url)
|
|
60
|
+
else:
|
|
61
|
+
self.client = client
|
|
62
|
+
|
|
63
|
+
async def _create_structured_completion(
|
|
64
|
+
self,
|
|
65
|
+
model: str,
|
|
66
|
+
messages: list[ChatCompletionMessageParam],
|
|
67
|
+
temperature: float | None,
|
|
68
|
+
max_tokens: int,
|
|
69
|
+
response_model: type[BaseModel],
|
|
70
|
+
):
|
|
71
|
+
"""Create a structured completion using OpenAI's beta parse API."""
|
|
72
|
+
return await self.client.beta.chat.completions.parse(
|
|
73
|
+
model=model,
|
|
74
|
+
messages=messages,
|
|
75
|
+
temperature=temperature,
|
|
76
|
+
max_tokens=max_tokens,
|
|
77
|
+
response_format=response_model, # type: ignore
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
async def _create_completion(
|
|
81
|
+
self,
|
|
82
|
+
model: str,
|
|
83
|
+
messages: list[ChatCompletionMessageParam],
|
|
84
|
+
temperature: float | None,
|
|
85
|
+
max_tokens: int,
|
|
86
|
+
response_model: type[BaseModel] | None = None,
|
|
87
|
+
):
|
|
88
|
+
"""Create a regular completion with JSON format."""
|
|
89
|
+
return await self.client.chat.completions.create(
|
|
90
|
+
model=model,
|
|
91
|
+
messages=messages,
|
|
92
|
+
temperature=temperature,
|
|
93
|
+
max_tokens=max_tokens,
|
|
94
|
+
response_format={'type': 'json_object'},
|
|
95
|
+
)
|
|
@@ -63,7 +63,7 @@ MAX_QUERY_LENGTH = 32
|
|
|
63
63
|
|
|
64
64
|
def fulltext_query(query: str, group_ids: list[str] | None = None):
|
|
65
65
|
group_ids_filter_list = (
|
|
66
|
-
[f"
|
|
66
|
+
[f'group_id:"{lucene_sanitize(g)}"' for g in group_ids] if group_ids is not None else []
|
|
67
67
|
)
|
|
68
68
|
group_ids_filter = ''
|
|
69
69
|
for f in group_ids_filter_list:
|
|
@@ -301,12 +301,12 @@ async def edge_bfs_search(
|
|
|
301
301
|
|
|
302
302
|
query = (
|
|
303
303
|
"""
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
304
|
+
UNWIND $bfs_origin_node_uuids AS origin_uuid
|
|
305
|
+
MATCH path = (origin:Entity|Episodic {uuid: origin_uuid})-[:RELATES_TO|MENTIONS]->{1,3}(n:Entity)
|
|
306
|
+
UNWIND relationships(path) AS rel
|
|
307
|
+
MATCH (n:Entity)-[r:RELATES_TO]-(m:Entity)
|
|
308
|
+
WHERE r.uuid = rel.uuid
|
|
309
|
+
"""
|
|
310
310
|
+ filter_query
|
|
311
311
|
+ """
|
|
312
312
|
RETURN DISTINCT
|
|
@@ -455,10 +455,10 @@ async def node_bfs_search(
|
|
|
455
455
|
|
|
456
456
|
query = (
|
|
457
457
|
"""
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
458
|
+
UNWIND $bfs_origin_node_uuids AS origin_uuid
|
|
459
|
+
MATCH (origin:Entity|Episodic {uuid: origin_uuid})-[:RELATES_TO|MENTIONS]->{1,3}(n:Entity)
|
|
460
|
+
WHERE n.group_id = origin.group_id
|
|
461
|
+
"""
|
|
462
462
|
+ filter_query
|
|
463
463
|
+ ENTITY_NODE_RETURN
|
|
464
464
|
+ """
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "graphiti-core"
|
|
3
3
|
description = "A temporal graph building library"
|
|
4
|
-
version = "0.13.
|
|
4
|
+
version = "0.13.1"
|
|
5
5
|
authors = [
|
|
6
6
|
{ "name" = "Paul Paliychuk", "email" = "paul@getzep.com" },
|
|
7
7
|
{ "name" = "Preston Rasmussen", "email" = "preston@getzep.com" },
|
|
@@ -15,7 +15,7 @@ dependencies = [
|
|
|
15
15
|
"pydantic>=2.11.5",
|
|
16
16
|
"neo4j>=5.26.0",
|
|
17
17
|
"diskcache>=5.6.3",
|
|
18
|
-
"openai>=1.
|
|
18
|
+
"openai>=1.91.0",
|
|
19
19
|
"tenacity>=9.0.0",
|
|
20
20
|
"numpy>=1.0.0",
|
|
21
21
|
"python-dotenv>=1.0.1",
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Copyright 2024, Zep Software, Inc.
|
|
3
|
-
|
|
4
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
-
you may not use this file except in compliance with the License.
|
|
6
|
-
You may obtain a copy of the License at
|
|
7
|
-
|
|
8
|
-
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
-
|
|
10
|
-
Unless required by applicable law or agreed to in writing, software
|
|
11
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
-
See the License for the specific language governing permissions and
|
|
14
|
-
limitations under the License.
|
|
15
|
-
"""
|
|
16
|
-
|
|
17
|
-
import json
|
|
18
|
-
import logging
|
|
19
|
-
from typing import Any
|
|
20
|
-
|
|
21
|
-
from openai import AsyncAzureOpenAI
|
|
22
|
-
from openai.types.chat import ChatCompletionMessageParam
|
|
23
|
-
from pydantic import BaseModel
|
|
24
|
-
|
|
25
|
-
from ..prompts.models import Message
|
|
26
|
-
from .client import LLMClient
|
|
27
|
-
from .config import LLMConfig, ModelSize
|
|
28
|
-
|
|
29
|
-
logger = logging.getLogger(__name__)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
class AzureOpenAILLMClient(LLMClient):
|
|
33
|
-
"""Wrapper class for AsyncAzureOpenAI that implements the LLMClient interface."""
|
|
34
|
-
|
|
35
|
-
def __init__(self, azure_client: AsyncAzureOpenAI, config: LLMConfig | None = None):
|
|
36
|
-
super().__init__(config, cache=False)
|
|
37
|
-
self.azure_client = azure_client
|
|
38
|
-
|
|
39
|
-
async def _generate_response(
|
|
40
|
-
self,
|
|
41
|
-
messages: list[Message],
|
|
42
|
-
response_model: type[BaseModel] | None = None,
|
|
43
|
-
max_tokens: int = 1024,
|
|
44
|
-
model_size: ModelSize = ModelSize.medium,
|
|
45
|
-
) -> dict[str, Any]:
|
|
46
|
-
"""Generate response using Azure OpenAI client."""
|
|
47
|
-
# Convert messages to OpenAI format
|
|
48
|
-
openai_messages: list[ChatCompletionMessageParam] = []
|
|
49
|
-
for message in messages:
|
|
50
|
-
message.content = self._clean_input(message.content)
|
|
51
|
-
if message.role == 'user':
|
|
52
|
-
openai_messages.append({'role': 'user', 'content': message.content})
|
|
53
|
-
elif message.role == 'system':
|
|
54
|
-
openai_messages.append({'role': 'system', 'content': message.content})
|
|
55
|
-
|
|
56
|
-
# Ensure model is a string
|
|
57
|
-
model_name = self.model if self.model else 'gpt-4o-mini'
|
|
58
|
-
|
|
59
|
-
try:
|
|
60
|
-
response = await self.azure_client.chat.completions.create(
|
|
61
|
-
model=model_name,
|
|
62
|
-
messages=openai_messages,
|
|
63
|
-
temperature=float(self.temperature) if self.temperature is not None else 0.7,
|
|
64
|
-
max_tokens=max_tokens,
|
|
65
|
-
response_format={'type': 'json_object'},
|
|
66
|
-
)
|
|
67
|
-
result = response.choices[0].message.content or '{}'
|
|
68
|
-
|
|
69
|
-
# Parse JSON response
|
|
70
|
-
return json.loads(result)
|
|
71
|
-
except Exception as e:
|
|
72
|
-
logger.error(f'Error in Azure OpenAI LLM response: {e}')
|
|
73
|
-
raise
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/cross_encoder/bge_reranker_client.py
RENAMED
|
File without changes
|
|
File without changes
|
{graphiti_core-0.13.0 → graphiti_core-0.13.1}/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
|
{graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/llm_client/openai_generic_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
|
{graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/utils/maintenance/edge_operations.py
RENAMED
|
File without changes
|
|
File without changes
|
{graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/utils/maintenance/node_operations.py
RENAMED
|
File without changes
|
{graphiti_core-0.13.0 → graphiti_core-0.13.1}/graphiti_core/utils/maintenance/temporal_operations.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|