diff --git a/comps/__init__.py b/comps/__init__.py index 14ff81d15..51c6202a8 100644 --- a/comps/__init__.py +++ b/comps/__init__.py @@ -16,6 +16,7 @@ TextDoc, RAGASParams, RAGASScores, + GraphDoc, LVMDoc, ) diff --git a/comps/cores/mega/constants.py b/comps/cores/mega/constants.py index 6de0d0edc..ae0eeeec8 100644 --- a/comps/cores/mega/constants.py +++ b/comps/cores/mega/constants.py @@ -27,6 +27,7 @@ class ServiceType(Enum): UNDEFINED = 10 RAGAS = 11 LVM = 12 + KNOWLEDGE_GRAPH = 13 class MegaServiceEndpoint(Enum): @@ -50,6 +51,8 @@ class MegaServiceEndpoint(Enum): RERANKING = "/v1/reranking" GUARDRAILS = "/v1/guardrails" RAGAS = "/v1/ragas" + GRAPHS = "/v1/graphs" + # COMMON LIST_SERVICE = "/v1/list_service" LIST_PARAMETERS = "/v1/list_parameters" diff --git a/comps/cores/proto/docarray.py b/comps/cores/proto/docarray.py index 23c38890e..035b5edbe 100644 --- a/comps/cores/proto/docarray.py +++ b/comps/cores/proto/docarray.py @@ -104,6 +104,19 @@ class RAGASScores(BaseDoc): context_precision: float +class GraphDoc(BaseDoc): + text: str + strtype: Optional[str] = Field( + description="type of input query, can be 'query', 'cypher', 'rag'", + default="query", + ) + max_new_tokens: Optional[int] = Field(default=1024) + rag_index_name: Optional[str] = Field(default="rag") + rag_node_label: Optional[str] = Field(default="Task") + rag_text_node_properties: Optional[list] = Field(default=["name", "description", "status"]) + rag_embedding_node_property: Optional[str] = Field(default="embedding") + + class LVMDoc(BaseDoc): image: str prompt: str diff --git a/comps/knowledgegraphs/README.md b/comps/knowledgegraphs/README.md new file mode 100755 index 000000000..248e46eba --- /dev/null +++ b/comps/knowledgegraphs/README.md @@ -0,0 +1,146 @@ +# Knowledge Graph Microservice + +This microservice, designed for efficiently handling and retrieving informantion from knowledge graph. The microservice integrates text retriever, knowledge graph quick search and LLM agent, which can be combined to enhance question answering. + +The service contains three modes: + +- "cypher": Query knowledge graph directly with cypher +- "rag": Apply similarity search on embeddings of knowledge graph +- "query": An LLM agent will automatically choose tools (RAG or CypherChain) to enhance the question answering + +Here is the overall workflow: + +![Workflow](doc/workflow.png) + +A prerequisite for using this microservice is that users must have a knowledge gragh database already running, and currently we have support [Neo4J](https://neo4j.com/) for quick deployment. Users need to set the graph service's endpoint into an environment variable and microservie utilizes it for data injestion and retrieve. If user want to use "rag" and "query" mode, still need a LLM text generation service (etc., TGI, vLLM and Ray) already running. + +Overall, this microservice provides efficient support for applications related with graph dataset, especially for answering multi-part questions, or any other conditions including comples relationship between entities. + +# 🚀1. Start Microservice with Docker + +## 1.1 Setup Environment Variables + +```bash +export NEO4J_ENDPOINT="neo4j://${your_ip}:7687" +export NEO4J_USERNAME="neo4j" +export NEO4J_PASSWORD=${define_a_password} +export HUGGINGFACEHUB_API_TOKEN=${your_huggingface_api_token} +export LLM_ENDPOINT="http://${your_ip}:8080" +export LLM_MODEL="meta-llama/Llama-2-7b-hf" +export AGENT_LLM="HuggingFaceH4/zephyr-7b-beta" +``` + +## 1.2 Start Neo4j Service + +```bash +docker pull neo4j + +docker run --rm \ + --publish=7474:7474 --publish=7687:7687 \ + --env NEO4J_AUTH=$NEO4J_USER/$NEO4J_PASSWORD \ + --volume=$PWD/neo4j_data:"/data" \ + --env='NEO4JLABS_PLUGINS=["apoc"]' \ + neo4j +``` + +## 1.3 Start LLM Service for "rag"/"query" mode + +You can start any LLM microserve, here we take TGI as an example. + +```bash +docker run -p 8080:80 \ + -v $PWD/llm_data:/data --runtime=habana \ + -e HABANA_VISIBLE_DEVICES=all \ + -e OMPI_MCA_btl_vader_single_copy_mechanism=none \ + -e HUGGING_FACE_HUB_TOKEN=$HUGGINGFACEHUB_API_TOKEN \ + --cap-add=sys_nice \ + --ipc=host \ + ghcr.io/huggingface/tgi-gaudi:2.0.0 \ + --model-id $LLM_MODEL \ + --max-input-tokens 1024 \ + --max-total-tokens 2048 +``` + +Verify LLM service. + +```bash +curl $LLM_ENDPOINT/generate \ + -X POST \ + -d '{"inputs":"What is Deep Learning?","parameters":{"max_new_tokens":32}}' \ + -H 'Content-Type: application/json' +``` + +## 1.4 Start Microservice + +```bash +cd ../.. +docker build -t opea/knowledge_graphs:latest \ + --build-arg https_proxy=$https_proxy \ + --build-arg http_proxy=$http_proxy \ + -f comps/knowledgegraphs/langchain/docker/Dockerfile . + +docker run --rm \ + --name="knowledge-graph-server" \ + -p 8060:8060 \ + --ipc=host \ + -e http_proxy=$http_proxy \ + -e https_proxy=$https_proxy \ + -e NEO4J_ENDPOINT=$NEO4J_ENDPOINT \ + -e NEO4J_USERNAME=$NEO4J_USERNAME \ + -e NEO4J_PASSWORD=$NEO4J_PASSWORD \ + -e HUGGINGFACEHUB_API_TOKEN=$HUGGINGFACEHUB_API_TOKEN \ + -e LLM_ENDPOINT=$LLM_ENDPOINT \ + opea/knowledge_graphs:latest +``` + +# 🚀2. Consume Knowledge Graph Service + +## 2.1 Cypher mode + +```bash +curl http://${your_ip}:8060/v1/graphs \ + -X POST \ + -d "{\"text\":\"MATCH (t:Task {status:'open'}) RETURN count(*)\",\"strtype\":\"cypher\"}" \ + -H 'Content-Type: application/json' +``` + +Example output: +![Cypher Output](doc/output_cypher.png) + +## 2.2 Rag mode + +```bash +curl http://${your_ip}:8060/v1/graphs \ + -X POST \ + -d "{\"text\":\"How many open tickets there are?\",\"strtype\":\"rag\", \"max_new_tokens\":128}" \ + -H 'Content-Type: application/json' +``` + +Example output: +![Cypher Output](doc/output_rag.png) + +## 2.3 Query mode + +First example: + +```bash +curl http://${your_ip}:8060/v1/graphs \ + -X POST \ + -d "{\"text\":\"Which tasks have optimization in their description?\",\"strtype\":\"query\"}" \ + -H 'Content-Type: application/json' +``` + +Example output: +![Cypher Output](doc/output_query1.png) + +Second example: + +```bash +curl http://${your_ip}:8060/v1/graphs \ + -X POST \ + -d "{\"text\":\"Which team is assigned to maintain PaymentService?\",\"strtype\":\"query\"}" \ + -H 'Content-Type: application/json' +``` + +Example output: +![Cypher Output](doc/output_query2.png) diff --git a/comps/knowledgegraphs/__init__.py b/comps/knowledgegraphs/__init__.py new file mode 100755 index 000000000..916f3a44b --- /dev/null +++ b/comps/knowledgegraphs/__init__.py @@ -0,0 +1,2 @@ +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 diff --git a/comps/knowledgegraphs/build_docker.sh b/comps/knowledgegraphs/build_docker.sh new file mode 100755 index 000000000..8cd45ce62 --- /dev/null +++ b/comps/knowledgegraphs/build_docker.sh @@ -0,0 +1,7 @@ +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +docker build -t opea/knowledge_graphs:latest \ + --build-arg https_proxy=$https_proxy \ + --build-arg http_proxy=$http_proxy \ + -f comps/knowledgegraphs/langchain/docker/Dockerfile . diff --git a/comps/knowledgegraphs/doc/output_cypher.png b/comps/knowledgegraphs/doc/output_cypher.png new file mode 100644 index 000000000..c77f54611 Binary files /dev/null and b/comps/knowledgegraphs/doc/output_cypher.png differ diff --git a/comps/knowledgegraphs/doc/output_query1.png b/comps/knowledgegraphs/doc/output_query1.png new file mode 100644 index 000000000..cc470e1a5 Binary files /dev/null and b/comps/knowledgegraphs/doc/output_query1.png differ diff --git a/comps/knowledgegraphs/doc/output_query2.png b/comps/knowledgegraphs/doc/output_query2.png new file mode 100644 index 000000000..af9e3dae9 Binary files /dev/null and b/comps/knowledgegraphs/doc/output_query2.png differ diff --git a/comps/knowledgegraphs/doc/output_rag.png b/comps/knowledgegraphs/doc/output_rag.png new file mode 100644 index 000000000..25bb1e409 Binary files /dev/null and b/comps/knowledgegraphs/doc/output_rag.png differ diff --git a/comps/knowledgegraphs/doc/workflow.png b/comps/knowledgegraphs/doc/workflow.png new file mode 100644 index 000000000..770870708 Binary files /dev/null and b/comps/knowledgegraphs/doc/workflow.png differ diff --git a/comps/knowledgegraphs/langchain/README.md b/comps/knowledgegraphs/langchain/README.md new file mode 100755 index 000000000..e69de29bb diff --git a/comps/knowledgegraphs/langchain/__init__.py b/comps/knowledgegraphs/langchain/__init__.py new file mode 100755 index 000000000..916f3a44b --- /dev/null +++ b/comps/knowledgegraphs/langchain/__init__.py @@ -0,0 +1,2 @@ +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 diff --git a/comps/knowledgegraphs/langchain/data/microservices.json b/comps/knowledgegraphs/langchain/data/microservices.json new file mode 100644 index 000000000..7a042b073 --- /dev/null +++ b/comps/knowledgegraphs/langchain/data/microservices.json @@ -0,0 +1,3 @@ +{ + "query": "MERGE (catalog:Microservice {name: 'CatalogService', technology: 'Java'}) MERGE (order:Microservice {name: 'OrderService', technology: 'Python'}) MERGE (user:Microservice {name: 'UserService', technology: 'Go'}) MERGE (payment:Microservice {name: 'PaymentService', technology: 'Node.js'}) MERGE (inventory:Microservice {name: 'InventoryService', technology: 'Java'}) MERGE (shipping:Microservice {name: 'ShippingService', technology: 'Python'}) MERGE (review:Microservice {name: 'ReviewService', technology: 'Go'}) MERGE (recommendation:Microservice {name: 'RecommendationService', technology: 'Node.js'}) MERGE (auth:Microservice {name: 'AuthService', technology: 'Node.js'}) MERGE (db:Microservice {name: 'Database', technology: 'SQL'}) MERGE (cache:Microservice {name: 'Cache', technology: 'In-memory'}) MERGE (mq:Microservice {name: 'MessageQueue', technology: 'Pub-Sub'}) MERGE (api:Microservice {name: 'ExternalAPI', technology: 'REST'}) MERGE (bugFixCatalog:Task {name: 'BugFix', description: 'Address and resolve a critical bug impacting the CatalogService, affecting the user interface and experience, and hampering the overall performance and responsiveness of the service.', status: 'open'}) MERGE (featureAddOrder:Task {name: 'FeatureAdd', description: 'Implement a new feature in OrderService to facilitate bulk orders, ensuring the features seamless integration with existing functionalities and maintaining the overall stability and performance of the service.', status: 'in progress'}) MERGE (refactorUser:Task {name: 'Refactor', description: 'Refactor the UserService codebase to enhance its readability, maintainability, and scalability, focusing primarily on modularization and optimization of existing functionalities.', status: 'completed'}) MERGE (optimizePayment:Task {name: 'Optimize', description: 'Optimize PaymentService by refining the transaction processing logic, reducing the service’s latency, and improving its reliability and efficiency in handling transactions.', status: 'open'}) MERGE (updateInventory:Task {name: 'Update', description: 'Update InventoryService to include real-time stock updates, ensuring accurate reflection of the inventory levels and aiding in the efficient management of stock.', status: 'in progress'}) MERGE (enhanceShipping:Task {name: 'Enhance', description: 'Enhance the ShippingService by integrating a new shipping partner API, thereby expanding the shipping options available to the customers and improving the overall delivery experience.', status: 'completed'}) MERGE (reviewFix:Task {name: 'ReviewFix', description: 'Rectify a recurring issue in ReviewService affecting the retrieval of user reviews, by refining the service’s logic and improving its efficiency in handling and displaying user reviews.', status: 'open'}) MERGE (recommendationFeature:Task {name: 'RecommendationFeature', description: 'Add a new feature to RecommendationService to provide more personalized and accurate product recommendations to the users, leveraging user behavior and preference data.', status: 'in progress'}) MERGE (optimizeAuth:Task {name: 'Optimize', description: 'Enhance AuthService’s performance and security by optimizing the authentication mechanisms and implementing additional security measures to safeguard user information.', status: 'open'}) MERGE (newTask:Task {name: 'ImproveSecurity', description: 'Enhance the security of microservices by implementing advanced encryption and securing endpoints.', status: 'open'}) MERGE (teamA:Team {name: 'TeamA'}) MERGE (teamB:Team {name: 'TeamB'}) MERGE (teamC:Team {name: 'TeamC'}) MERGE (teamD:Team {name: 'TeamD'}) MERGE (alice:Person {name: 'Alice'}) MERGE (bob:Person {name: 'Bob'}) MERGE (charlie:Person {name: 'Charlie'}) MERGE (diana:Person {name: 'Diana'}) MERGE (eva:Person {name: 'Eva'}) MERGE (frank:Person {name: 'Frank'}) MERGE (catalog)-[:DEPENDS_ON]->(db) MERGE (order)-[:DEPENDS_ON]->(db) MERGE (user)-[:DEPENDS_ON]->(db) MERGE (payment)-[:DEPENDS_ON]->(db) MERGE (inventory)-[:DEPENDS_ON]->(db) MERGE (shipping)-[:DEPENDS_ON]->(mq) MERGE (review)-[:DEPENDS_ON]->(cache) MERGE (recommendation)-[:DEPENDS_ON]->(api) MERGE (auth)-[:DEPENDS_ON]->(db) MERGE (order)-[:DEPENDS_ON]->(inventory) MERGE (order)-[:DEPENDS_ON]->(shipping) MERGE (order)-[:DEPENDS_ON]->(payment) MERGE (catalog)-[:DEPENDS_ON]->(review) MERGE (catalog)-[:DEPENDS_ON]->(recommendation) MERGE (user)-[:DEPENDS_ON]->(auth) MERGE (payment)-[:DEPENDS_ON]->(auth) MERGE (shipping)-[:DEPENDS_ON]->(auth) MERGE (catalog)-[:MAINTAINED_BY]->(teamA) MERGE (order)-[:MAINTAINED_BY]->(teamB) MERGE (user)-[:MAINTAINED_BY]->(teamC) MERGE (payment)-[:MAINTAINED_BY]->(teamD) MERGE (inventory)-[:MAINTAINED_BY]->(teamA) MERGE (shipping)-[:MAINTAINED_BY]->(teamB) MERGE (review)-[:MAINTAINED_BY]->(teamC) MERGE (recommendation)-[:MAINTAINED_BY]->(teamD) MERGE (auth)-[:MAINTAINED_BY]->(teamA) MERGE (bugFixCatalog)-[:ASSIGNED_TO]->(teamA) MERGE (featureAddOrder)-[:ASSIGNED_TO]->(teamB) MERGE (refactorUser)-[:ASSIGNED_TO]->(teamC) MERGE (optimizePayment)-[:ASSIGNED_TO]->(teamD) MERGE (updateInventory)-[:ASSIGNED_TO]->(teamA) MERGE (enhanceShipping)-[:ASSIGNED_TO]->(teamB) MERGE (reviewFix)-[:ASSIGNED_TO]->(teamC) MERGE (recommendationFeature)-[:ASSIGNED_TO]->(teamD) MERGE (optimizeAuth)-[:ASSIGNED_TO]->(teamA) MERGE (bugFixCatalog)-[:LINKED_TO]->(catalog) MERGE (featureAddOrder)-[:LINKED_TO]->(order) MERGE (refactorUser)-[:LINKED_TO]->(user) MERGE (optimizePayment)-[:LINKED_TO]->(payment) MERGE (updateInventory)-[:LINKED_TO]->(inventory) MERGE (enhanceShipping)-[:LINKED_TO]->(shipping) MERGE (reviewFix)-[:LINKED_TO]->(review) MERGE (recommendationFeature)-[:LINKED_TO]->(recommendation) MERGE (optimizeAuth)-[:LINKED_TO]->(auth) MERGE (alice)-[:PART_OF]->(teamA) MERGE (bob)-[:PART_OF]->(teamB) MERGE (charlie)-[:PART_OF]->(teamC) MERGE (diana)-[:PART_OF]->(teamD) MERGE (eva)-[:PART_OF]->(teamA) MERGE (frank)-[:PART_OF]->(teamB) MERGE (newTask)-[:LINKED_TO]->(auth) MERGE (newTask)-[:ASSIGNED_TO]->(teamA)" +} diff --git a/comps/knowledgegraphs/langchain/docker/Dockerfile b/comps/knowledgegraphs/langchain/docker/Dockerfile new file mode 100755 index 000000000..fe95d2102 --- /dev/null +++ b/comps/knowledgegraphs/langchain/docker/Dockerfile @@ -0,0 +1,27 @@ + +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +FROM langchain/langchain:latest + +RUN apt-get update -y && apt-get install -y --no-install-recommends --fix-missing \ + libgl1-mesa-glx \ + libjemalloc-dev \ + vim + +RUN useradd -m -s /bin/bash user && \ + mkdir -p /home/user && \ + chown -R user /home/user/ + +COPY comps /home/user/comps + +USER user + +RUN pip install --no-cache-dir --upgrade pip && \ + pip install --no-cache-dir -r /home/user/comps/knowledgegraphs/requirements.txt + +ENV PYTHONPATH=$PYTHONPATH:/home/user + +WORKDIR /home/user/comps/knowledgegraphs/langchain + +ENTRYPOINT ["python", "knowledge_graph.py"] diff --git a/comps/knowledgegraphs/langchain/ingest.py b/comps/knowledgegraphs/langchain/ingest.py new file mode 100755 index 000000000..7e7e1d8b3 --- /dev/null +++ b/comps/knowledgegraphs/langchain/ingest.py @@ -0,0 +1,21 @@ +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +import json +import os + +from langchain_community.graphs import Neo4jGraph + +neo4j_endpoint = os.getenv("NEO4J_ENDPOINT", "neo4j://localhost:7687") +neo4j_username = os.getenv("NEO4J_USERNAME", "neo4j") +neo4j_password = os.getenv("NEO4J_PASSWORD", "neo4j") +graph = Neo4jGraph(url=neo4j_endpoint, username=neo4j_username, password=neo4j_password) + +# remove all nodes +graph.query("MATCH (n) DETACH DELETE n") + +# ingest +import_query = json.load(open("data/microservices.json", "r"))["query"] +graph.query(import_query) +print("Total nodes: ", graph.query("MATCH (n) RETURN count(n)")) +print("Total edges: ", graph.query("MATCH ()-->() RETURN count(*)")) diff --git a/comps/knowledgegraphs/langchain/knowledge_graph.py b/comps/knowledgegraphs/langchain/knowledge_graph.py new file mode 100755 index 000000000..9ed2c5b65 --- /dev/null +++ b/comps/knowledgegraphs/langchain/knowledge_graph.py @@ -0,0 +1,162 @@ +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +import os +import pathlib +import sys + +cur_path = pathlib.Path(__file__).parent.resolve() +comps_path = os.path.join(cur_path, "../../../") +sys.path.append(comps_path) +import json + +import requests +from langchain import hub +from langchain.agents import AgentExecutor, Tool, load_tools +from langchain.agents.format_scratchpad import format_log_to_str +from langchain.agents.output_parsers import ReActJsonSingleInputOutputParser +from langchain.chains import GraphCypherQAChain, RetrievalQA +from langchain.tools.render import render_text_description +from langchain_community.chat_models.huggingface import ChatHuggingFace +from langchain_community.embeddings import HuggingFaceEmbeddings +from langchain_community.graphs import Neo4jGraph +from langchain_community.llms import HuggingFaceEndpoint +from langchain_community.vectorstores.neo4j_vector import Neo4jVector +from langsmith import traceable + +from comps import GeneratedDoc, GraphDoc, ServiceType, opea_microservices, register_microservice + + +def get_retriever(input, neo4j_endpoint, neo4j_username, neo4j_password, llm): + embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-mpnet-base-v2") + vector_index = Neo4jVector.from_existing_graph( + embeddings, + url=neo4j_endpoint, + username=neo4j_username, + password=neo4j_password, + index_name=input.rag_index_name, + node_label=input.rag_node_label, + text_node_properties=input.rag_text_node_properties, + embedding_node_property=input.rag_embedding_node_property, + ) + vector_qa = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=vector_index.as_retriever()) + return vector_qa + + +def get_cypherchain(graph, cypher_llm, qa_llm): + graph.refresh_schema() + cypher_chain = GraphCypherQAChain.from_llm(cypher_llm=cypher_llm, qa_llm=qa_llm, graph=graph, verbose=True) + return cypher_chain + + +def get_agent(vector_qa, cypher_chain, llm_repo_id): + # define two tools + tools = [ + Tool( + name="Tasks", + func=vector_qa.invoke, + description="""Useful when you need to answer questions about descriptions of tasks. + Not useful for counting the number of tasks. + Use full question as input. + """, + ), + Tool( + name="Graph", + func=cypher_chain.invoke, + description="""Useful when you need to answer questions about microservices, + their dependencies or assigned people. Also useful for any sort of + aggregation like counting the number of tasks, etc. + Use full question as input. + """, + ), + ] + + # setup ReAct style prompt + prompt = hub.pull("hwchase17/react-json") + prompt = prompt.partial( + tools=render_text_description(tools), + tool_names=", ".join([t.name for t in tools]), + ) + + # define chat model + llm = HuggingFaceEndpoint(repo_id=llm_repo_id, max_new_tokens=512) + chat_model = ChatHuggingFace(llm=llm) + chat_model_with_stop = chat_model.bind(stop=["\nObservation"]) + + # define agent + agent = ( + { + "input": lambda x: x["input"], + "agent_scratchpad": lambda x: format_log_to_str(x["intermediate_steps"]), + } + | prompt + | chat_model_with_stop + | ReActJsonSingleInputOutputParser() + ) + + # instantiate AgentExecutor + agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True) + return agent_executor + + +@register_microservice( + name="opea_service@knowledge_graph", + endpoint="/v1/graphs", + host="0.0.0.0", + port=8060, +) +def graph_query(input: GraphDoc) -> GeneratedDoc: + print(input) + + ## Connect to Neo4j + neo4j_endpoint = os.getenv("NEO4J_ENDPOINT", "neo4j://localhost:7687") + neo4j_username = os.getenv("NEO4J_USERNAME", "neo4j") + neo4j_password = os.getenv("NEO4J_PASSWORD", "neo4j") + graph = Neo4jGraph(url=neo4j_endpoint, username=neo4j_username, password=neo4j_password) + + ## keep for multiple tests, will remove later + graph.query("MATCH (n) DETACH DELETE n") + import_query = json.load(open("data/microservices.json", "r"))["query"] + graph.query(import_query) + + ## get tool flag + flag_agent = True if input.strtype == "query" else False + flag_rag = True if input.strtype in ["query", "rag"] else False + + ## define LLM + if flag_agent or flag_rag: + llm_endpoint = os.getenv("LLM_ENDPOINT", "http://localhost:8080") + llm = HuggingFaceEndpoint( + endpoint_url=llm_endpoint, + timeout=600, + max_new_tokens=input.max_new_tokens, + ) + + ## define a retriever + if flag_rag: + vector_qa = get_retriever(input, neo4j_endpoint, neo4j_username, neo4j_password, llm) + + ## define an agent + if flag_agent: + llm_repo_id = os.getenv("AGENT_LLM", "HuggingFaceH4/zephyr-7b-beta") + cypher_chain = get_cypherchain(graph, llm, llm) # define a cypher generator + agent_executor = get_agent(vector_qa, cypher_chain, llm_repo_id) + + ## process input query + if input.strtype == "cypher": + result_dicts = graph.query(input.text) + result = "" + for result_dict in result_dicts: + for key in result_dict: + result += str(key) + ": " + str(result_dict[key]) + elif input.strtype == "rag": + result = vector_qa.invoke(input.text)["result"] + elif input.strtype == "query": + result = agent_executor.invoke({"input": input.text})["output"] + else: + result = "Please specify strtype as one of cypher, rag, query." + return GeneratedDoc(text=result, prompt=input.text) + + +if __name__ == "__main__": + opea_microservices["opea_service@knowledge_graph"].start() diff --git a/comps/knowledgegraphs/requirements.txt b/comps/knowledgegraphs/requirements.txt new file mode 100755 index 000000000..e368e2067 --- /dev/null +++ b/comps/knowledgegraphs/requirements.txt @@ -0,0 +1,29 @@ +beautifulsoup4 +docarray +docarray[full] +easyocr +fastapi +fastapi +huggingface_hub +langchain +langchain_community==0.2.5 +langchain_openai +langchainhub +langsmith +neo4j +numpy +opentelemetry-api +opentelemetry-api +opentelemetry-exporter-otlp +opentelemetry-exporter-otlp +opentelemetry-sdk +opentelemetry-sdk +pandas +pandas +Pillow +pymupdf +python-docx +redis +sentence-transformers +shortuuid +tiktoken diff --git a/comps/knowledgegraphs/test.py b/comps/knowledgegraphs/test.py new file mode 100644 index 000000000..72fefc144 --- /dev/null +++ b/comps/knowledgegraphs/test.py @@ -0,0 +1,36 @@ +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +import argparse +import json +import os +import timeit + +import pandas as pd +import requests + + +def test_text(ip_addr="localhost", batch_size=1): + proxies = {"http": ""} + url = "http://localhost:8060/v1/graphs" + + # payload = {"text":"MATCH (t:Task {status:'open'}) RETURN count(*)","strtype":"cypher"} + content = {"text": "MATCH (t:Task {status:'open'}) RETURN count(*)"} + payload = {"input": json.dumps(content)} + + try: + resp = requests.post(url=url, data=payload, proxies=proxies) + print(resp.text) + resp.raise_for_status() # Raise an exception for unsuccessful HTTP status codes + print("Request successful!") + except requests.exceptions.RequestException as e: + print("An error occurred:", e) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--batch_size", type=int, default=1, help="Batch size for testing") + parser.add_argument("--ip_addr", type=str, default="localhost", help="IP address of the server") + + args = parser.parse_args() + test_text(ip_addr=args.ip_addr, batch_size=args.batch_size)