Remove version used in video

This commit is contained in:
Lance Martin
2024-02-23 16:37:35 -08:00
parent e312092366
commit 7c64f82425
@@ -1,624 +0,0 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "54abe00a-0132-493a-bee0-5dcb3044c412",
"metadata": {},
"outputs": [],
"source": [
"! pip install langchain_community tiktoken langchain-openai langchainhub chromadb langchain langgraph faiss-cpu"
]
},
{
"cell_type": "markdown",
"id": "c02e806b-5169-4572-b007-3302df9801e0",
"metadata": {},
"source": [
"Optionally, use [LangSmith](https://docs.smith.langchain.com/) for tracing: \n",
"\n",
"```\n",
"export LANGCHAIN_TRACING_V2=true\n",
"export LANGCHAIN_ENDPOINT=https://api.smith.langchain.com\n",
"export LANGCHAIN_API_KEY=<your-api-key>\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "15bb14a1-4640-461d-9385-401ce346da75",
"metadata": {},
"source": [
"## Docs"
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "e471e650-97c2-4524-82c4-9bffaa8e6447",
"metadata": {},
"outputs": [],
"source": [
"from bs4 import BeautifulSoup as Soup\n",
"from langchain_community.document_loaders.recursive_url_loader import RecursiveUrlLoader\n",
"\n",
"# LCEL docs \n",
"url = \"https://python.langchain.com/docs/expression_language/\"\n",
"loader = RecursiveUrlLoader(\n",
" url=url, max_depth=20, extractor=lambda x: Soup(x, \"html.parser\").text\n",
")\n",
"docs = loader.load()\n",
"\n",
"# Sort the list based on the URLs in 'metadata' -> 'source'\n",
"d_sorted = sorted(docs, key=lambda x: x.metadata[\"source\"])\n",
"d_reversed = list(reversed(d_sorted))\n",
"\n",
"# Concatenate the 'page_content' of each sorted dictionary\n",
"concatenated_content = \"\\n\\n\\n --- \\n\\n\\n\".join(\n",
" [doc.page_content for doc in d_reversed]\n",
")"
]
},
{
"cell_type": "markdown",
"id": "5ccf1981-54b6-4667-b56e-a43dbfec35c7",
"metadata": {},
"source": [
"## Tool Use"
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "133889df-277a-4641-93cb-5dafa942b47a",
"metadata": {},
"outputs": [],
"source": [
"from operator import itemgetter\n",
"from langchain_openai import ChatOpenAI\n",
"from langchain.prompts import PromptTemplate\n",
"from langchain_core.pydantic_v1 import BaseModel, Field\n",
"from langchain.output_parsers.openai_tools import PydanticToolsParser\n",
"from langchain_core.utils.function_calling import convert_to_openai_tool\n",
" \n",
"## Data model\n",
"class code(BaseModel):\n",
" \"\"\"Code output\"\"\"\n",
" prefix: str = Field(description=\"Description of the problem and approach\")\n",
" imports: str = Field(description=\"Code block import statements\")\n",
" code: str = Field(description=\"Code block not including import statements\")\n",
"\n",
"## LLM\n",
"model = ChatOpenAI(temperature=0, model=\"gpt-4-0125-preview\", streaming=True)\n",
"\n",
"# Tool\n",
"code_tool_oai = convert_to_openai_tool(code)\n",
"\n",
"# LLM with tool and enforce invocation\n",
"llm_with_tool = model.bind(\n",
" tools=[code_tool_oai],\n",
" tool_choice={\"type\": \"function\", \"function\": {\"name\": \"code\"}},\n",
")\n",
"\n",
"# Parser\n",
"parser_tool = PydanticToolsParser(tools=[code])\n",
"\n",
"## Prompt\n",
"template = \"\"\"You are a coding assistant with expertise in LCEL, LangChain expression language. \\n \n",
" Here is a full set of LCEL documentation: \n",
" \\n ------- \\n\n",
" {context} \n",
" \\n ------- \\n\n",
" Answer the user question based on the above provided documentation. \\n\n",
" Ensure any code you provide can be executed with all required imports and variables defined. \\n\n",
" Structure your answer with a description of the code solution. \\n\n",
" Then list the imports. And finally list the functioning code block. \\n\n",
" Here is the user question: \\n --- --- --- \\n {question}\"\"\"\n",
"\n",
"# Prompt \n",
"prompt = PromptTemplate(\n",
" template=template,\n",
" input_variables=[\"context\", \"question\"],\n",
")\n",
"\n",
"# Chain\n",
"chain = (\n",
" {\n",
" # \"context\": lambda x: docs,\n",
" \"context\": lambda x: concatenated_content,\n",
" \"question\": itemgetter(\"question\"),\n",
" }\n",
" | prompt\n",
" | llm_with_tool \n",
" | parser_tool\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 19,
"id": "450cfac7-8a2a-43ed-a431-b38fb4d0de66",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[code(prefix=\"To create a Retrieval-Augmented Generation (RAG) chain in LangChain Expression Language (LCEL), you need to follow a structured approach. This involves setting up a retriever to fetch relevant documents based on the user's query, and then using those documents to generate a response with a language model. Here's a step-by-step guide to creating a RAG chain in LCEL:\\n\\n\", imports='from operator import itemgetter\\nfrom langchain_community.vectorstores import FAISS\\nfrom langchain_core.output_parsers import StrOutputParser\\nfrom langchain_core.prompts import ChatPromptTemplate\\nfrom langchain_core.runnables import RunnablePassthrough\\nfrom langchain_openai import ChatOpenAI, OpenAIEmbeddings', code='# Initialize the vector store with sample texts and embeddings\\nvectorstore = FAISS.from_texts(\\n [\"harrison worked at kensho\"], embedding=OpenAIEmbeddings())\\n\\n# Create a retriever from the vector store\\nretriever = vectorstore.as_retriever()\\n\\n# Define the prompt template\\ntemplate = \"\"\"Answer the question based only on the following context:{context}Question: {question}\"\"\"\\nprompt = ChatPromptTemplate.from_template(template)\\n\\n# Initialize the model\\nmodel = ChatOpenAI()\\n\\n# Create the RAG chain\\nchain = (\\n {\"context\": retriever, \"question\": RunnablePassthrough()} | prompt | model | StrOutputParser()\\n)\\n\\n# Invoke the chain with a query\\nresponse = chain.invoke(\"where did harrison work?\")\\nprint(response)')]"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"chain.invoke({\"question\":\"How to create a RAG chain in LCEL?\"})"
]
},
{
"cell_type": "markdown",
"id": "32a6686d-48d9-4b4c-becf-0d496384eed9",
"metadata": {},
"source": [
"## State"
]
},
{
"cell_type": "code",
"execution_count": 20,
"id": "95be35d1-b90c-4559-a6bf-2cd79e68ae4a",
"metadata": {},
"outputs": [],
"source": [
"from typing import Dict, TypedDict\n",
"\n",
"from langchain_core.messages import BaseMessage\n",
"\n",
"\n",
"class GraphState(TypedDict):\n",
" \"\"\"\n",
" Represents the state of our graph.\n",
"\n",
" Attributes:\n",
" keys: A dictionary where each key is a string.\n",
" \"\"\"\n",
"\n",
" keys: Dict[str, any]"
]
},
{
"cell_type": "code",
"execution_count": 21,
"id": "396bee5b-9cc3-44d2-bc6a-955af0e0a847",
"metadata": {},
"outputs": [],
"source": [
"from operator import itemgetter\n",
"from bs4 import BeautifulSoup as Soup\n",
"from langchain_community.document_loaders.recursive_url_loader import RecursiveUrlLoader\n",
"from langchain_openai import ChatOpenAI\n",
"from langchain.prompts import PromptTemplate\n",
"from langchain_core.output_parsers import StrOutputParser\n",
"from langchain_core.runnables import RunnablePassthrough\n",
"from langchain_core.pydantic_v1 import BaseModel, Field\n",
"from langchain.output_parsers import PydanticOutputParser\n",
"from langchain.output_parsers.openai_tools import PydanticToolsParser\n",
"from langchain_core.utils.function_calling import convert_to_openai_tool\n",
"\n",
"def generate(state):\n",
" \"\"\"\n",
" Generate a code solution based on LCEL docs and the input question \n",
" with optional feedback from code execution tests \n",
"\n",
" Args:\n",
" state (dict): The current graph state\n",
"\n",
" Returns:\n",
" state (dict): New key added to state, documents, that contains retrieved documents\n",
" \"\"\"\n",
" \n",
" ## State\n",
" state_dict = state[\"keys\"]\n",
" question = state_dict[\"question\"]\n",
" iter = state_dict[\"iterations\"]\n",
" \n",
" ## Data model\n",
" class code(BaseModel):\n",
" \"\"\"Code output\"\"\"\n",
" prefix: str = Field(description=\"Description of the problem and approach\")\n",
" imports: str = Field(description=\"Code block import statements\")\n",
" code: str = Field(description=\"Code block not including import statements\")\n",
" \n",
" ## LLM\n",
" model = ChatOpenAI(temperature=0, model=\"gpt-4-0125-preview\", streaming=True)\n",
" \n",
" # Tool\n",
" code_tool_oai = convert_to_openai_tool(code)\n",
" \n",
" # LLM with tool and enforce invocation\n",
" llm_with_tool = model.bind(\n",
" tools=[convert_to_openai_tool(code_tool_oai)],\n",
" tool_choice={\"type\": \"function\", \"function\": {\"name\": \"code\"}},\n",
" )\n",
" \n",
" # Parser\n",
" parser_tool = PydanticToolsParser(tools=[code])\n",
" \n",
" ## Prompt\n",
" template = \"\"\"You are a coding assistant with expertise in LCEL, LangChain expression language. \\n \n",
" Here is a full set of LCEL documentation: \n",
" \\n ------- \\n\n",
" {context} \n",
" \\n ------- \\n\n",
" Answer the user question based on the above provided documentation. \\n\n",
" Ensure any code you provide can be executed with all required imports and variables defined. \\n\n",
" Structure your answer with a description of the code solution. \\n\n",
" Then list the imports. And finally list the functioning code block. \\n\n",
" Here is the user question: \\n --- --- --- \\n {question}\"\"\"\n",
"\n",
" ## Generation\n",
" if \"error\" in state_dict:\n",
" print(\"---RE-GENERATE SOLUTION w/ ERROR FEEDBACK---\")\n",
" \n",
" error = state_dict[\"error\"]\n",
" code_solution = state_dict[\"generation\"]\n",
" \n",
" # Udpate prompt \n",
" addendum = \"\"\" \\n --- --- --- \\n You previously tried to solve this problem. \\n Here is your solution: \n",
" \\n --- --- --- \\n {generation} \\n --- --- --- \\n Here is the resulting error from code \n",
" execution: \\n --- --- --- \\n {error} \\n --- --- --- \\n Please re-try to answer this. \n",
" Structure your answer with a description of the code solution. \\n Then list the imports. \n",
" And finally list the functioning code block. Structure your answer with a description of \n",
" the code solution. \\n Then list the imports. And finally list the functioning code block. \n",
" \\n Here is the user question: \\n --- --- --- \\n {question}\"\"\"\n",
" template = template + addendum\n",
"\n",
" # Prompt \n",
" prompt = PromptTemplate(\n",
" template=template,\n",
" input_variables=[\"context\", \"question\", \"generation\", \"error\"],\n",
" )\n",
" \n",
" # Chain\n",
" chain = (\n",
" {\n",
" \"context\": lambda x: concatenated_content,\n",
" \"question\": itemgetter(\"question\"),\n",
" \"generation\": itemgetter(\"generation\"),\n",
" \"error\": itemgetter(\"error\"),\n",
" }\n",
" | prompt\n",
" | llm_with_tool \n",
" | parser_tool\n",
" )\n",
"\n",
" code_solution = chain.invoke({\"question\":question,\n",
" \"generation\":str(code_solution[0]),\n",
" \"error\":error})\n",
" \n",
" else:\n",
" print(\"---GENERATE SOLUTION---\")\n",
" \n",
" # Prompt \n",
" prompt = PromptTemplate(\n",
" template=template,\n",
" input_variables=[\"context\", \"question\"],\n",
" )\n",
"\n",
" # Chain\n",
" chain = (\n",
" {\n",
" # \"context\": lambda x: docs,\n",
" \"context\": lambda x: concatenated_content,\n",
" \"question\": itemgetter(\"question\"),\n",
" }\n",
" | prompt\n",
" | llm_with_tool \n",
" | parser_tool\n",
" )\n",
"\n",
" code_solution = chain.invoke({\"question\":question})\n",
"\n",
" iter = iter+1 \n",
" return {\"keys\": {\"generation\": code_solution, \"question\": question, \"iterations\": iter}}\n",
"\n",
"def check_code_imports(state):\n",
" \"\"\"\n",
" Check imports\n",
"\n",
" Args:\n",
" state (dict): The current graph state\n",
"\n",
" Returns:\n",
" state (dict): New key added to state, error\n",
" \"\"\"\n",
" \n",
" ## State\n",
" print(\"---CHECKING CODE IMPORTS---\")\n",
" state_dict = state[\"keys\"]\n",
" question = state_dict[\"question\"]\n",
" code_solution = state_dict[\"generation\"]\n",
" imports = code_solution[0].imports\n",
" iter = state_dict[\"iterations\"]\n",
"\n",
" try: \n",
" # Attempt to execute the imports\n",
" exec(imports)\n",
" except Exception as e:\n",
" print(\"---CODE IMPORT CHECK: FAILED---\")\n",
" # Catch any error during execution (e.g., ImportError, SyntaxError)\n",
" error = f\"Execution error: {e}\"\n",
" if \"error\" in state_dict:\n",
" error_prev_runs = state_dict[\"error\"]\n",
" error = error_prev_runs + \"\\n --- Most recent run error --- \\n\" + error \n",
" else:\n",
" print(\"---CODE IMPORT CHECK: SUCCESS---\")\n",
" # No errors occurred\n",
" error = \"None\"\n",
"\n",
" return {\"keys\": {\"generation\": code_solution, \"question\": question, \"error\": error, \"iterations\":iter}}\n",
"\n",
"def check_code_execution(state):\n",
" \"\"\"\n",
" Check code block execution\n",
"\n",
" Args:\n",
" state (dict): The current graph state\n",
"\n",
" Returns:\n",
" state (dict): New key added to state, error\n",
" \"\"\"\n",
" \n",
" ## State\n",
" print(\"---CHECKING CODE EXECUTION---\")\n",
" state_dict = state[\"keys\"]\n",
" question = state_dict[\"question\"]\n",
" code_solution = state_dict[\"generation\"]\n",
" prefix = code_solution[0].prefix\n",
" imports = code_solution[0].imports\n",
" code = code_solution[0].code\n",
" code_block = imports +\"\\n\"+ code\n",
" iter = state_dict[\"iterations\"]\n",
"\n",
" try: \n",
" # Attempt to execute the code block\n",
" exec(code_block)\n",
" except Exception as e:\n",
" print(\"---CODE BLOCK CHECK: FAILED---\")\n",
" # Catch any error during execution (e.g., ImportError, SyntaxError)\n",
" error = f\"Execution error: {e}\"\n",
" if \"error\" in state_dict:\n",
" error_prev_runs = state_dict[\"error\"]\n",
" error = error_prev_runs + \"\\n --- Most recent run error --- \\n\" + error \n",
" else:\n",
" print(\"---CODE BLOCK CHECK: SUCCESS---\")\n",
" # No errors occurred\n",
" error = \"None\"\n",
"\n",
" return {\"keys\": {\"generation\": code_solution, \n",
" \"question\": question, \n",
" \"error\": error, \n",
" \"prefix\":prefix,\n",
" \"imports\":imports,\n",
" \"iterations\":iter,\n",
" \"code\":code}}\n",
"\n",
"### Edges\n",
"\n",
"def decide_to_check_code_exec(state):\n",
" \"\"\"\n",
" Determines whether to test code execution, or re-try answer generation.\n",
"\n",
" Args:\n",
" state (dict): The current graph state\n",
"\n",
" Returns:\n",
" str: Next node to call\n",
" \"\"\"\n",
"\n",
" print(\"---DECIDE TO TEST CODE EXECUTION---\")\n",
" state_dict = state[\"keys\"]\n",
" question = state_dict[\"question\"]\n",
" code_solution = state_dict[\"generation\"]\n",
" error = state_dict[\"error\"]\n",
"\n",
" if error == \"None\":\n",
" # All documents have been filtered check_relevance\n",
" # We will re-generate a new query\n",
" print(\"---DECISION: TEST CODE EXECUTION---\")\n",
" return \"check_code_execution\"\n",
" else:\n",
" # We have relevant documents, so generate answer\n",
" print(\"---DECISION: RE-TRY SOLUTION---\")\n",
" return \"generate\"\n",
"\n",
"def decide_to_finish(state):\n",
" \"\"\"\n",
" Determines whether to finish (re-try code 3 times.\n",
"\n",
" Args:\n",
" state (dict): The current graph state\n",
"\n",
" Returns:\n",
" str: Next node to call\n",
" \"\"\"\n",
"\n",
" print(\"---DECIDE TO TEST CODE EXECUTION---\")\n",
" state_dict = state[\"keys\"]\n",
" question = state_dict[\"question\"]\n",
" code_solution = state_dict[\"generation\"]\n",
" error = state_dict[\"error\"]\n",
" iter = state_dict[\"iterations\"]\n",
"\n",
" if error == \"None\" or iter == 3:\n",
" # All documents have been filtered check_relevance\n",
" # We will re-generate a new query\n",
" print(\"---DECISION: TEST CODE EXECUTION---\")\n",
" return \"end\"\n",
" else:\n",
" # We have relevant documents, so generate answer\n",
" print(\"---DECISION: RE-TRY SOLUTION---\")\n",
" return \"generate\""
]
},
{
"cell_type": "code",
"execution_count": 23,
"id": "1e19e44d-1628-41ea-8d45-51ea3ebd3c00",
"metadata": {},
"outputs": [],
"source": [
"from langgraph.graph import END, StateGraph\n",
"\n",
"workflow = StateGraph(GraphState)\n",
"\n",
"# Define the nodes\n",
"workflow.add_node(\"generate\", generate) # generation solution\n",
"workflow.add_node(\"check_code_imports\", check_code_imports) # check imports\n",
"workflow.add_node(\"check_code_execution\", check_code_execution) # check execution\n",
"\n",
"# Build graph\n",
"workflow.set_entry_point(\"generate\")\n",
"workflow.add_edge(\"generate\", \"check_code_imports\")\n",
"workflow.add_conditional_edges(\n",
" \"check_code_imports\",\n",
" decide_to_check_code_exec,\n",
" {\n",
" \"check_code_execution\": \"check_code_execution\",\n",
" \"generate\": \"generate\",\n",
" },\n",
")\n",
"workflow.add_conditional_edges(\n",
" \"check_code_execution\",\n",
" decide_to_finish,\n",
" {\n",
" \"end\": END,\n",
" \"generate\": \"generate\",\n",
" },\n",
")\n",
"\n",
"# Compile\n",
"app = workflow.compile()"
]
},
{
"cell_type": "code",
"execution_count": 24,
"id": "8cf3924b-3121-4c52-8264-338ef2a82e14",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"---GENERATE SOLUTION---\n",
"---CHECKING CODE IMPORTS---\n",
"---CODE IMPORT CHECK: SUCCESS---\n",
"---DECIDE TO TEST CODE EXECUTION---\n",
"---DECISION: TEST CODE EXECUTION---\n",
"---CHECKING CODE EXECUTION---\n",
"---CODE BLOCK CHECK: FAILED---\n",
"---DECIDE TO TEST CODE EXECUTION---\n",
"---DECISION: RE-TRY SOLUTION---\n",
"---RE-GENERATE SOLUTION w/ ERROR FEEDBACK---\n",
"---CHECKING CODE IMPORTS---\n",
"---CODE IMPORT CHECK: SUCCESS---\n",
"---DECIDE TO TEST CODE EXECUTION---\n",
"---DECISION: TEST CODE EXECUTION---\n",
"---CHECKING CODE EXECUTION---\n",
"Why did the bear break up with his girlfriend?\n",
"Because he couldn't bear the relationship anymore!\n",
"---CODE BLOCK CHECK: SUCCESS---\n",
"---DECIDE TO TEST CODE EXECUTION---\n",
"---DECISION: TEST CODE EXECUTION---\n"
]
}
],
"source": [
"question = \"I am passing text key 'foo' to my prompt and want to process it with a function, process_text(...), prior to the prompt. How can I do this using LCEL?\"\n",
"config = {\"recursion_limit\": 50}\n",
"answer = app.invoke({\"keys\":{\"question\":question, \"iterations\":0}},config=config)"
]
},
{
"cell_type": "code",
"execution_count": 27,
"id": "eb8999e8-fa58-45b9-9506-3eb7ce32f0a0",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"\"To process a text key 'foo' with a function before passing it to a prompt using LCEL, you can use a RunnableLambda. This allows you to define a custom function that processes the input text and then passes the modified text to the prompt. Here's how you can do it:\""
]
},
"execution_count": 27,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"answer['keys']['generation'][0].prefix"
]
},
{
"cell_type": "code",
"execution_count": 30,
"id": "486205af-0e59-4b74-94eb-518cfc55b01f",
"metadata": {},
"outputs": [],
"source": [
"exec(answer['keys']['generation'][0].imports)"
]
},
{
"cell_type": "code",
"execution_count": 31,
"id": "216c3c91-1954-4ee4-bc7c-e78a0ea655d5",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Why did the bear break up with his girlfriend? \n",
"\n",
"Because he couldn't bear the relationship any longer!\n"
]
}
],
"source": [
"exec(answer['keys']['generation'][0].code)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4fc107f0-eecc-46f9-88d2-15c568537dfb",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.16"
}
},
"nbformat": 4,
"nbformat_minor": 5
}