From 7d2170b0926a30f7a056f7e3313a0cc7d8522869 Mon Sep 17 00:00:00 2001 From: Alex Thomas Date: Tue, 19 Nov 2024 14:36:01 +0000 Subject: [PATCH] Added remaining langchain-community code --- .../langchain_neo4j/chains/graph_qa/cypher.py | 10 ++-- .../chains/graph_qa/prompts.py | 41 +++++++++++++++ .../langchain_neo4j/graphs/graph_document.py | 51 +++++++++++++++++++ .../langchain_neo4j/graphs/graph_store.py | 37 ++++++++++++++ .../langchain_neo4j/graphs/neo4j_graph.py | 5 +- .../vectorstores/neo4j_vector.py | 11 ++-- .../langchain_neo4j/vectorstores/utils.py | 12 +++++ .../integration_tests/graphs/test_neo4j.py | 2 +- .../vectorstores/test_neo4jvector.py | 2 +- .../tests/unit_tests/chains/test_graph_qa.py | 9 ++-- 10 files changed, 162 insertions(+), 18 deletions(-) create mode 100644 libs/neo4j/langchain_neo4j/chains/graph_qa/prompts.py create mode 100644 libs/neo4j/langchain_neo4j/graphs/graph_document.py create mode 100644 libs/neo4j/langchain_neo4j/graphs/graph_store.py create mode 100644 libs/neo4j/langchain_neo4j/vectorstores/utils.py diff --git a/libs/neo4j/langchain_neo4j/chains/graph_qa/cypher.py b/libs/neo4j/langchain_neo4j/chains/graph_qa/cypher.py index b8353a1..48b0843 100644 --- a/libs/neo4j/langchain_neo4j/chains/graph_qa/cypher.py +++ b/libs/neo4j/langchain_neo4j/chains/graph_qa/cypher.py @@ -7,11 +7,6 @@ from langchain.chains.base import Chain from langchain.chains.llm import LLMChain -from langchain_community.chains.graph_qa.prompts import ( - CYPHER_GENERATION_PROMPT, - CYPHER_QA_PROMPT, -) -from langchain_community.graphs.graph_store import GraphStore from langchain_core.callbacks import CallbackManagerForChainRun from langchain_core.language_models import BaseLanguageModel from langchain_core.messages import ( @@ -34,6 +29,11 @@ CypherQueryCorrector, Schema, ) +from langchain_neo4j.chains.graph_qa.prompts import ( + CYPHER_GENERATION_PROMPT, + CYPHER_QA_PROMPT, +) +from langchain_neo4j.graphs.graph_store import GraphStore INTERMEDIATE_STEPS_KEY = "intermediate_steps" diff --git a/libs/neo4j/langchain_neo4j/chains/graph_qa/prompts.py b/libs/neo4j/langchain_neo4j/chains/graph_qa/prompts.py new file mode 100644 index 0000000..9cac173 --- /dev/null +++ b/libs/neo4j/langchain_neo4j/chains/graph_qa/prompts.py @@ -0,0 +1,41 @@ +# flake8: noqa +from langchain_core.prompts.prompt import PromptTemplate + +CYPHER_GENERATION_TEMPLATE = """Task:Generate Cypher statement to query a graph database. +Instructions: +Use only the provided relationship types and properties in the schema. +Do not use any other relationship types or properties that are not provided. +Schema: +{schema} +Note: Do not include any explanations or apologies in your responses. +Do not respond to any questions that might ask anything else than for you to construct a Cypher statement. +Do not include any text except the generated Cypher statement. + +The question is: +{question}""" + +CYPHER_GENERATION_PROMPT = PromptTemplate( + input_variables=["schema", "question"], template=CYPHER_GENERATION_TEMPLATE +) + +CYPHER_QA_TEMPLATE = """You are an assistant that helps to form nice and human understandable answers. +The information part contains the provided information that you must use to construct an answer. +The provided information is authoritative, you must never doubt it or try to use your internal knowledge to correct it. +Make the answer sound as a response to the question. Do not mention that you based the result on the given information. +Here is an example: + +Question: Which managers own Neo4j stocks? +Context:[manager:CTL LLC, manager:JANE STREET GROUP LLC] +Helpful Answer: CTL LLC, JANE STREET GROUP LLC owns Neo4j stocks. + +Follow this example when generating answers. +If the provided information is empty, say that you don't know the answer. +Information: +{context} + +Question: {question} +Helpful Answer:""" + +CYPHER_QA_PROMPT = PromptTemplate( + input_variables=["context", "question"], template=CYPHER_QA_TEMPLATE +) diff --git a/libs/neo4j/langchain_neo4j/graphs/graph_document.py b/libs/neo4j/langchain_neo4j/graphs/graph_document.py new file mode 100644 index 0000000..ff82ca4 --- /dev/null +++ b/libs/neo4j/langchain_neo4j/graphs/graph_document.py @@ -0,0 +1,51 @@ +from __future__ import annotations + +from typing import List, Union + +from langchain_core.documents import Document +from langchain_core.load.serializable import Serializable +from pydantic import Field + + +class Node(Serializable): + """Represents a node in a graph with associated properties. + + Attributes: + id (Union[str, int]): A unique identifier for the node. + type (str): The type or label of the node, default is "Node". + properties (dict): Additional properties and metadata associated with the node. + """ + + id: Union[str, int] + type: str = "Node" + properties: dict = Field(default_factory=dict) + + +class Relationship(Serializable): + """Represents a directed relationship between two nodes in a graph. + + Attributes: + source (Node): The source node of the relationship. + target (Node): The target node of the relationship. + type (str): The type of the relationship. + properties (dict): Additional properties associated with the relationship. + """ + + source: Node + target: Node + type: str + properties: dict = Field(default_factory=dict) + + +class GraphDocument(Serializable): + """Represents a graph document consisting of nodes and relationships. + + Attributes: + nodes (List[Node]): A list of nodes in the graph. + relationships (List[Relationship]): A list of relationships in the graph. + source (Document): The document from which the graph information is derived. + """ + + nodes: List[Node] + relationships: List[Relationship] + source: Document diff --git a/libs/neo4j/langchain_neo4j/graphs/graph_store.py b/libs/neo4j/langchain_neo4j/graphs/graph_store.py new file mode 100644 index 0000000..8a7d650 --- /dev/null +++ b/libs/neo4j/langchain_neo4j/graphs/graph_store.py @@ -0,0 +1,37 @@ +from abc import abstractmethod +from typing import Any, Dict, List + +from langchain_neo4j.graphs.graph_document import GraphDocument + + +class GraphStore: + """Abstract class for graph operations.""" + + @property + @abstractmethod + def get_schema(self) -> str: + """Return the schema of the Graph database""" + pass + + @property + @abstractmethod + def get_structured_schema(self) -> Dict[str, Any]: + """Return the schema of the Graph database""" + pass + + @abstractmethod + def query(self, query: str, params: dict = {}) -> List[Dict[str, Any]]: + """Query the graph.""" + pass + + @abstractmethod + def refresh_schema(self) -> None: + """Refresh the graph schema information.""" + pass + + @abstractmethod + def add_graph_documents( + self, graph_documents: List[GraphDocument], include_source: bool = False + ) -> None: + """Take GraphDocument as input as uses it to construct a graph.""" + pass diff --git a/libs/neo4j/langchain_neo4j/graphs/neo4j_graph.py b/libs/neo4j/langchain_neo4j/graphs/neo4j_graph.py index 06e644b..54bb643 100644 --- a/libs/neo4j/langchain_neo4j/graphs/neo4j_graph.py +++ b/libs/neo4j/langchain_neo4j/graphs/neo4j_graph.py @@ -1,10 +1,11 @@ from hashlib import md5 from typing import Any, Dict, List, Optional -from langchain_community.graphs.graph_document import GraphDocument -from langchain_community.graphs.graph_store import GraphStore from langchain_core.utils import get_from_dict_or_env +from langchain_neo4j.graphs.graph_document import GraphDocument +from langchain_neo4j.graphs.graph_store import GraphStore + BASE_ENTITY_LABEL = "__Entity__" EXCLUDED_LABELS = ["_Bloom_Perspective_", "_Bloom_Scene_"] EXCLUDED_RELS = ["_Bloom_HAS_SCENE_"] diff --git a/libs/neo4j/langchain_neo4j/vectorstores/neo4j_vector.py b/libs/neo4j/langchain_neo4j/vectorstores/neo4j_vector.py index 0dbf383..ea4aca1 100644 --- a/libs/neo4j/langchain_neo4j/vectorstores/neo4j_vector.py +++ b/libs/neo4j/langchain_neo4j/vectorstores/neo4j_vector.py @@ -16,16 +16,14 @@ ) import numpy as np -from langchain_community.vectorstores.utils import ( - DistanceStrategy, - maximal_marginal_relevance, -) from langchain_core.documents import Document from langchain_core.embeddings import Embeddings from langchain_core.utils import get_from_dict_or_env from langchain_core.vectorstores import VectorStore +from langchain_core.vectorstores.utils import maximal_marginal_relevance from langchain_neo4j.graphs.neo4j_graph import Neo4jGraph +from langchain_neo4j.vectorstores.utils import DistanceStrategy DEFAULT_DISTANCE_STRATEGY = DistanceStrategy.COSINE DISTANCE_MAPPING = { @@ -468,7 +466,7 @@ class Neo4jVector(VectorStore): .. code-block:: python from langchain_neo4j import Neo4jVector - from langchain_community.embeddings.openai import OpenAIEmbeddings + from langchain_openai import OpenAIEmbeddings url="bolt://localhost:7687" username="neo4j" @@ -1253,7 +1251,8 @@ def from_embeddings( .. code-block:: python from langchain_neo4j import Neo4jVector - from langchain_community.embeddings import OpenAIEmbeddings + from langchain_openai import OpenAIEmbeddings + embeddings = OpenAIEmbeddings() text_embeddings = embeddings.embed_documents(texts) text_embedding_pairs = list(zip(texts, text_embeddings)) diff --git a/libs/neo4j/langchain_neo4j/vectorstores/utils.py b/libs/neo4j/langchain_neo4j/vectorstores/utils.py new file mode 100644 index 0000000..5ddda4c --- /dev/null +++ b/libs/neo4j/langchain_neo4j/vectorstores/utils.py @@ -0,0 +1,12 @@ +from enum import Enum + + +class DistanceStrategy(str, Enum): + """Enumerator of the Distance strategies for calculating distances + between vectors.""" + + EUCLIDEAN_DISTANCE = "EUCLIDEAN_DISTANCE" + MAX_INNER_PRODUCT = "MAX_INNER_PRODUCT" + DOT_PRODUCT = "DOT_PRODUCT" + JACCARD = "JACCARD" + COSINE = "COSINE" diff --git a/libs/neo4j/tests/integration_tests/graphs/test_neo4j.py b/libs/neo4j/tests/integration_tests/graphs/test_neo4j.py index a02cffa..1a12276 100644 --- a/libs/neo4j/tests/integration_tests/graphs/test_neo4j.py +++ b/libs/neo4j/tests/integration_tests/graphs/test_neo4j.py @@ -1,9 +1,9 @@ import os -from langchain_community.graphs.graph_document import GraphDocument, Node, Relationship from langchain_core.documents import Document from langchain_neo4j import Neo4jGraph +from langchain_neo4j.graphs.graph_document import GraphDocument, Node, Relationship from langchain_neo4j.graphs.neo4j_graph import ( BASE_ENTITY_LABEL, node_properties_query, diff --git a/libs/neo4j/tests/integration_tests/vectorstores/test_neo4jvector.py b/libs/neo4j/tests/integration_tests/vectorstores/test_neo4jvector.py index ccf62c6..a8db90b 100644 --- a/libs/neo4j/tests/integration_tests/vectorstores/test_neo4jvector.py +++ b/libs/neo4j/tests/integration_tests/vectorstores/test_neo4jvector.py @@ -4,7 +4,6 @@ from math import isclose from typing import Any, Dict, List, cast -from langchain_community.vectorstores.utils import DistanceStrategy from langchain_core.documents import Document from yaml import safe_load @@ -14,6 +13,7 @@ SearchType, _get_search_index_query, ) +from langchain_neo4j.vectorstores.utils import DistanceStrategy from tests.integration_tests.vectorstores.fake_embeddings import ( AngularTwoDimensionalEmbeddings, FakeEmbeddings, diff --git a/libs/neo4j/tests/unit_tests/chains/test_graph_qa.py b/libs/neo4j/tests/unit_tests/chains/test_graph_qa.py index 4450983..c6c08af 100644 --- a/libs/neo4j/tests/unit_tests/chains/test_graph_qa.py +++ b/libs/neo4j/tests/unit_tests/chains/test_graph_qa.py @@ -2,10 +2,7 @@ from csv import DictReader from typing import Any, Dict, List -from langchain.chains.graph_qa.prompts import CYPHER_GENERATION_PROMPT, CYPHER_QA_PROMPT from langchain.memory import ConversationBufferMemory, ReadOnlySharedMemory -from langchain_community.graphs.graph_document import GraphDocument -from langchain_community.graphs.graph_store import GraphStore from langchain_core.prompts import PromptTemplate from langchain_neo4j.chains.graph_qa.cypher import ( @@ -17,6 +14,12 @@ CypherQueryCorrector, Schema, ) +from langchain_neo4j.chains.graph_qa.prompts import ( + CYPHER_GENERATION_PROMPT, + CYPHER_QA_PROMPT, +) +from langchain_neo4j.graphs.graph_document import GraphDocument +from langchain_neo4j.graphs.graph_store import GraphStore from tests.unit_tests.llms.fake_llm import FakeLLM