diff --git a/examples/email_reply_agent.ipynb b/examples/email_reply_agent.ipynb index 8ef65ba7..099794b8 100644 --- a/examples/email_reply_agent.ipynb +++ b/examples/email_reply_agent.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 15, + "execution_count": 1, "metadata": {}, "outputs": [ { @@ -11,7 +11,7 @@ "True" ] }, - "execution_count": 15, + "execution_count": 1, "metadata": {}, "output_type": "execute_result" } @@ -19,7 +19,7 @@ "source": [ "from flo_ai import Flo\n", "from flo_ai import FloSession\n", - "from langchain_openai import ChatOpenAI\n", + "from langchain_openai import ChatOpenAI, OpenAIEmbeddings\n", "\n", "from dotenv import load_dotenv\n", "load_dotenv()" @@ -34,7 +34,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -76,7 +76,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -111,17 +111,104 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Register the tools" + "# Setup a retriver db" ] }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 4, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/vizsatiz/Documents/hub/flo/.venv/lib/python3.11/site-packages/sentence_transformers/cross_encoder/CrossEncoder.py:11: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from tqdm.autonotebook import tqdm, trange\n" + ] + } + ], "source": [ + "# import\n", + "from langchain_chroma import Chroma\n", + "from langchain_community.document_loaders import TextLoader\n", + "from langchain_community.embeddings.sentence_transformer import (\n", + " SentenceTransformerEmbeddings,\n", + ")\n", + "from langchain_text_splitters import CharacterTextSplitter\n", + "\n", + "# load the document and split it into chunks\n", + "loader = TextLoader(\"./rag_document.txt\")\n", + "documents = loader.load()\n", + "\n", + "# split it into chunks\n", + "text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)\n", + "docs = text_splitter.split_documents(documents)\n", + "\n", + "# create the open-source embedding function\n", + "embedding_function = SentenceTransformerEmbeddings(model_name=\"all-MiniLM-L6-v2\")\n", + "\n", + "# load it into Chroma\n", + "db = Chroma.from_documents(docs, embedding_function)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from flo_ai.retrievers.flo_retriever import FloRagBuilder\n", + "from flo_ai.retrievers.flo_compression_pipeline import FloCompressionPipeline\n", + "\n", "llm = ChatOpenAI(temperature=0, model_name='gpt-4o')\n", - "session = FloSession(llm).register_tool(\n", + "session = FloSession(llm)\n", + "builder = FloRagBuilder(session, db.as_retriever())\n", + "compression_pipeline = FloCompressionPipeline(OpenAIEmbeddings(model=\"text-embedding-3-small\"))\n", + "compression_pipeline.add_embedding_reduntant_filter()\n", + "compression_pipeline.add_embedding_relevant_filter()\n", + "\n", + "retriever_tool = builder.with_multi_query().with_compression(compression_pipeline).build_retriever_tool(name=\"HousingLoanRetreiver\",\n", + " description=\"Tool to fetch data around housing loans\")\n", + "session.register_tool(name=\"HousingLoanTool\", tool=retriever_tool)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Register the tools" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "session.register_tool(\n", " name=\"SendEmailTool\", \n", " tool=SendEmailTool()\n", ").register_tool(\n", @@ -139,7 +226,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -161,6 +248,10 @@ " prompt: You are capable of fetching any kind of transactions from the database given transaction reference id\n", " tools:\n", " - name: FetchTransactionTool\n", + " - name: HousingLoanTeamLead\n", + " prompt: Fetch the housing loan information from the db and answer the question\n", + " tools:\n", + " - name: HousingLoanTool\n", "\"\"\"" ] }, @@ -176,23 +267,47 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 21, "metadata": {}, "outputs": [], "source": [ - "email_input = \"\"\"\n", + "email_input_for_transaction_without_id = \"\"\"\n", "Hi Tom,\n", "\n", "We have a failed transaction. Can you tell me more about why this failed.\n", "\n", "Thanks\n", "Vishnu\n", + "\"\"\"\n", + "\n", + "email_input_for_transaction_with_id = \"\"\"\n", + "Hi Tom,\n", + "\n", + "We have a failed transaction. Can you tell me more about why this failed.\n", + "\n", + "Transaction ID: 12123123432\n", + "\n", + "Thanks\n", + "Vishnu\n", + "\"\"\"\n", + "\n", + "email_input_for_housing_loan = \"\"\"\n", + "Hi Tom,\n", + "\n", + "I am looking for a housing loan, is the interest percentage on the loan\n", + "\n", + "The tenure is 8 years\n", + "Amount am looking for is 85 lakhs\n", + "And am looking for fixed rate housing loan\n", + "\n", + "Thanks\n", + "Vishnu\n", "\"\"\"" ] }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ @@ -200,12 +315,12 @@ "You are a support ticket handling agent. You are expected to understand the email, and perform the following steps:\n", "\n", "1. Reply asking for more information if there is missing information in the email\n", - "2. Perform the neccessary steps to required to answer the users question and reply the user query.\n", + "2. Perform the neccessary steps to required to answer the users question and reply the user query as email.\n", "3. If are not able to come up with a answer just reply I dont know over the email\n", "\n", "Email:\n", "\n", - "\"\"\" + email_input" + "\"\"\" + email_input_for_transaction_with_id" ] }, { @@ -217,33 +332,65 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 23, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "{'SupportSupervisor-x7llc': {'next': 'TransactionFetcher-YdHZo'}}\n", + "{'SupportSupervisor-0XuY9': {'next': 'TransactionFetcher-7pEar'}}\n", "----\n", "\n", "\n", "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n", - "\u001b[32;1m\u001b[1;3mHi Vishnu,\n", + "\u001b[32;1m\u001b[1;3m\n", + "Invoking: `fetch_transactions` with `{'reference_number': '12123123432'}`\n", + "\n", + "\n", + "\u001b[0m\u001b[36;1m\u001b[1;3mThe transaction happened on 23/07/2024 IST and it failed because there was not enough balance in the account\u001b[0m\u001b[32;1m\u001b[1;3mSubject: Re: Failed Transaction Inquiry\n", + "\n", + "Hi Vishnu,\n", "\n", "Thank you for reaching out.\n", "\n", - "Could you please provide me with the transaction reference number for the failed transaction? This will help me to look into the details and provide you with the necessary information.\n", + "The transaction with ID 12123123432, which occurred on 23/07/2024 IST, failed due to insufficient balance in the account.\n", "\n", - "Looking forward to your response.\n", + "If you have any further questions or need additional assistance, please feel free to ask.\n", "\n", "Best regards, \n", "Tom\u001b[0m\n", "\n", "\u001b[1m> Finished chain.\u001b[0m\n", - "{'TransactionFetcher-YdHZo': {'messages': [HumanMessage(content='Hi Vishnu,\\n\\nThank you for reaching out.\\n\\nCould you please provide me with the transaction reference number for the failed transaction? This will help me to look into the details and provide you with the necessary information.\\n\\nLooking forward to your response.\\n\\nBest regards, \\nTom', name='TransactionFetcher-YdHZo')]}}\n", + "{'TransactionFetcher-7pEar': {'messages': [HumanMessage(content='Subject: Re: Failed Transaction Inquiry\\n\\nHi Vishnu,\\n\\nThank you for reaching out.\\n\\nThe transaction with ID 12123123432, which occurred on 23/07/2024 IST, failed due to insufficient balance in the account.\\n\\nIf you have any further questions or need additional assistance, please feel free to ask.\\n\\nBest regards, \\nTom', name='TransactionFetcher-7pEar')]}}\n", + "----\n", + "{'SupportSupervisor-0XuY9': {'next': 'EmailSender-YJNuR'}}\n", + "----\n", + "\n", + "\n", + "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n", + "\u001b[32;1m\u001b[1;3m\n", + "Invoking: `email_triage` with `{'email': 'Subject: Re: Failed Transaction Inquiry\\n\\nHi Vishnu,\\n\\nThank you for reaching out.\\n\\nThe transaction with ID 12123123432, which occurred on 23/07/2024 IST, failed due to insufficient balance in the account.\\n\\nIf you have any further questions or need additional assistance, please feel free to ask.\\n\\nBest regards,\\nTom'}`\n", + "\n", + "\n", + "\u001b[0mSubject: Re: Failed Transaction Inquiry\n", + "\n", + "Hi Vishnu,\n", + "\n", + "Thank you for reaching out.\n", + "\n", + "The transaction with ID 12123123432, which occurred on 23/07/2024 IST, failed due to insufficient balance in the account.\n", + "\n", + "If you have any further questions or need additional assistance, please feel free to ask.\n", + "\n", + "Best regards,\n", + "Tom\n", + "\u001b[36;1m\u001b[1;3mEmail sent successfully\u001b[0m\u001b[32;1m\u001b[1;3mI have sent the email to Vishnu with the details regarding the failed transaction. If there are any further questions or additional assistance needed, Vishnu can reach out again.\u001b[0m\n", + "\n", + "\u001b[1m> Finished chain.\u001b[0m\n", + "{'EmailSender-YJNuR': {'messages': [HumanMessage(content='I have sent the email to Vishnu with the details regarding the failed transaction. If there are any further questions or additional assistance needed, Vishnu can reach out again.', name='EmailSender-YJNuR')]}}\n", "----\n", - "{'SupportSupervisor-x7llc': {'next': 'FINISH'}}\n", + "{'SupportSupervisor-0XuY9': {'next': 'FINISH'}}\n", "----\n" ] } diff --git a/examples/history_aware_rag.py b/examples/history_aware_rag.py index 72b96607..da2199ee 100644 --- a/examples/history_aware_rag.py +++ b/examples/history_aware_rag.py @@ -35,6 +35,6 @@ logging.basicConfig() logging.getLogger("langchain.retrievers.multi_query").setLevel(logging.INFO) -rag = rag_builder.with_multi_query().build() -print(rag.invoke({ "question": "Tell me about corporate loans" })) +rag_tool = rag_builder.with_multi_query().build_rag_tool(name="RAGTool", description="RAG to answer question by looking at db") +print(rag_tool.invoke({"query": "What is the interest rate on housing loans"})) diff --git a/examples/rag_document.txt b/examples/rag_document.txt new file mode 100644 index 00000000..893a656e --- /dev/null +++ b/examples/rag_document.txt @@ -0,0 +1,61 @@ +Housing Loan + +Your dream home is never far away! Get hassle free home loans from Federal Bank to turn your dream home into reality. We assist you to realize your dream home. Avail your Housing Loan from us at competitive interest rates. The loan scheme assists borrowers for construction of house, acquisition of land & construction of house, repairs / renovation / remodeling / extension of house, reimbursement of debt incurred for construction / purchase / furnishing / beautification / purchase of flat / villa / house plots / takeover of housing loans / supplementary housing loan to employees of well-run companies / purchase of house plot for subsequent construction of house etc. + + + +Maximum funding for your dream house -85% of the project cost. +Loan amount up to Rs. 1500 Lakhs +Longer repayment period up to 360 months +Repayment holiday up to 36 months. +Loan can be availed in overdraft format +Minimal paperwork +Speedy loan approval +No pre closure charges +Low processing fees +Optional funding for life insurance +Easy top up loans in future. +Repayment of the loan is made as Equated Monthly Installments (EMI). + + +Eligibility +For Residents: + + + +For NRIs: + +NRI individuals including salaried people, self-employed and business persons are eligible for Housing Loan. +Persons of Indian Origin (PIO) are also eligible for Housing Loan subject to following conditions: +The loan is covered by primary / collateral security of immovable property in India in the name of PIO. +The PIO should hold a valid PIO Card accompanied by a valid foreign passport. +A close relative (as defined in Companies Act) residing in India should join as co-obligant to the loan. +Age of the borrower should not exceed 55 years at the end of loan tenure. +Monthly income should be not less than Rs. 50000/-. +To avail the loan, you can apply online in simple steps. After submitting the application, bank will process your application and will be informing further details. You can track the progress of application too. + + +Repayment of the loan is made as Equated Monthly Installments (EMI). + +You can use any one of the ways to repay the loan: + + Cheques + Standing instructions at your branch + FedNet - Internet Banking + Automated Payment through ECS + Mobile Banking + + Housing loan interest rates will change subject to the changes made by Bank/RBI from time to time. + +Present Repo Rate 6.50% (p.a) + + +Loan scheme + +Interest Rate (%) * + +Home Loan + +8.80 (Repo Rate+ 2.30) Onwards + +*T&C Apply \ No newline at end of file diff --git a/examples/rag_with_reranking.py b/examples/rag_with_reranking.py index 8e64afc4..686ebcce 100644 --- a/examples/rag_with_reranking.py +++ b/examples/rag_with_reranking.py @@ -1,7 +1,7 @@ +import os from langchain_mongodb import MongoDBAtlasVectorSearch from pymongo import MongoClient from langchain_openai import OpenAIEmbeddings -import os from langchain_openai import ChatOpenAI from dotenv import load_dotenv load_dotenv() @@ -51,6 +51,6 @@ compression_pipeline.add_embedding_reduntant_filter() compression_pipeline.add_embedding_relevant_filter() -rag = rag_builder.with_prompt(custom_prompt).with_multi_query().with_compression(compression_pipeline).build() +rag = rag_builder.with_prompt(custom_prompt).with_multi_query().with_compression(compression_pipeline).build_rag() print(rag.invoke({ "question": "What are the documents applying for housing loan" })) diff --git a/flo_ai/__init__.py b/flo_ai/__init__.py index da803678..0b6b0fee 100644 --- a/flo_ai/__init__.py +++ b/flo_ai/__init__.py @@ -3,3 +3,4 @@ from flo_ai.models.flo_supervisor import FloSupervisorBuilder from flo_ai.models.flo_team import FloTeamBuilder from flo_ai.state.flo_session import FloSession +from flo_ai.retrievers.flo_retriever import FloRagBuilder diff --git a/flo_ai/models/flo_agent.py b/flo_ai/models/flo_agent.py index 2dbde77a..d0a27960 100644 --- a/flo_ai/models/flo_agent.py +++ b/flo_ai/models/flo_agent.py @@ -1,6 +1,6 @@ from langchain_core.tools import BaseTool from langchain.agents import AgentExecutor -from langchain.agents import create_openai_tools_agent +from langchain.agents import create_tool_calling_agent from langchain_core.runnables import Runnable from langchain_core.language_models import BaseLanguageModel from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder @@ -48,7 +48,7 @@ def __init__(self, def build(self) -> AgentExecutor: - agent = create_openai_tools_agent(self.llm, self.tools, self.prompt) + agent = create_tool_calling_agent(self.llm, self.tools, self.prompt) executor = AgentExecutor(agent=agent, tools=self.tools, verbose=self.verbose, diff --git a/flo_ai/retrievers/flo_retriever.py b/flo_ai/retrievers/flo_retriever.py index 4dcbbc8b..eba610f8 100644 --- a/flo_ai/retrievers/flo_retriever.py +++ b/flo_ai/retrievers/flo_retriever.py @@ -1,5 +1,5 @@ from langchain_core.vectorstores import VectorStoreRetriever -from langchain_core.runnables import RunnableParallel +from langchain_core.runnables import RunnableParallel, Runnable from flo_ai.state.flo_session import FloSession from langchain.schema.output_parser import StrOutputParser from langchain.schema.runnable import RunnablePassthrough @@ -8,6 +8,9 @@ from langchain.retrievers import ContextualCompressionRetriever from langchain.retrievers.document_compressors import DocumentCompressorPipeline from flo_ai.retrievers.flo_compression_pipeline import FloCompressionPipeline +from langchain.tools.retriever import create_retriever_tool +from flo_ai.tools.flo_rag_tool import create_flo_rag_tool +from langchain_core.tools import Tool class FloRagBuilder(): def __init__(self, session: FloSession, retriever: VectorStoreRetriever) -> None: @@ -23,6 +26,7 @@ def __init__(self, session: FloSession, retriever: VectorStoreRetriever) -> None ("human", "{question}"), ] ) + self.history_aware_retriever = self.__create_history_aware_retriever() def with_prompt(self, prompt: ChatPromptTemplate): self.default_prompt = prompt @@ -60,7 +64,7 @@ def __create_history_aware_retriever(self): ] ) self.history_aware_retriever = contextualize_q_prompt | self.session.llm | StrOutputParser() - return self + return self.history_aware_retriever def __get_retriever(self): def __precontext_retriver(input_prompt: dict): @@ -77,7 +81,6 @@ def __get_optional_chat_history(self, x): return x["chat_history"] if "chat_history" in x else [] def __build_history_aware_rag(self): - self.history_aware_retriever = self.__create_history_aware_retriever() rag_chain = ( RunnablePassthrough.assign( context=(lambda x: x["context"]), @@ -95,5 +98,12 @@ def __build_history_aware_rag(self): ).assign(answer=rag_chain) return rag_chain_with_source - def build(self): + def build_rag(self): return self.__build_history_aware_rag() + + def build_retriever_tool(self, name, description): + return create_retriever_tool(self.retriever, name, description) + + def build_rag_tool(self, name, description) -> Tool: + rag = self.__build_history_aware_rag() + return create_flo_rag_tool(rag, name, description) diff --git a/flo_ai/tools/flo_rag_tool.py b/flo_ai/tools/flo_rag_tool.py new file mode 100644 index 00000000..97b3944e --- /dev/null +++ b/flo_ai/tools/flo_rag_tool.py @@ -0,0 +1,39 @@ +from langchain.pydantic_v1 import BaseModel, Field +from langchain.tools import Tool +from langchain_core.runnables import Runnable +from functools import partial + +class FloRagToolInput(BaseModel): + query: str = Field(description="query to look up in retriever") + +def __get_rag_answer(query: str, runnable: Runnable): + result = runnable.invoke({ "question": query }) + return result["answer"].content + +async def __aget_rag_answer(query: str, runnable: Runnable): + result = await runnable.ainvoke({ "question": query }) + return result["answer"].content + +def create_flo_rag_tool( + runnable_rag: Runnable, + name: str, + description: str +) -> Tool: + func = partial( + __get_rag_answer, + runnable=runnable_rag + ) + + afunc = partial( + __aget_rag_answer, + runnable=runnable_rag + ) + + return Tool( + name=name, + description=description, + func=func, + coroutine=afunc, + args_schema=FloRagToolInput, + ) +