├── README.md ├── agent ├── build_agent.py ├── build_agent_w_memory.py └── xml_agent.py ├── client.py ├── document_loaders ├── csv_loader.py ├── csv_sample.csv ├── csv_sample.pdf ├── csv_sample_no_header.csv ├── facebook_chat.json ├── html_loader.py ├── integration.py ├── json_loader.py ├── pdf_loader.py └── sample.html ├── document_transformers ├── code_splitters.py ├── split_by_token.py ├── state_of_the_union.txt └── text_splitters.py ├── few_shot_prompt.py ├── langgraph └── chatbot │ ├── README.md │ ├── chatbot_with_customizing_state.py │ ├── chatbot_with_memory.json │ ├── chatbot_with_memory.py │ ├── chatbot_with_memory_sqlite.py │ ├── chatbot_with_time_travel.py │ ├── chatbot_with_tool.py │ ├── human_in_the_loop.py │ ├── lat.ipynb │ ├── lat.png │ ├── lat_chart.jpeg │ ├── pyproject.toml │ ├── reflection.png │ ├── reflection.py │ ├── reflexion.png │ ├── reflexion.py │ ├── schema.py │ ├── simple_chatbot.py │ └── uv.lock ├── langserve ├── client.py ├── sdk_client.py └── server.py ├── langsmith ├── .python-version ├── README.md ├── create_prompt.py ├── evaluation.py ├── main.py ├── pull_prompt.py ├── pyproject.toml └── uv.lock ├── lcel ├── Chinook.db ├── Chinook_Sqlite.sql ├── code_writing.py ├── faiss_index │ ├── index.faiss │ └── index.pkl ├── multiple_chains.py ├── prompt_llm_function_call.py ├── prompt_llm_output_parser.py ├── querying_sql_db.py ├── rag.py ├── runnable_branch.py ├── runnable_lambda.py └── runnable_parallel.py ├── llm_chatmodels.py ├── mcp ├── README.md ├── client.py ├── math_server.py ├── mcp_doc_messages.json ├── mcpdoc_server.py ├── messages.json ├── multi_server_client.py ├── requirements.txt ├── weather_messages.json └── weather_server.py ├── memory ├── adding_memory.py ├── conversation_buffer_window_memory.py ├── conversation_summary_buffer_memory.py ├── conversation_summary_memory.py ├── entity.py └── memory_example.py ├── output_parser.py ├── output_parsers ├── auto_fixing_parser.py ├── datetime_parser.py ├── enum_parser.py ├── list_parser.py ├── pydantic_parser_actor.py ├── pydantic_parser_joke.py └── structured_output_parser.py ├── prompt_template.py ├── pydantic_example ├── quickstart.py ├── validation_fail.py └── validation_successful.py ├── quickstart.py ├── requirements.txt ├── retriever ├── context_compression.py ├── multi_query_retriever.py └── state_of_the_union.txt ├── serialization ├── example_json.py ├── example_yaml.py ├── simple_prompt.json └── simple_prompt.yaml ├── serve.py ├── simple_prompt.json ├── simple_prompt.yaml ├── text_embedding ├── csv_sample.csv └── text_embedding.py └── vector_stores ├── .DS_Store ├── fortune_500_2020.csv ├── fortune_500_db ├── 3ac7dac4-a1b8-4432-9306-3bec6614ef1c │ ├── data_level0.bin │ ├── header.bin │ ├── index_metadata.pickle │ ├── length.bin │ └── link_lists.bin └── chroma.sqlite3 ├── quickstart.py └── state_of_the_union.txt /README.md: -------------------------------------------------------------------------------- 1 | # learn-langchain 2 | 3 | For the latest update version of langchain, Please refer to requirements.txt 4 | -------------------------------------------------------------------------------- /agent/build_agent.py: -------------------------------------------------------------------------------- 1 | # Note as of 02/27/2024 2 | # before you start you need to install the following 3 | # pip install langchain==0.1.9 langchain-openai==0.0.8 4 | from langchain_openai import ChatOpenAI 5 | 6 | llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0) 7 | 8 | print(llm.invoke("how many letters in the word educa?")) 9 | """ 10 | content='There are 6 letters in the word "educa".' 11 | """ 12 | 13 | 14 | # Build a custom tool 15 | from langchain.agents import tool 16 | 17 | @tool 18 | def get_word_length(word: str) -> int: 19 | """Returns the length of a word.""" 20 | return len(word) 21 | 22 | tools = [get_word_length] 23 | 24 | 25 | 26 | 27 | from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder 28 | 29 | prompt = ChatPromptTemplate.from_messages( 30 | [ 31 | ( 32 | "system", 33 | "You are very powerful assistant, but bad at calculating lengths of words.", 34 | ), 35 | ("user", "{input}"), 36 | MessagesPlaceholder(variable_name="agent_scratchpad"), 37 | ] 38 | ) 39 | # input: should be a string containing the user objective 40 | # agent_scratchpad: should be a sequence of messages that contains 41 | # the previous agent tool invocations and 42 | # the corresponding tool outputs 43 | 44 | 45 | # How does the agent know what tools it can use? 46 | # In this case we’re relying on OpenAI function calling LLMs, 47 | # which take functions as a separate argument and 48 | # have been specifically trained to know when to invoke those functions. 49 | from langchain_core.utils.function_calling import convert_to_openai_function 50 | 51 | llm_with_tools = llm.bind( 52 | functions=[ 53 | convert_to_openai_function(t) for t in tools # type: ignore 54 | ]) 55 | 56 | 57 | from langchain.agents.format_scratchpad import format_to_openai_function_messages 58 | from langchain.agents.output_parsers import OpenAIFunctionsAgentOutputParser 59 | 60 | agent = ( 61 | { 62 | "input": lambda x: x["input"], 63 | "agent_scratchpad": lambda x: format_to_openai_function_messages( 64 | x["intermediate_steps"] 65 | ), 66 | } 67 | | prompt 68 | | llm_with_tools 69 | | OpenAIFunctionsAgentOutputParser() 70 | ) 71 | 72 | print(agent.invoke({"input": "how many letters in the word educa?", "intermediate_steps": []})) 73 | """ 74 | tool='get_word_length' 75 | tool_input={'word': 'educa'} 76 | log="\nInvoking: `get_word_length` with `{'word': 'educa'}`\n\n\n" 77 | message_log=[ 78 | AIMessage( 79 | content='', 80 | additional_kwargs={ 81 | 'function_call': { 82 | 'arguments': '{\n "word": "educa"\n}', 83 | 'name': 'get_word_length'}})] 84 | """ 85 | 86 | 87 | 88 | # Define Runtime 89 | from langchain.schema import AgentFinish 90 | 91 | user_input = "how many letters in the word educa?" 92 | intermediate_steps = [] 93 | while True: 94 | output = agent.invoke( 95 | { 96 | "input": user_input, 97 | "intermediate_steps": intermediate_steps, 98 | } 99 | ) 100 | 101 | # AgentFinish: This signifies that the agent has finished and 102 | # should return to the user 103 | if isinstance(output, AgentFinish): 104 | final_result = output.return_values["output"] 105 | break 106 | else: 107 | # AgentAction: This represents the action an agent should take 108 | print(f"TOOL NAME: {output.tool}") 109 | print(f"TOOL INPUT: {output.tool_input}") 110 | tool = {"get_word_length": get_word_length}[output.tool] 111 | """ 112 | tool = {"x": lambda a: a+1}['x'] 113 | result = tool(3) # This will set 'result' to 4 114 | """ 115 | observation = tool.run(output.tool_input) 116 | intermediate_steps.append((output, observation)) 117 | print(final_result) 118 | """ 119 | TOOL NAME: get_word_length 120 | TOOL INPUT: {'word': 'educa'} 121 | There are 5 letters in the word "educa". 122 | """ 123 | 124 | 125 | 126 | # Using AgentExcutor 127 | from langchain.agents import AgentExecutor 128 | 129 | agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True) # type: ignore 130 | print(agent_executor.invoke({"input": "how many letters in the word educa?"})) 131 | """ 132 | > Entering new AgentExecutor chain... 133 | 134 | Invoking: `get_word_length` with `{'word': 'educa'}` 135 | 136 | 137 | There are 5 letters in the word "educa". 138 | 139 | > Finished chain. 140 | {'input': 'how many letters in the word educa?', 'output': 'There are 5 letters in the word "educa".'} 141 | """ -------------------------------------------------------------------------------- /agent/build_agent_w_memory.py: -------------------------------------------------------------------------------- 1 | # Note as of 02/27/2024 2 | # before you start you need to install the following 3 | # pip install langchain==0.1.9 langchain-openai==0.0.8 4 | from langchain_openai import ChatOpenAI 5 | from langchain.agents import tool 6 | from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder 7 | from langchain_core.utils.function_calling import convert_to_openai_function 8 | 9 | @tool 10 | def get_word_length(word: str) -> int: 11 | """Returns the length of a word.""" 12 | return len(word) 13 | 14 | tools = [get_word_length] 15 | 16 | llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0) 17 | 18 | MEMORY_KEY = "chat_history" 19 | prompt = ChatPromptTemplate.from_messages( 20 | [ 21 | ( 22 | "system", 23 | "You are very powerful assistant, but bad at calculating lengths of words.", 24 | ), 25 | MessagesPlaceholder(variable_name=MEMORY_KEY), 26 | ("user", "{input}"), 27 | MessagesPlaceholder(variable_name="agent_scratchpad"), 28 | ] 29 | ) 30 | 31 | llm_with_tools = llm.bind( 32 | functions=[ 33 | convert_to_openai_function(t) for t in tools # type: ignore 34 | ]) 35 | 36 | 37 | from langchain.agents.format_scratchpad import format_to_openai_function_messages 38 | from langchain.agents.output_parsers import OpenAIFunctionsAgentOutputParser 39 | from langchain.schema.messages import AIMessage, HumanMessage 40 | from langchain.agents import AgentExecutor 41 | 42 | 43 | agent = ( 44 | { 45 | "input": lambda x: x["input"], 46 | "agent_scratchpad": lambda x: format_to_openai_function_messages( 47 | x["intermediate_steps"] 48 | ), 49 | "chat_history": lambda x: x["chat_history"], 50 | } 51 | | prompt 52 | | llm_with_tools 53 | | OpenAIFunctionsAgentOutputParser() 54 | ) 55 | 56 | chat_history = [] 57 | 58 | agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True) # type: ignore 59 | 60 | input1 = "how many letters in the word educa?" 61 | result = agent_executor.invoke({"input": input1, "chat_history": chat_history}) 62 | print(result) 63 | """ 64 | {'input': 'how many letters in the word educa?', 'chat_history': [], 'output': 'There are 5 letters in the word "educa".'} 65 | """ 66 | 67 | chat_history.extend( 68 | [ 69 | HumanMessage(content=input1), 70 | AIMessage(content=result["output"]), 71 | ] 72 | ) 73 | print(agent_executor.invoke({"input": "is that a real word?", "chat_history": chat_history})) 74 | """ 75 | { 76 | 'input': 'is that a real word?', 77 | 'chat_history': [ 78 | HumanMessage(content='how many letters in the word educa?'), 79 | AIMessage(content='There are 5 letters in the word "educa".')], 80 | 'output': '"Educa" is not a common English word. It seems to be a shortened form of the word "education".'} 81 | """ 82 | -------------------------------------------------------------------------------- /agent/xml_agent.py: -------------------------------------------------------------------------------- 1 | from langchain.agents import AgentExecutor, XMLAgent, tool 2 | from langchain_openai import ChatOpenAI 3 | 4 | llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0) 5 | 6 | @tool 7 | def search(query: str) -> str: 8 | """Search things about current events.""" 9 | return "32 degrees" 10 | 11 | tool_list = [search] 12 | 13 | # Get prompt to use 14 | prompt = XMLAgent.get_default_prompt() 15 | """ 16 | input_variables=['intermediate_steps', 'question', 'tools'] 17 | messages=[ 18 | HumanMessagePromptTemplate( 19 | prompt=PromptTemplate( 20 | input_variables=['question', 'tools'], 21 | template="You are a helpful assistant. Help the user answer any questions.\n\n 22 | You have access to the following tools:\n\n{tools}\n\n 23 | In order to use a tool, you can use and tags. 24 | You will then get back a response in the form \n 25 | For example, if you have a tool called 'search' that could run a google search, 26 | in order to search for the weather in SF 27 | you would respond:\n\n 28 | search 29 | weather in SF\n 30 | 64 degrees\n\n 31 | When you are done, respond with a final answer between 32 | . 33 | 34 | For example:\n\nThe weather in SF is 64 degrees\n\n 35 | Begin!\n\nQuestion: {question}")), 36 | AIMessagePromptTemplate( 37 | prompt=PromptTemplate( 38 | input_variables=['intermediate_steps'], 39 | template='{intermediate_steps}'))] 40 | """ 41 | # Logic for going from intermediate steps to a string to pass into model 42 | # This is pretty tied to the prompt 43 | def convert_intermediate_steps(intermediate_steps): 44 | log = "" 45 | for action, observation in intermediate_steps: 46 | log += ( 47 | f"{action.tool}{action.tool_input}" 48 | f"{observation}" 49 | ) 50 | return log 51 | 52 | 53 | # Logic for converting tools to string to go in prompt 54 | def convert_tools(tools): 55 | return "\n".join([f"{tool.name}: {tool.description}" for tool in tools]) 56 | 57 | agent = ( 58 | { 59 | "question": lambda x: x["question"], 60 | "intermediate_steps": lambda x: convert_intermediate_steps( 61 | x["intermediate_steps"] 62 | ), 63 | } 64 | | prompt.partial(tools=convert_tools(tool_list)) # prompt.partial: Get a new ChatPromptTemplate with some input variables already filled in 65 | | llm.bind(stop=["", ""]) 66 | | XMLAgent.get_default_output_parser() 67 | ) 68 | """ 69 | Expects output to be in one of two formats. 70 | 71 | If the output signals that an action should be taken, 72 | should be in the below format. This will result in an AgentAction 73 | being returned. 74 | 75 | ``` 76 | search 77 | what is 2 + 2 78 | ``` 79 | 80 | If the output signals that a final answer should be given, 81 | should be in the below format. This will result in an AgentFinish 82 | being returned. 83 | 84 | ``` 85 | Foo 86 | ``` 87 | """ 88 | 89 | agent_executor = AgentExecutor(agent=agent, tools=tool_list, verbose=True) # type: ignore 90 | 91 | print(agent_executor.invoke({"question": "whats the weather in New york?"})) 92 | -------------------------------------------------------------------------------- /client.py: -------------------------------------------------------------------------------- 1 | from langserve import RemoteRunnable 2 | 3 | remote_chain = RemoteRunnable("http://localhost:8000/category_chain/") 4 | print(remote_chain.invoke({"text": "colors"})) 5 | -------------------------------------------------------------------------------- /document_loaders/csv_loader.py: -------------------------------------------------------------------------------- 1 | # Note as of 02/27/2024 2 | # before you start you need to install the following 3 | # pip install langchain==0.1.9 langchain-openai==0.0.8 4 | from langchain_community.document_loaders import CSVLoader 5 | 6 | loader = CSVLoader(file_path='./csv_sample.csv') 7 | data = loader.load() 8 | print(type(data[0])) 9 | """ 10 | 11 | """ 12 | 13 | print(data) 14 | """ 15 | [Document(page_content='name: Neville Hardy\nage: 56\ncountry: Niue', metadata={'source': './csv_sample.csv', 'row': 0}), Document(page_content='name: Dacia Cohen\nage: 74\ncountry: Falkland Islands (Malvinas)', metadata={'source': './csv_sample.csv', 'row': 1}), Document(page_content='name: Kathey Daniel\nage: 10\ncountry: Slovenia', metadata={'source': './csv_sample.csv', 'row': 2}), Document(page_content='name: Mallie Welch\nage: 12\ncountry: Equatorial Guinea', metadata={'source': './csv_sample.csv', 'row': 3}), Document(page_content='name: Katia Bryant\nage: 14\ncountry: Ghana', metadata={'source': './csv_sample.csv', 'row': 4}), Document(page_content='name: Laurice Robertson\nage: 53\ncountry: Saudi Arabia', metadata={'source': './csv_sample.csv', 'row': 5}), Document(page_content='name: Minh Barrett\nage: 27\ncountry: French Southern Territories', metadata={'source': './csv_sample.csv', 'row': 6}), Document(page_content='name: Latashia Perez\nage: 52\ncountry: Finland', metadata={'source': './csv_sample.csv', 'row': 7}), Document(page_content='name: Elvina Ross\nage: 68\ncountry: New Zealand', metadata={'source': './csv_sample.csv', 'row': 8})] 16 | """ 17 | 18 | print(data[0].page_content) 19 | """ 20 | name: Neville Hardy 21 | age: 56 22 | country: Niue 23 | """ 24 | 25 | print(data[0].metadata) 26 | """ 27 | {'source': './csv_sample.csv', 'row': 0} 28 | """ 29 | 30 | loader = CSVLoader( 31 | file_path='./csv_sample_no_header.csv', 32 | csv_args={ 33 | 'delimiter': ',', 34 | 'quotechar': '"', 35 | 'fieldnames': ['name', 'age', 'country'] 36 | }) 37 | 38 | data = loader.load() 39 | print(data[1].page_content) 40 | """ 41 | name: Dacia Cohen 42 | age: 74 43 | country: Falkland Islands (Malvinas) 44 | """ -------------------------------------------------------------------------------- /document_loaders/csv_sample.csv: -------------------------------------------------------------------------------- 1 | name,age,country 2 | Neville Hardy,56,Niue 3 | Dacia Cohen,74,Falkland Islands (Malvinas) 4 | Kathey Daniel,10,Slovenia 5 | Mallie Welch,12,Equatorial Guinea 6 | Katia Bryant,14,Ghana 7 | Laurice Robertson,53,Saudi Arabia 8 | Minh Barrett,27,French Southern Territories 9 | Latashia Perez,52,Finland 10 | Elvina Ross,68,New Zealand -------------------------------------------------------------------------------- /document_loaders/csv_sample.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimz119/learn-langchain/f0c2604aab7cab7f2c4d44d7bb484fd0dc2463bc/document_loaders/csv_sample.pdf -------------------------------------------------------------------------------- /document_loaders/csv_sample_no_header.csv: -------------------------------------------------------------------------------- 1 | Neville Hardy,56,Niue 2 | Dacia Cohen,74,Falkland Islands (Malvinas) 3 | Kathey Daniel,10,Slovenia 4 | Mallie Welch,12,Equatorial Guinea 5 | Katia Bryant,14,Ghana 6 | Laurice Robertson,53,Saudi Arabia 7 | Minh Barrett,27,French Southern Territories 8 | Latashia Perez,52,Finland 9 | Elvina Ross,68,New Zealand -------------------------------------------------------------------------------- /document_loaders/facebook_chat.json: -------------------------------------------------------------------------------- 1 | { 2 | "image": { 3 | "creation_timestamp": 1675549016, 4 | "uri": "image_of_the_chat.jpg" 5 | }, 6 | "is_still_participant": true, 7 | "joinable_mode": { 8 | "link": "", 9 | "mode": 1 10 | }, 11 | "magic_words": [], 12 | "messages": [ 13 | { 14 | "content": "Bye!", 15 | "sender_name": "User 2", 16 | "timestamp_ms": 1675597571851 17 | }, 18 | { 19 | "content": "Oh no worries! Bye", 20 | "sender_name": "User 1", 21 | "timestamp_ms": 1675597435669 22 | }, 23 | { 24 | "content": "No Im sorry it was my mistake, the blue one is not for sale", 25 | "sender_name": "User 2", 26 | "timestamp_ms": 1675596277579 27 | }, 28 | { 29 | "content": "I thought you were selling the blue one!", 30 | "sender_name": "User 1", 31 | "timestamp_ms": 1675595140251 32 | }, 33 | { 34 | "content": "Im not interested in this bag. Im interested in the blue one!", 35 | "sender_name": "User 1", 36 | "timestamp_ms": 1675595109305 37 | }, 38 | { 39 | "content": "Here is $129", 40 | "sender_name": "User 2", 41 | "timestamp_ms": 1675595068468 42 | }, 43 | { 44 | "photos": [ 45 | { 46 | "creation_timestamp": 1675595059, 47 | "uri": "url_of_some_picture.jpg" 48 | } 49 | ], 50 | "sender_name": "User 2", 51 | "timestamp_ms": 1675595060730 52 | }, 53 | { 54 | "content": "Online is at least $100", 55 | "sender_name": "User 2", 56 | "timestamp_ms": 1675595045152 57 | }, 58 | { 59 | "content": "How much do you want?", 60 | "sender_name": "User 1", 61 | "timestamp_ms": 1675594799696 62 | }, 63 | { 64 | "content": "Goodmorning! $50 is too low.", 65 | "sender_name": "User 2", 66 | "timestamp_ms": 1675577876645 67 | }, 68 | { 69 | "content": "Hi! Im interested in your bag. Im offering $50. Let me know if you are interested. Thanks!", 70 | "sender_name": "User 1", 71 | "timestamp_ms": 1675549022673 72 | } 73 | ], 74 | "participants": [ 75 | { 76 | "name": "User 1" 77 | }, 78 | { 79 | "name": "User 2" 80 | } 81 | ], 82 | "thread_path": "inbox/User 1 and User 2 chat", 83 | "title": "User 1 and User 2 chat" 84 | } 85 | -------------------------------------------------------------------------------- /document_loaders/html_loader.py: -------------------------------------------------------------------------------- 1 | # Note as of 02/27/2024 2 | # before you start you need to install the following 3 | # pip install langchain==0.1.9 langchain-openai==0.0.8 4 | from langchain_community.document_loaders import UnstructuredHTMLLoader 5 | 6 | # requires `pip install unstructured` 7 | loader = UnstructuredHTMLLoader("sample.html") 8 | data = loader.load() 9 | print(data) 10 | """ 11 | [Document(page_content="Welcome to My Web Page\n\nThis is a simple HTML page. It's a great starting point for learning HTML.\n\nClick here to visit Example.com", metadata={'source': 'sample.html'})] 12 | """ 13 | 14 | from langchain.document_loaders import BSHTMLLoader 15 | 16 | # requires `pip install beautifulsoup4 lxml` 17 | loader = BSHTMLLoader("sample.html") 18 | data = loader.load() 19 | print(data) 20 | """ 21 | [Document(page_content="\n\nMy Simple Web Page\n\n\nWelcome to My Web Page\nThis is a simple HTML page. It's a great starting point for learning HTML.\nClick here to visit Example.com\n\n\n", metadata={'source': 'sample.html', 'title': 'My Simple Web Page'})] 22 | """ 23 | 24 | print(data[0].page_content) 25 | """ 26 | 27 | 28 | My Simple Web Page 29 | 30 | 31 | Welcome to My Web Page 32 | This is a simple HTML page. It's a great starting point for learning HTML. 33 | Click here to visit Example.com 34 | 35 | 36 | 37 | """ -------------------------------------------------------------------------------- /document_loaders/integration.py: -------------------------------------------------------------------------------- 1 | # Note as of 02/27/2024 2 | # before you start you need to install the following 3 | # pip install langchain==0.1.9 langchain-openai==0.0.8 4 | from langchain_openai import ChatOpenAI 5 | from langchain_community.document_loaders import CSVLoader 6 | from langchain.prompts.chat import ChatPromptTemplate 7 | 8 | template = "You are a helpful assistant that extract the {column} given the data `{data}`" 9 | human_template = "What is the age of {name}" 10 | 11 | chat_prompt = ChatPromptTemplate.from_messages([ 12 | ("system", template), # role 13 | ("human", human_template), # content 14 | ]) 15 | 16 | loader = CSVLoader(file_path='./csv_sample.csv') 17 | data = loader.load() 18 | 19 | text_list = [] 20 | for record in data: 21 | text_list.append(record.page_content) 22 | 23 | chat_prompt_output = chat_prompt.format_messages( 24 | column="age", 25 | data=("\n".join(text_list)), 26 | name="Minh Barrett") 27 | print(chat_prompt_output) 28 | 29 | chat_model = ChatOpenAI() 30 | print(chat_model.invoke(chat_prompt_output)) 31 | -------------------------------------------------------------------------------- /document_loaders/json_loader.py: -------------------------------------------------------------------------------- 1 | # Note as of 02/27/2024 2 | # before you start you need to install the following 3 | # pip install langchain==0.1.9 langchain-openai==0.0.8 4 | from langchain_community.document_loaders import JSONLoader 5 | import json 6 | from pathlib import Path 7 | from pprint import pprint 8 | 9 | # required `pip install jq` 10 | file_path='./facebook_chat.json' 11 | data = json.loads(Path(file_path).read_text()) 12 | pprint(data) 13 | """ 14 | {'image': {'creation_timestamp': 1675549016, 'uri': 'image_of_the_chat.jpg'}, 15 | 'is_still_participant': True, 16 | 'joinable_mode': {'link': '', 'mode': 1}, 17 | 'magic_words': [], 18 | 'messages': [{'content': 'Bye!', 19 | 'sender_name': 'User 2', 20 | 'timestamp_ms': 1675597571851}, 21 | {'content': 'Oh no worries! Bye', 22 | 'sender_name': 'User 1', 23 | 'timestamp_ms': 1675597435669}, 24 | {'content': 'No Im sorry it was my mistake, the blue one is not ' 25 | 'for sale', 26 | 'sender_name': 'User 2', 27 | 'timestamp_ms': 1675596277579}, 28 | {'content': 'I thought you were selling the blue one!', 29 | 'sender_name': 'User 1', 30 | 'timestamp_ms': 1675595140251}, 31 | {'content': 'Im not interested in this bag. Im interested in the ' 32 | 'blue one!', 33 | 'sender_name': 'User 1', 34 | 'timestamp_ms': 1675595109305}, 35 | {'content': 'Here is $129', 36 | 'sender_name': 'User 2', 37 | 'timestamp_ms': 1675595068468}, 38 | {'photos': [{'creation_timestamp': 1675595059, 39 | 'uri': 'url_of_some_picture.jpg'}], 40 | 'sender_name': 'User 2', 41 | 'timestamp_ms': 1675595060730}, 42 | {'content': 'Online is at least $100', 43 | 'sender_name': 'User 2', 44 | 'timestamp_ms': 1675595045152}, 45 | {'content': 'How much do you want?', 46 | 'sender_name': 'User 1', 47 | 'timestamp_ms': 1675594799696}, 48 | {'content': 'Goodmorning! $50 is too low.', 49 | 'sender_name': 'User 2', 50 | 'timestamp_ms': 1675577876645}, 51 | {'content': 'Hi! Im interested in your bag. Im offering $50. Let ' 52 | 'me know if you are interested. Thanks!', 53 | 'sender_name': 'User 1', 54 | 'timestamp_ms': 1675549022673}], 55 | 'participants': [{'name': 'User 1'}, {'name': 'User 2'}], 56 | 'thread_path': 'inbox/User 1 and User 2 chat', 57 | 'title': 'User 1 and User 2 chat'} 58 | """ 59 | 60 | loader = JSONLoader( 61 | file_path='./facebook_chat.json', 62 | jq_schema='.messages[].content', 63 | text_content=False) 64 | 65 | data = loader.load() 66 | pprint(data) 67 | """ 68 | [Document(page_content='Bye!', metadata={'source': '/Users/seungjoonlee/git/learn-langchain/document_loader/facebook_chat.json', 'seq_num': 1}), 69 | Document(page_content='Oh no worries! Bye', metadata={'source': '/Users/seungjoonlee/git/learn-langchain/document_loader/facebook_chat.json', 'seq_num': 2}), 70 | Document(page_content='No Im sorry it was my mistake, the blue one is not for sale', metadata={'source': '/Users/seungjoonlee/git/learn-langchain/document_loader/facebook_chat.json', 'seq_num': 3}), 71 | Document(page_content='I thought you were selling the blue one!', metadata={'source': '/Users/seungjoonlee/git/learn-langchain/document_loader/facebook_chat.json', 'seq_num': 4}), 72 | Document(page_content='Im not interested in this bag. Im interested in the blue one!', metadata={'source': '/Users/seungjoonlee/git/learn-langchain/document_loader/facebook_chat.json', 'seq_num': 5}), 73 | Document(page_content='Here is $129', metadata={'source': '/Users/seungjoonlee/git/learn-langchain/document_loader/facebook_chat.json', 'seq_num': 6}), 74 | Document(page_content='', metadata={'source': '/Users/seungjoonlee/git/learn-langchain/document_loader/facebook_chat.json', 'seq_num': 7}), 75 | Document(page_content='Online is at least $100', metadata={'source': '/Users/seungjoonlee/git/learn-langchain/document_loader/facebook_chat.json', 'seq_num': 8}), 76 | Document(page_content='How much do you want?', metadata={'source': '/Users/seungjoonlee/git/learn-langchain/document_loader/facebook_chat.json', 'seq_num': 9}), 77 | Document(page_content='Goodmorning! $50 is too low.', metadata={'source': '/Users/seungjoonlee/git/learn-langchain/document_loader/facebook_chat.json', 'seq_num': 10}), 78 | Document(page_content='Hi! Im interested in your bag. Im offering $50. Let me know if you are interested. Thanks!', metadata={'source': '/Users/seungjoonlee/git/learn-langchain/document_loader/facebook_chat.json', 'seq_num': 11})] 79 | """ -------------------------------------------------------------------------------- /document_loaders/pdf_loader.py: -------------------------------------------------------------------------------- 1 | # Note as of 02/27/2024 2 | # before you start you need to install the following 3 | # pip install langchain==0.1.9 langchain-openai==0.0.8 4 | from langchain_community.document_loaders import PyPDFLoader 5 | 6 | # required `pip install pypdf` 7 | loader = PyPDFLoader("csv_sample.pdf") 8 | pages = loader.load_and_split() 9 | 10 | print(pages[0].page_content) 11 | """ 12 | csv_sample 13 | Page 1nameagecountry 14 | Neville Hardy 56Niue 15 | Dacia Cohen 74Falkland Islands (Malvinas) 16 | Kathey Daniel 10Slovenia 17 | Mallie Welch 12Equatorial Guinea 18 | Katia Bryant 14Ghana 19 | Laurice Robertson 53Saudi Arabia 20 | Minh Barrett 27French Southern Territories 21 | Latashia Perez 52Finland 22 | Elvina Ross 68New Zealand 23 | """ 24 | -------------------------------------------------------------------------------- /document_loaders/sample.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | My Simple Web Page 5 | 6 | 7 |

Welcome to My Web Page

8 |

This is a simple HTML page. It's a great starting point for learning HTML.

9 | Click here to visit Example.com 10 | 11 | 12 | -------------------------------------------------------------------------------- /document_transformers/code_splitters.py: -------------------------------------------------------------------------------- 1 | from langchain.text_splitter import ( 2 | RecursiveCharacterTextSplitter, 3 | Language, 4 | ) 5 | 6 | print([e.value for e in Language]) 7 | """ 8 | ['cpp', 'go', 'java', 'kotlin', 'js', 'ts', 'php', 'proto', 'python', 'rst', 'ruby', 'rust', 'scala', 'swift', 'markdown', 'latex', 'html', 'sol', 'csharp', 'cobol'] 9 | """ 10 | 11 | separators = RecursiveCharacterTextSplitter.get_separators_for_language(Language.PYTHON) 12 | print(separators) 13 | """ 14 | ['\nclass ', '\ndef ', '\n\tdef ', '\n\n', '\n', ' ', ''] 15 | """ 16 | 17 | PYTHON_CODE = """ 18 | def hello_world(): 19 | print("Hello, World!") 20 | 21 | # Call the function 22 | hello_world() 23 | """ 24 | python_splitter = RecursiveCharacterTextSplitter.from_language( 25 | language=Language.PYTHON, chunk_size=50, chunk_overlap=0 26 | ) 27 | python_docs = python_splitter.create_documents([PYTHON_CODE]) 28 | print(python_docs) 29 | """ 30 | [Document(page_content='def hello_world():\n print("Hello, World!")'), Document(page_content='# Call the function\nhello_world()')] 31 | """ -------------------------------------------------------------------------------- /document_transformers/split_by_token.py: -------------------------------------------------------------------------------- 1 | from langchain.text_splitter import CharacterTextSplitter 2 | 3 | 4 | # required `pip install tiktoken` 5 | with open('./state_of_the_union.txt') as f: 6 | state_of_the_union = f.read() 7 | 8 | text_splitter = CharacterTextSplitter.from_tiktoken_encoder( 9 | chunk_size=100, chunk_overlap=0 10 | ) 11 | texts = text_splitter.split_text(state_of_the_union) 12 | print(len(texts)) 13 | print(texts[0]) 14 | -------------------------------------------------------------------------------- /document_transformers/text_splitters.py: -------------------------------------------------------------------------------- 1 | from langchain.text_splitter import RecursiveCharacterTextSplitter 2 | 3 | 4 | with open('./state_of_the_union.txt') as f: 5 | state_of_the_union = f.read() 6 | 7 | # default split on are ["\n\n", "\n", " ", ""] 8 | text_splitter = RecursiveCharacterTextSplitter( 9 | # Set a really small chunk size, just to show. 10 | chunk_size = 100, 11 | chunk_overlap = 20, # max overlap 12 | length_function = len, 13 | add_start_index = True, 14 | ) 15 | 16 | texts = text_splitter.create_documents([state_of_the_union]) 17 | print(texts[0]) 18 | """ 19 | page_content='Madam Speaker, Madam Vice President, our First Lady and Second Gentleman. Members of Congress and' metadata={'start_index': 0} 20 | """ 21 | print(texts[1]) 22 | """ 23 | page_content='of Congress and the Cabinet. Justices of the Supreme Court. My fellow Americans.' metadata={'start_index': 82} 24 | """ -------------------------------------------------------------------------------- /few_shot_prompt.py: -------------------------------------------------------------------------------- 1 | from langchain.prompts.few_shot import FewShotPromptTemplate 2 | from langchain.prompts.prompt import PromptTemplate 3 | 4 | examples = [ 5 | { 6 | "question": "Who lived longer, Muhammad Ali or Alan Turing?", 7 | "answer": 8 | """ 9 | Are follow up questions needed here: Yes. 10 | Follow up: How old was Muhammad Ali when he died? 11 | Intermediate answer: Muhammad Ali was 74 years old when he died. 12 | Follow up: How old was Alan Turing when he died? 13 | Intermediate answer: Alan Turing was 41 years old when he died. 14 | So the final answer is: Muhammad Ali 15 | """ 16 | }, 17 | { 18 | "question": "When was the founder of craigslist born?", 19 | "answer": 20 | """ 21 | Are follow up questions needed here: Yes. 22 | Follow up: Who was the founder of craigslist? 23 | Intermediate answer: Craigslist was founded by Craig Newmark. 24 | Follow up: When was Craig Newmark born? 25 | Intermediate answer: Craig Newmark was born on December 6, 1952. 26 | So the final answer is: December 6, 1952 27 | """ 28 | }, 29 | { 30 | "question": "Who was the maternal grandfather of George Washington?", 31 | "answer": 32 | """ 33 | Are follow up questions needed here: Yes. 34 | Follow up: Who was the mother of George Washington? 35 | Intermediate answer: The mother of George Washington was Mary Ball Washington. 36 | Follow up: Who was the father of Mary Ball Washington? 37 | Intermediate answer: The father of Mary Ball Washington was Joseph Ball. 38 | So the final answer is: Joseph Ball 39 | """ 40 | }, 41 | { 42 | "question": "Are both the directors of Jaws and Casino Royale from the same country?", 43 | "answer": 44 | """ 45 | Are follow up questions needed here: Yes. 46 | Follow up: Who is the director of Jaws? 47 | Intermediate Answer: The director of Jaws is Steven Spielberg. 48 | Follow up: Where is Steven Spielberg from? 49 | Intermediate Answer: The United States. 50 | Follow up: Who is the director of Casino Royale? 51 | Intermediate Answer: The director of Casino Royale is Martin Campbell. 52 | Follow up: Where is Martin Campbell from? 53 | Intermediate Answer: New Zealand. 54 | So the final answer is: No 55 | """ 56 | } 57 | ] 58 | 59 | example_prompt = PromptTemplate(input_variables=["question", "answer"], template="Question: {question}\n{answer}") 60 | 61 | print(example_prompt.format(**examples[0])) 62 | 63 | prompt = FewShotPromptTemplate( 64 | examples=examples, 65 | example_prompt=example_prompt, 66 | suffix="Question: {input}", 67 | input_variables=["input"] 68 | ) 69 | 70 | print(prompt.format(input="Who was the father of Mary Ball Washington?")) 71 | 72 | from langchain_openai import OpenAI 73 | 74 | # Specify the model_name on 2024-02-27 75 | # After model has been deprecated, the model does not answer any more. 76 | # OpenAI에서 기존에 되던 모델을 text-davinci-003를 없앤 후로 정확히 대답하지 않습니다. 77 | # 어떤 식으로 랭체인을 사용하는지만 보셨으면 좋겠습니다. 78 | llm = OpenAI(model_name="davinci-002") 79 | print(llm.invoke(prompt.format(input="Who was the father of Mary Ball Washington?"))) 80 | -------------------------------------------------------------------------------- /langgraph/chatbot/README.md: -------------------------------------------------------------------------------- 1 | # LangGraph 2 | 3 | LangGraph is a powerful tool developed by the LangChain team to simplify the creation and visualization of complex workflows in natural language processing (NLP) applications. It provides an intuitive interface for designing, debugging, and optimizing workflows, making it easier to build robust and scalable applications. 4 | 5 | ## Features 6 | 7 | - **Workflow Visualization**: Create and visualize complex workflows with ease. 8 | - **Debugging Tools**: Identify and resolve issues in your workflows efficiently. 9 | - **Integration with LangChain**: Seamlessly integrates with the LangChain framework for enhanced functionality. 10 | - **Scalability**: Build workflows that scale with your application's needs. 11 | -------------------------------------------------------------------------------- /langgraph/chatbot/chatbot_with_customizing_state.py: -------------------------------------------------------------------------------- 1 | from typing import Annotated 2 | 3 | from langchain_openai import ChatOpenAI 4 | from langchain_community.tools.tavily_search import TavilySearchResults 5 | from langchain_core.tools import tool 6 | from typing_extensions import TypedDict 7 | 8 | from langgraph.checkpoint.memory import MemorySaver 9 | from langgraph.graph import StateGraph, START, END 10 | from langgraph.graph.message import add_messages 11 | from langgraph.prebuilt import ToolNode, tools_condition 12 | 13 | from langgraph.types import Command, interrupt 14 | 15 | class State(TypedDict): 16 | messages: Annotated[list, add_messages] 17 | name: str 18 | birthday: str 19 | 20 | graph_builder = StateGraph(State) 21 | 22 | 23 | from langchain_core.messages import ToolMessage 24 | from langchain_core.tools import InjectedToolCallId, tool 25 | 26 | from langgraph.types import Command, interrupt 27 | 28 | 29 | @tool 30 | # Note that because we are generating a ToolMessage for a state update, we 31 | # generally require the ID of the corresponding tool call. We can use 32 | # LangChain's InjectedToolCallId to signal that this argument should not 33 | # be revealed to the model in the tool's schema. 34 | def human_assistance( 35 | name: str, birthday: str, tool_call_id: Annotated[str, InjectedToolCallId] 36 | ) -> str: 37 | """Request assistance from a human.""" 38 | human_response = interrupt( 39 | { 40 | "question": "Is this correct?", 41 | "name": name, 42 | "birthday": birthday, 43 | }, 44 | ) 45 | # If the information is correct, update the state as-is. 46 | if human_response.get("correct", "").lower().startswith("y"): 47 | verified_name = name 48 | verified_birthday = birthday 49 | response = "Correct" 50 | # Otherwise, receive information from the human reviewer. 51 | else: 52 | verified_name = human_response.get("name", name) 53 | verified_birthday = human_response.get("birthday", birthday) 54 | response = f"Made a correction: {human_response}" 55 | 56 | # This time we explicitly update the state with a ToolMessage inside 57 | # the tool. 58 | state_update = { 59 | "name": verified_name, 60 | "birthday": verified_birthday, 61 | "messages": [ToolMessage(response, tool_call_id=tool_call_id)], 62 | } 63 | # We return a Command object in the tool to update our state. 64 | return Command(update=state_update) # type: ignore 65 | 66 | 67 | tool = TavilySearchResults(max_results=2) 68 | tools = [tool, human_assistance] 69 | 70 | llm = ChatOpenAI(model="gpt-4o") # type: ignore 71 | llm_with_tools = llm.bind_tools(tools) 72 | 73 | def chatbot(state: State): 74 | message = llm_with_tools.invoke(state["messages"]) 75 | # Because we will be interrupting during tool execution, 76 | # we disable parallel tool calling to avoid repeating any 77 | # tool invocations when we resume. 78 | assert len(message.tool_calls) <= 1 # type: ignore 79 | return {"messages": [message]} 80 | 81 | graph_builder.add_node("chatbot", chatbot) 82 | 83 | tool_node = ToolNode(tools=tools) 84 | graph_builder.add_node("tools", tool_node) 85 | 86 | graph_builder.add_conditional_edges( 87 | "chatbot", 88 | tools_condition, 89 | ) 90 | graph_builder.add_edge("tools", "chatbot") 91 | graph_builder.add_edge(START, "chatbot") 92 | 93 | memory = MemorySaver() 94 | 95 | graph = graph_builder.compile(checkpointer=memory) 96 | 97 | user_input = ( 98 | "Can you look up when LangGraph was released? " 99 | "When you have the answer, use the human_assistance tool for review." 100 | ) 101 | config = {"configurable": {"thread_id": "1"}} 102 | 103 | events = graph.stream( 104 | {"messages": [{"role": "user", "content": user_input}]}, 105 | config, # type: ignore 106 | stream_mode="values", 107 | ) 108 | for event in events: 109 | if "messages" in event: 110 | event["messages"][-1].pretty_print() 111 | 112 | human_command = Command( 113 | resume={ 114 | "name": "LangGraph", 115 | "birthday": "Jan 17, 2024", 116 | }, 117 | ) 118 | 119 | events = graph.stream(human_command, config, stream_mode="values") # type: ignore 120 | for event in events: 121 | if "messages" in event: 122 | event["messages"][-1].pretty_print() 123 | 124 | snapshot = graph.get_state(config) # type: ignore 125 | print({k: v for k, v in snapshot.values.items() if k in ("name", "birthday")}) 126 | 127 | # Update the state with a new name 128 | snapshot = graph.update_state(config, {"name": "LangGraph (library)"}) # type: ignore 129 | snapshot = graph.get_state(config) # type: ignore 130 | print({k: v for k, v in snapshot.values.items() if k in ("name", "birthday")}) 131 | -------------------------------------------------------------------------------- /langgraph/chatbot/chatbot_with_memory.json: -------------------------------------------------------------------------------- 1 | { 2 | "StateSnapshot": { 3 | "values": { 4 | "messages": [ 5 | { 6 | "type": "HumanMessage", 7 | "content": "Hi there! My name is Will.", 8 | "additional_kwargs": {}, 9 | "response_metadata": {}, 10 | "id": "028b7fa2-ad08-46bb-9caf-ee914c7a3a9f" 11 | }, 12 | { 13 | "type": "AIMessage", 14 | "content": "Hello, Will! How can I assist you today?", 15 | "additional_kwargs": { 16 | "refusal": null 17 | }, 18 | "response_metadata": { 19 | "token_usage": { 20 | "completion_tokens": 13, 21 | "prompt_tokens": 87, 22 | "total_tokens": 100, 23 | "completion_tokens_details": { 24 | "accepted_prediction_tokens": 0, 25 | "audio_tokens": 0, 26 | "reasoning_tokens": 0, 27 | "rejected_prediction_tokens": 0 28 | }, 29 | "prompt_tokens_details": { 30 | "audio_tokens": 0, 31 | "cached_tokens": 0 32 | } 33 | }, 34 | "model_name": "gpt-4o-2024-08-06", 35 | "system_fingerprint": "fp_898ac29719", 36 | "id": "chatcmpl-BJ1lT1eOs5OiL2HRWuMOSSK11bhgG", 37 | "finish_reason": "stop", 38 | "logprobs": null 39 | }, 40 | "id": "run-26ad0d58-875f-49fb-97f9-24a5912b7f90-0", 41 | "usage_metadata": { 42 | "input_tokens": 87, 43 | "output_tokens": 13, 44 | "total_tokens": 100, 45 | "input_token_details": { 46 | "audio": 0, 47 | "cache_read": 0 48 | }, 49 | "output_token_details": { 50 | "audio": 0, 51 | "reasoning": 0 52 | } 53 | } 54 | }, 55 | { 56 | "type": "HumanMessage", 57 | "content": "Remember my name?", 58 | "additional_kwargs": {}, 59 | "response_metadata": {}, 60 | "id": "ef57b445-5073-46c3-a2af-0da2c5537455" 61 | }, 62 | { 63 | "type": "AIMessage", 64 | "content": "Yes, your name is Will! How can I help you today?", 65 | "additional_kwargs": { 66 | "refusal": null 67 | }, 68 | "response_metadata": { 69 | "token_usage": { 70 | "completion_tokens": 16, 71 | "prompt_tokens": 110, 72 | "total_tokens": 126, 73 | "completion_tokens_details": { 74 | "accepted_prediction_tokens": 0, 75 | "audio_tokens": 0, 76 | "reasoning_tokens": 0, 77 | "rejected_prediction_tokens": 0 78 | }, 79 | "prompt_tokens_details": { 80 | "audio_tokens": 0, 81 | "cached_tokens": 0 82 | } 83 | }, 84 | "model_name": "gpt-4o-2024-08-06", 85 | "system_fingerprint": "fp_898ac29719", 86 | "id": "chatcmpl-BJ1lUqUbb9SKZYgCSZxQOiahvQxhe", 87 | "finish_reason": "stop", 88 | "logprobs": null 89 | }, 90 | "id": "run-67c6298d-3028-45a2-be32-6df01f1d5340-0", 91 | "usage_metadata": { 92 | "input_tokens": 110, 93 | "output_tokens": 16, 94 | "total_tokens": 126, 95 | "input_token_details": { 96 | "audio": 0, 97 | "cache_read": 0 98 | }, 99 | "output_token_details": { 100 | "audio": 0, 101 | "reasoning": 0 102 | } 103 | } 104 | } 105 | ] 106 | }, 107 | "next": [], 108 | "config": { 109 | "configurable": { 110 | "thread_id": "1", 111 | "checkpoint_ns": "", 112 | "checkpoint_id": "1f01244c-db8f-6dce-8004-964cf12634f9" 113 | } 114 | }, 115 | "metadata": { 116 | "source": "loop", 117 | "writes": { 118 | "chatbot": { 119 | "messages": [ 120 | { 121 | "type": "AIMessage", 122 | "content": "Yes, your name is Will! How can I help you today?", 123 | "additional_kwargs": { 124 | "refusal": null 125 | }, 126 | "response_metadata": { 127 | "token_usage": { 128 | "completion_tokens": 16, 129 | "prompt_tokens": 110, 130 | "total_tokens": 126, 131 | "completion_tokens_details": { 132 | "accepted_prediction_tokens": 0, 133 | "audio_tokens": 0, 134 | "reasoning_tokens": 0, 135 | "rejected_prediction_tokens": 0 136 | }, 137 | "prompt_tokens_details": { 138 | "audio_tokens": 0, 139 | "cached_tokens": 0 140 | } 141 | }, 142 | "model_name": "gpt-4o-2024-08-06", 143 | "system_fingerprint": "fp_898ac29719", 144 | "id": "chatcmpl-BJ1lUqUbb9SKZYgCSZxQOiahvQxhe", 145 | "finish_reason": "stop", 146 | "logprobs": null 147 | }, 148 | "id": "run-67c6298d-3028-45a2-be32-6df01f1d5340-0", 149 | "usage_metadata": { 150 | "input_tokens": 110, 151 | "output_tokens": 16, 152 | "total_tokens": 126, 153 | "input_token_details": { 154 | "audio": 0, 155 | "cache_read": 0 156 | }, 157 | "output_token_details": { 158 | "audio": 0, 159 | "reasoning": 0 160 | } 161 | } 162 | } 163 | ] 164 | } 165 | }, 166 | "step": 4, 167 | "parents": {}, 168 | "thread_id": "1" 169 | }, 170 | "created_at": "2025-04-05T17:38:36.701414+00:00", 171 | "parent_config": { 172 | "configurable": { 173 | "thread_id": "1", 174 | "checkpoint_ns": "", 175 | "checkpoint_id": "1f01244c-d550-6de6-8003-fd3b2449d796" 176 | } 177 | }, 178 | "tasks": [] 179 | } 180 | } -------------------------------------------------------------------------------- /langgraph/chatbot/chatbot_with_memory.py: -------------------------------------------------------------------------------- 1 | from langgraph.checkpoint.memory import MemorySaver 2 | 3 | memory = MemorySaver() 4 | 5 | from typing import Annotated 6 | 7 | from langchain_openai import ChatOpenAI 8 | from langchain_community.tools.tavily_search import TavilySearchResults 9 | from typing_extensions import TypedDict 10 | 11 | from langgraph.graph import StateGraph, START, END 12 | from langgraph.graph.message import add_messages 13 | from langgraph.prebuilt import ToolNode, tools_condition 14 | 15 | 16 | class State(TypedDict): 17 | messages: Annotated[list, add_messages] 18 | 19 | graph_builder = StateGraph(State) 20 | 21 | tool = TavilySearchResults(max_results=2) 22 | tools = [tool] 23 | 24 | llm = ChatOpenAI(model="gpt-4o") # type: ignore 25 | llm_with_tools = llm.bind_tools(tools) 26 | 27 | def chatbot(state: State): 28 | return {"messages": [llm_with_tools.invoke(state["messages"])]} 29 | 30 | 31 | graph_builder.add_node("chatbot", chatbot) 32 | 33 | # Add a tool node to the graph 34 | # This node will be called when the tool is invoked 35 | tool_node = ToolNode(tools=[tool]) 36 | graph_builder.add_node("tools", tool_node) 37 | 38 | graph_builder.add_conditional_edges( 39 | "chatbot", 40 | tools_condition, 41 | ) 42 | 43 | # Any time a tool is called, we return to the chatbot to decide the next step 44 | graph_builder.add_edge("tools", "chatbot") 45 | graph_builder.add_edge(START, "chatbot") 46 | 47 | # Compile with memory 48 | graph = graph_builder.compile(checkpointer=memory) 49 | # print(graph.get_graph().draw_mermaid()) 50 | # https://mermaid.live/ 51 | 52 | config = {"configurable": {"thread_id": "1"}} 53 | 54 | user_input = "Hi there! My name is Will." 55 | 56 | # The config is the **second positional argument** to stream() or invoke()! 57 | events = graph.stream( 58 | {"messages": [{"role": "user", "content": user_input}]}, 59 | config, # type: ignore 60 | stream_mode="values", 61 | ) 62 | for event in events: 63 | event["messages"][-1].pretty_print() 64 | 65 | user_input = "Remember my name?" 66 | 67 | config = {"configurable": {"thread_id": "2"}} 68 | 69 | # The config is the **second positional argument** to stream() or invoke()! 70 | events = graph.stream( 71 | {"messages": [{"role": "user", "content": user_input}]}, 72 | config, # type: ignore 73 | stream_mode="values", 74 | ) 75 | for event in events: 76 | event["messages"][-1].pretty_print() 77 | 78 | # events = graph.stream( 79 | # {"messages": [{"role": "user", "content": user_input}]}, 80 | # {"configurable": {"thread_id": "2"}}, 81 | # stream_mode="values", 82 | # ) 83 | # for event in events: 84 | # event["messages"][-1].pretty_print() 85 | 86 | # config = {"configurable": {"thread_id": "2"}} 87 | 88 | snapshot = graph.get_state(config) # type: ignore 89 | print(snapshot) 90 | # refer to chatbot_with_memory.json 91 | # print(snapshot) -------------------------------------------------------------------------------- /langgraph/chatbot/chatbot_with_memory_sqlite.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | from langgraph.checkpoint.sqlite import SqliteSaver 3 | 4 | # find the current directory 5 | import os 6 | current_dir = os.path.dirname(os.path.abspath(__file__)) 7 | 8 | conn = sqlite3.connect(f"{current_dir}/checkpoints.sqlite", check_same_thread=False) 9 | memory = SqliteSaver(conn) 10 | 11 | from typing import Annotated 12 | 13 | from langchain_openai import ChatOpenAI 14 | from langchain_community.tools.tavily_search import TavilySearchResults 15 | from typing_extensions import TypedDict 16 | 17 | from langgraph.graph import StateGraph, START, END 18 | from langgraph.graph.message import add_messages 19 | from langgraph.prebuilt import ToolNode, tools_condition 20 | 21 | 22 | class State(TypedDict): 23 | messages: Annotated[list, add_messages] 24 | 25 | graph_builder = StateGraph(State) 26 | 27 | tool = TavilySearchResults(max_results=2) 28 | tools = [tool] 29 | 30 | llm = ChatOpenAI(model="gpt-4o") # type: ignore 31 | llm_with_tools = llm.bind_tools(tools) 32 | 33 | def chatbot(state: State): 34 | return {"messages": [llm_with_tools.invoke(state["messages"])]} 35 | 36 | 37 | graph_builder.add_node("chatbot", chatbot) 38 | 39 | # Add a tool node to the graph 40 | # This node will be called when the tool is invoked 41 | tool_node = ToolNode(tools=[tool]) 42 | graph_builder.add_node("tools", tool_node) 43 | 44 | graph_builder.add_conditional_edges( 45 | "chatbot", 46 | tools_condition, 47 | ) 48 | 49 | # Any time a tool is called, we return to the chatbot to decide the next step 50 | graph_builder.add_edge("tools", "chatbot") 51 | graph_builder.add_edge(START, "chatbot") 52 | 53 | # Compile with memory 54 | graph = graph_builder.compile(checkpointer=memory) 55 | # print(graph.get_graph().draw_mermaid()) 56 | # https://mermaid.live/ 57 | 58 | config = {"configurable": {"thread_id": "1"}} 59 | 60 | user_input = "Hi there! My name is Joon." 61 | 62 | # The config is the **second positional argument** to stream() or invoke()! 63 | events = graph.stream( 64 | {"messages": [{"role": "user", "content": user_input}]}, 65 | config, # type: ignore 66 | stream_mode="values", 67 | ) 68 | for event in events: 69 | event["messages"][-1].pretty_print() 70 | 71 | user_input = "Remember my name?" 72 | 73 | config = {"configurable": {"thread_id": "1"}} 74 | 75 | # The config is the **second positional argument** to stream() or invoke()! 76 | events = graph.stream( 77 | {"messages": [{"role": "user", "content": user_input}]}, 78 | config, # type: ignore 79 | stream_mode="values", 80 | ) 81 | for event in events: 82 | event["messages"][-1].pretty_print() 83 | 84 | # # events = graph.stream( 85 | # # {"messages": [{"role": "user", "content": user_input}]}, 86 | # # {"configurable": {"thread_id": "2"}}, 87 | # # stream_mode="values", 88 | # # ) 89 | # # for event in events: 90 | # # event["messages"][-1].pretty_print() 91 | 92 | # # config = {"configurable": {"thread_id": "2"}} 93 | 94 | # snapshot = graph.get_state(config) # type: ignore 95 | # print(snapshot) 96 | # # refer to chatbot_with_memory.json 97 | # # print(snapshot) 98 | 99 | print(list(graph.get_state_history(config))) -------------------------------------------------------------------------------- /langgraph/chatbot/chatbot_with_time_travel.py: -------------------------------------------------------------------------------- 1 | from typing import Annotated 2 | 3 | from langchain_openai import ChatOpenAI 4 | from langchain_community.tools.tavily_search import TavilySearchResults 5 | from langchain_core.messages import BaseMessage 6 | from typing_extensions import TypedDict 7 | 8 | from langgraph.checkpoint.memory import MemorySaver 9 | from langgraph.graph import StateGraph, START, END 10 | from langgraph.graph.message import add_messages 11 | from langgraph.prebuilt import ToolNode, tools_condition 12 | 13 | 14 | class State(TypedDict): 15 | messages: Annotated[list, add_messages] 16 | 17 | 18 | graph_builder = StateGraph(State) 19 | 20 | 21 | tool = TavilySearchResults(max_results=2) 22 | tools = [tool] 23 | llm = ChatOpenAI(model="gpt-4o") # type: ignore 24 | llm_with_tools = llm.bind_tools(tools) 25 | 26 | 27 | def chatbot(state: State): 28 | return {"messages": [llm_with_tools.invoke(state["messages"])]} 29 | 30 | 31 | graph_builder.add_node("chatbot", chatbot) 32 | 33 | tool_node = ToolNode(tools=[tool]) 34 | graph_builder.add_node("tools", tool_node) 35 | 36 | graph_builder.add_conditional_edges( 37 | "chatbot", 38 | tools_condition, 39 | ) 40 | graph_builder.add_edge("tools", "chatbot") 41 | graph_builder.add_edge(START, "chatbot") 42 | 43 | memory = MemorySaver() 44 | graph = graph_builder.compile(checkpointer=memory) 45 | 46 | config = {"configurable": {"thread_id": "1"}} 47 | events = graph.stream( 48 | { 49 | "messages": [ 50 | { 51 | "role": "user", 52 | "content": ( 53 | "What is the population of South Korea?" 54 | "Could you do some research on it for me?" 55 | ), 56 | }, 57 | ], 58 | }, 59 | config, # type: ignore 60 | stream_mode="values", 61 | ) 62 | for event in events: 63 | if "messages" in event: 64 | event["messages"][-1].pretty_print() 65 | 66 | _ = input() 67 | 68 | events = graph.stream( 69 | { 70 | "messages": [ 71 | { 72 | "role": "user", 73 | "content": ( 74 | "Ya that's helpful. How about the population of Japan?" 75 | ), 76 | }, 77 | ], 78 | }, 79 | config, # type: ignore 80 | stream_mode="values", 81 | ) 82 | for event in events: 83 | if "messages" in event: 84 | event["messages"][-1].pretty_print() 85 | 86 | _ = input() 87 | 88 | events = graph.stream( 89 | { 90 | "messages": [ 91 | { 92 | "role": "user", 93 | "content": ( 94 | "Ya that's helpful. How about the population of China?" 95 | ), 96 | }, 97 | ], 98 | }, 99 | config, # type: ignore 100 | stream_mode="values", 101 | ) 102 | for event in events: 103 | if "messages" in event: 104 | event["messages"][-1].pretty_print() 105 | 106 | _ = input() 107 | 108 | # This is a simple example of how to replay a state 109 | to_replay = None 110 | for state in graph.get_state_history(config): # type: ignore 111 | print("Num Messages: ", len(state.values["messages"]), "Next: ", state.next) 112 | 113 | if len(state.values["messages"]) > 0: 114 | print(state.values["messages"][-1].pretty_print()) 115 | 116 | print("-" * 80) 117 | if len(state.values["messages"]) == 6: 118 | # ================================== Ai Message ================================== 119 | # Tool Calls: 120 | # tavily_search_results_json (call_YQAiFP2XMgBTeIZB5KHjfLkG) 121 | # Call ID: call_YQAiFP2XMgBTeIZB5KHjfLkG 122 | # Args: 123 | # query: current population of Japan 2023 124 | # None 125 | to_replay = state 126 | 127 | _ = input() 128 | 129 | print("Replay this state *********") 130 | print(to_replay.next) 131 | print(to_replay.config) 132 | 133 | # The `checkpoint_id` in the `to_replay.config` corresponds to a state we've persisted to our checkpointer. 134 | for event in graph.stream(None, to_replay.config, stream_mode="values"): 135 | if "messages" in event: 136 | event["messages"][-1].pretty_print() 137 | -------------------------------------------------------------------------------- /langgraph/chatbot/chatbot_with_tool.py: -------------------------------------------------------------------------------- 1 | from langchain_community.tools.tavily_search import TavilySearchResults 2 | 3 | tool = TavilySearchResults(max_results=2) 4 | tools = [tool] 5 | tool.invoke("What's a 'node' in LangGraph?") 6 | 7 | """ 8 | [ 9 | { 10 | "title": "LangGraph Glossary - GitHub Pages", 11 | "url": "https://langchain-ai.github.io/langgraph/concepts/low_level/", 12 | "content": "In LangGraph, nodes are typically python functions (sync or async) where the first positional argument is the state, and (optionally), the second positional argument is a \"config\", containing optional configurable parameters (such as a thread_id).\nSimilar to NetworkX, you add these nodes to a graph using the add_node method:\nfrom langchain_core.runnables import RunnableConfig [...] LangGraph Glossary¶\nGraphs¶\nAt its core, LangGraph models agent workflows as graphs. You define the behavior of your agents using three key components:\n\n\nState: A shared data structure that represents the current snapshot of your application. It can be any Python type, but is typically a TypedDict or Pydantic BaseModel.\n\n\nNodes: Python functions that encode the logic of your agents. They receive the current State as input, perform some computation or side-effect, and return an updated State. [...] By composing Nodes and Edges, you can create complex, looping workflows that evolve the State over time. The real power, though, comes from how LangGraph manages that State. To emphasize: Nodes and Edges are nothing more than Python functions - they can contain an LLM or just good ol' Python code.\nIn short: nodes do the work. edges tell what to do next.", 13 | "score": 0.8995547 14 | }, 15 | { 16 | "title": "A Comprehensive Guide About Langgraph: Code Included - Ionio", 17 | "url": "https://www.ionio.ai/blog/a-comprehensive-guide-about-langgraph-code-included", 18 | "content": "Now let’s take a look at each component of langgraph in detail 🚀\nNodes\nA node can be any function or tool your agent uses in langgraph and these nodes are connected with other nodes using edges. Every workflow ends with a “END” node in langgraph which shows the end of workflow. You also need to define a starting node which will be the starting point of your workflow. Optionally, you can also define an ending node if you know the ending point of your workflow.", 19 | "score": 0.8991304 20 | } 21 | ] 22 | """ 23 | 24 | from typing import Annotated 25 | from typing_extensions import TypedDict 26 | from langgraph.graph import StateGraph, START, END 27 | from langgraph.graph.message import add_messages 28 | from langchain_openai import ChatOpenAI 29 | 30 | class State(TypedDict): 31 | # Messages have the type "list". The `add_messages` function 32 | # in the annotation defines how this state key should be updated 33 | # (in this case, it appends messages to the list, rather than overwriting them) 34 | messages: Annotated[list, add_messages] 35 | 36 | 37 | graph_builder = StateGraph(State) 38 | 39 | llm = ChatOpenAI(model="gpt-4o") # type: ignore 40 | llm_with_tools = llm.bind_tools(tools) 41 | 42 | def chatbot(state: State): 43 | return {"messages": [llm_with_tools.invoke(state["messages"])]} 44 | 45 | def stream_graph_updates(user_input: str): 46 | for event in graph.stream({"messages": [{"role": "user", "content": user_input}]}): 47 | for value in event.values(): 48 | print("Assistant:", value["messages"][-1].content) 49 | 50 | # The first argument is the unique node name 51 | # The second argument is the function or object that will be called whenever 52 | # the node is used. 53 | graph_builder.add_node("chatbot", chatbot) 54 | 55 | 56 | import json 57 | 58 | from langchain_core.messages import ToolMessage 59 | 60 | 61 | class BasicToolNode: 62 | """A node that runs the tools requested in the last AIMessage.""" 63 | 64 | def __init__(self, tools: list) -> None: 65 | self.tools_by_name = {tool.name: tool for tool in tools} 66 | 67 | def __call__(self, inputs: dict): 68 | if messages := inputs.get("messages", []): 69 | message = messages[-1] 70 | else: 71 | raise ValueError("No message found in input") 72 | outputs = [] 73 | for tool_call in message.tool_calls: 74 | tool_result = self.tools_by_name[tool_call["name"]].invoke( 75 | tool_call["args"] 76 | ) 77 | outputs.append( 78 | ToolMessage( 79 | content=json.dumps(tool_result), 80 | name=tool_call["name"], 81 | tool_call_id=tool_call["id"], 82 | ) 83 | ) 84 | return {"messages": outputs} 85 | 86 | 87 | tool_node = BasicToolNode(tools=[tool]) 88 | graph_builder.add_node("tools", tool_node) 89 | 90 | 91 | def route_tools( 92 | state: State, 93 | ): 94 | """ 95 | Use in the conditional_edge to route to the ToolNode if the last message 96 | has tool calls. Otherwise, route to the end. 97 | """ 98 | if isinstance(state, list): 99 | ai_message = state[-1] # type: ignore 100 | elif messages := state.get("messages", []): 101 | ai_message = messages[-1] 102 | else: 103 | raise ValueError(f"No messages found in input state to tool_edge: {state}") 104 | if hasattr(ai_message, "tool_calls") and len(ai_message.tool_calls) > 0: # type: ignore 105 | return "tools" 106 | return END 107 | 108 | 109 | # The `tools_condition` function returns "tools" if the chatbot asks to use a tool, and "END" if 110 | # it is fine directly responding. This conditional routing defines the main agent loop. 111 | graph_builder.add_conditional_edges( 112 | "chatbot", 113 | route_tools, 114 | # The following dictionary lets you tell the graph to interpret the condition's outputs as a specific node 115 | # It defaults to the identity function, but if you 116 | # want to use a node named something else apart from "tools", 117 | # You can update the value of the dictionary to something else 118 | # e.g., "tools": "my_tools" 119 | {"tools": "tools", END: END}, 120 | ) 121 | # Any time a tool is called, we return to the chatbot to decide the next step 122 | graph_builder.add_edge("tools", "chatbot") 123 | graph_builder.add_edge(START, "chatbot") 124 | graph = graph_builder.compile() 125 | # print(graph.get_graph().draw_mermaid()) 126 | # https://mermaid.live/ 127 | 128 | while True: 129 | try: 130 | user_input = input("User: ") 131 | if user_input.lower() in ["quit", "exit", "q"]: 132 | print("Goodbye!") 133 | break 134 | 135 | stream_graph_updates(user_input) 136 | except: 137 | # fallback if input() is not available 138 | user_input = "What do you know about LangGraph?" 139 | print("User: " + user_input) 140 | stream_graph_updates(user_input) 141 | break 142 | -------------------------------------------------------------------------------- /langgraph/chatbot/human_in_the_loop.py: -------------------------------------------------------------------------------- 1 | from typing import Annotated 2 | 3 | from langchain_openai import ChatOpenAI 4 | from langchain_community.tools.tavily_search import TavilySearchResults 5 | from langchain_core.tools import tool 6 | from typing_extensions import TypedDict 7 | 8 | from langgraph.checkpoint.memory import MemorySaver 9 | from langgraph.graph import StateGraph, START, END 10 | from langgraph.graph.message import add_messages 11 | from langgraph.prebuilt import ToolNode, tools_condition 12 | 13 | from langgraph.types import Command, interrupt 14 | 15 | class State(TypedDict): 16 | messages: Annotated[list, add_messages] 17 | 18 | 19 | graph_builder = StateGraph(State) 20 | 21 | 22 | @tool 23 | def human_assistance(query: str) -> str: 24 | """Request assistance from a human.""" 25 | human_response = interrupt({"query": query}) 26 | return human_response["data"] 27 | 28 | 29 | tool = TavilySearchResults(max_results=2) 30 | tools = [tool, human_assistance] 31 | 32 | llm = ChatOpenAI(model="gpt-4o") # type: ignore 33 | llm_with_tools = llm.bind_tools(tools) 34 | 35 | def chatbot(state: State): 36 | message = llm_with_tools.invoke(state["messages"]) 37 | # Because we will be interrupting during tool execution, 38 | # we disable parallel tool calling to avoid repeating any 39 | # tool invocations when we resume. 40 | assert len(message.tool_calls) <= 1 # type: ignore 41 | return {"messages": [message]} 42 | 43 | graph_builder.add_node("chatbot", chatbot) 44 | 45 | tool_node = ToolNode(tools=tools) 46 | graph_builder.add_node("tools", tool_node) 47 | 48 | graph_builder.add_conditional_edges( 49 | "chatbot", 50 | tools_condition, 51 | ) 52 | graph_builder.add_edge("tools", "chatbot") 53 | graph_builder.add_edge(START, "chatbot") 54 | 55 | memory = MemorySaver() 56 | 57 | graph = graph_builder.compile(checkpointer=memory) 58 | 59 | user_input = "I need some expert guidance for building an AI agent. Could you request assistance for me?" 60 | config = {"configurable": {"thread_id": "1"}} 61 | 62 | events = graph.stream( 63 | {"messages": [{"role": "user", "content": user_input}]}, 64 | config, # type: ignore 65 | stream_mode="values", 66 | ) 67 | for event in events: 68 | if "messages" in event: 69 | event["messages"][-1].pretty_print() 70 | 71 | snapshot = graph.get_state(config) # type: ignore 72 | print(snapshot.next) 73 | 74 | # human_response = ( 75 | # "We, the experts are here to help! We'd recommend you check out LangGraph to build your agent." 76 | # " It's much more reliable and extensible than simple autonomous agents." 77 | # ) 78 | 79 | human_response = input("Please provide your response to the user: ") 80 | 81 | human_command = Command(resume={"data": human_response}) 82 | 83 | events = graph.stream(human_command, config, stream_mode="values") # type: ignore 84 | for event in events: 85 | if "messages" in event: 86 | event["messages"][-1].pretty_print() 87 | -------------------------------------------------------------------------------- /langgraph/chatbot/lat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimz119/learn-langchain/f0c2604aab7cab7f2c4d44d7bb484fd0dc2463bc/langgraph/chatbot/lat.png -------------------------------------------------------------------------------- /langgraph/chatbot/lat_chart.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimz119/learn-langchain/f0c2604aab7cab7f2c4d44d7bb484fd0dc2463bc/langgraph/chatbot/lat_chart.jpeg -------------------------------------------------------------------------------- /langgraph/chatbot/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "chatbot" 3 | version = "0.1.0" 4 | description = "Add your description here" 5 | readme = "README.md" 6 | requires-python = ">=3.13" 7 | dependencies = [ 8 | "langchain-anthropic>=0.3.10", 9 | "langchain-community>=0.3.21", 10 | "langchain-openai>=0.3.12", 11 | "langgraph>=0.3.25", 12 | "langgraph-checkpoint-sqlite>=2.0.6", 13 | "langsmith>=0.3.24", 14 | "tavily-python>=0.5.4", 15 | ] 16 | -------------------------------------------------------------------------------- /langgraph/chatbot/reflection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimz119/learn-langchain/f0c2604aab7cab7f2c4d44d7bb484fd0dc2463bc/langgraph/chatbot/reflection.png -------------------------------------------------------------------------------- /langgraph/chatbot/reflection.py: -------------------------------------------------------------------------------- 1 | from langchain_core.messages import AIMessage, BaseMessage, HumanMessage 2 | from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder 3 | from langchain_openai import ChatOpenAI 4 | 5 | prompt = ChatPromptTemplate.from_messages( 6 | [ 7 | ( 8 | "system", 9 | "You are an essay assistant tasked with writing excellent 3-paragraph essays." 10 | " Generate the best essay possible for the user's request." 11 | " If the user provides critique, respond with a revised version of your previous attempts.", 12 | ), 13 | MessagesPlaceholder(variable_name="messages"), 14 | ] 15 | ) 16 | llm = ChatOpenAI(model="gpt-4o") # type: ignore 17 | generate = prompt | llm 18 | 19 | essay = "" 20 | request = HumanMessage( 21 | content="Write an essay on why the little prince is relevant in modern childhood" 22 | ) 23 | for chunk in generate.stream({"messages": [request]}): 24 | print(chunk.content, end="") 25 | essay += chunk.content 26 | 27 | # Start reflecting on the essay 28 | reflection_prompt = ChatPromptTemplate.from_messages( 29 | [ 30 | ( 31 | "system", 32 | "You are a teacher grading an essay submission. Generate critique and recommendations for the user's submission." 33 | " Provide detailed recommendations, including requests for length, depth, style, etc.", 34 | ), 35 | MessagesPlaceholder(variable_name="messages"), 36 | ] 37 | ) 38 | reflect = reflection_prompt | llm 39 | reflection = "" 40 | for chunk in reflect.stream({"messages": [request, HumanMessage(content=essay)]}): 41 | print(chunk.content, end="") 42 | reflection += chunk.content 43 | 44 | # import asyncio 45 | # from typing import Annotated, List, Sequence 46 | # from langgraph.graph import END, StateGraph, START 47 | # from langgraph.graph.message import add_messages 48 | # from langgraph.checkpoint.memory import MemorySaver 49 | # from typing_extensions import TypedDict 50 | 51 | 52 | # class State(TypedDict): 53 | # messages: Annotated[list, add_messages] 54 | 55 | 56 | # async def generation_node(state: State) -> State: 57 | # return {"messages": [await generate.ainvoke(state["messages"])]} # type: ignore 58 | 59 | 60 | # async def reflection_node(state: State) -> State: 61 | # # Other messages we need to adjust 62 | # cls_map = { 63 | # "ai": HumanMessage, 64 | # "human": AIMessage 65 | # } 66 | # # First message is the original user request. We hold it the same for all nodes 67 | # translated = [state["messages"][0]] + [ 68 | # cls_map[msg.type](content=msg.content) for msg in state["messages"][1:] 69 | # ] 70 | # res = await reflect.ainvoke(translated) 71 | # # We treat the output of this as human feedback for the generator 72 | # return {"messages": [HumanMessage(content=res.content)]} 73 | 74 | 75 | # builder = StateGraph(State) 76 | # builder.add_node("generate", generation_node) 77 | # builder.add_node("reflect", reflection_node) 78 | # builder.add_edge(START, "generate") 79 | 80 | 81 | # def should_continue(state: State): 82 | # if len(state["messages"]) > 6: 83 | # # End after 3 iterations 84 | # return END 85 | # return "reflect" 86 | 87 | 88 | # builder.add_conditional_edges("generate", should_continue) 89 | # builder.add_edge("reflect", "generate") 90 | # memory = MemorySaver() 91 | # graph = builder.compile(checkpointer=memory) 92 | # # print(graph.get_graph().draw_mermaid()) 93 | 94 | # config = {"configurable": {"thread_id": "1"}} 95 | 96 | # async def main(): 97 | # async for event in graph.astream( 98 | # { 99 | # "messages": [ 100 | # HumanMessage( 101 | # content="Generate an essay on the topicality of The Little Prince and its message in modern life" 102 | # ) 103 | # ], 104 | # }, 105 | # config, 106 | # ): 107 | # print(event) 108 | # print("---") 109 | 110 | # if __name__ == "__main__": 111 | # asyncio.run(main()) -------------------------------------------------------------------------------- /langgraph/chatbot/reflexion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimz119/learn-langchain/f0c2604aab7cab7f2c4d44d7bb484fd0dc2463bc/langgraph/chatbot/reflexion.png -------------------------------------------------------------------------------- /langgraph/chatbot/schema.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field 2 | from typing import List 3 | from pydantic import ValidationError 4 | from langchain_core.messages import ToolMessage 5 | 6 | class Reflection(BaseModel): 7 | missing: str = Field(description="Critique of what is missing.") 8 | superfluous: str = Field(description="Critique of what is superfluous") 9 | 10 | class AnswerQuestion(BaseModel): 11 | """Answer the question. Provide an answer, reflection, and then follow up with search queries to improve the answer.""" 12 | answer: str = Field(description="~250 word detailed answer to the question.") 13 | search_queries: List[str] = Field( 14 | description="1-3 search queries for researching improvements to address the critique of your current answer." 15 | ) 16 | reflection: Reflection = Field(description="Your reflection on the initial answer.") 17 | 18 | class ResponderWithRetries: 19 | def __init__(self, runnable, validator): 20 | self.runnable = runnable 21 | self.validator = validator 22 | 23 | def respond(self, state: dict): 24 | response = [] 25 | for attempt in range(3): 26 | response = self.runnable.invoke( 27 | {"messages": state["messages"]}, {"tags": [f"attempt:{attempt}"]} 28 | ) 29 | try: 30 | self.validator.invoke(response) 31 | return {"messages": response} 32 | except ValidationError as e: 33 | state = state + [ 34 | response, 35 | ToolMessage( 36 | content=f"{repr(e)}\n\nPay close attention to the function schema.\n\n" 37 | + self.validator.schema_json() 38 | + " Respond by fixing all validation errors.", 39 | tool_call_id=response.tool_calls[0]["id"], 40 | ), 41 | ] # type: ignore 42 | return {"messages": response} 43 | 44 | # Extend the initial answer schema to include references. 45 | # Forcing citation in the model encourages grounded responses 46 | class ReviseAnswer(AnswerQuestion): 47 | """Revise your original answer to your question. Provide an answer, reflection, 48 | 49 | cite your reflection with references, and finally 50 | add search queries to improve the answer.""" 51 | 52 | references: list[str] = Field( 53 | description="Citations motivating your updated answer." 54 | ) 55 | -------------------------------------------------------------------------------- /langgraph/chatbot/simple_chatbot.py: -------------------------------------------------------------------------------- 1 | from typing import Annotated 2 | from typing_extensions import TypedDict 3 | from langgraph.graph import StateGraph, START, END 4 | from langgraph.graph.message import add_messages 5 | from langchain_openai import ChatOpenAI 6 | 7 | class State(TypedDict): 8 | # Messages have the type "list". The `add_messages` function 9 | # in the annotation defines how this state key should be updated 10 | # (in this case, it appends messages to the list, rather than overwriting them) 11 | messages: Annotated[list, add_messages] 12 | 13 | 14 | graph_builder = StateGraph(State) 15 | 16 | llm = ChatOpenAI(model="gpt-4o") # type: ignore 17 | 18 | def chatbot(state: State): 19 | return {"messages": [llm.invoke(state["messages"])]} 20 | 21 | 22 | # The first argument is the unique node name 23 | # The second argument is the function or object that will be called whenever 24 | # the node is used. 25 | graph_builder.add_node("chatbot", chatbot) 26 | 27 | graph_builder.add_edge(START, "chatbot") 28 | graph_builder.add_edge("chatbot", END) 29 | graph = graph_builder.compile() 30 | # print(graph.get_graph().draw_mermaid()) 31 | # https://mermaid.live/ 32 | 33 | def stream_graph_updates(user_input: str): 34 | for event in graph.stream({"messages": [{"role": "user", "content": user_input}]}): 35 | for value in event.values(): 36 | print("Assistant:", value["messages"][-1].content) 37 | 38 | 39 | while True: 40 | try: 41 | user_input = input("User: ") 42 | if user_input.lower() in ["quit", "exit", "q"]: 43 | print("Goodbye!") 44 | break 45 | stream_graph_updates(user_input) 46 | except: 47 | # fallback if input() is not available 48 | user_input = "What do you know about LangGraph?" 49 | print("User: " + user_input) 50 | stream_graph_updates(user_input) 51 | break 52 | -------------------------------------------------------------------------------- /langserve/client.py: -------------------------------------------------------------------------------- 1 | import json 2 | import requests 3 | """ 4 | POST /my_runnable/invoke - invoke the runnable on a single input 5 | POST /my_runnable/batch - invoke the runnable on a batch of inputs 6 | POST /my_runnable/stream - invoke on a single input and stream the output 7 | POST /my_runnable/stream_log - invoke on a single input and stream the output, including output of intermediate steps as it's generated 8 | GET /my_runnable/input_schema - json schema for input to the runnable 9 | GET /my_runnable/output_schema - json schema for output of the runnable 10 | GET /my_runnable/config_schema - json schema for config of the runnable 11 | """ 12 | response = requests.post( 13 | "http://localhost:8000/joke/invoke", 14 | json={'input': {'topic': 'cats'}} 15 | ) 16 | print(response.json()) 17 | """ 18 | { 19 | 'output': { 20 | 'content': "Sure, here's a cat-related joke for you:\n\nWhy don't cats play poker in the wild?\n\nToo many cheetahs!", 21 | 'additional_kwargs': {}, 22 | 'type': 'ai', 23 | 'example': False 24 | }, 25 | 'callback_events': [], 26 | 'metadata': { 27 | 'run_id': '8ed5fa43-6e1f-4743-8310-77dbe52503c0'}} 28 | """ 29 | 30 | response = requests.get( 31 | "http://localhost:8000/joke/input_schema", 32 | ) 33 | print(response.json()) 34 | """ 35 | {'title': 'PromptInput', 'type': 'object', 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}} 36 | """ 37 | 38 | response = requests.post( 39 | "http://localhost:8000/joke/stream", 40 | json={'input': {'topic': 'cats'}}, 41 | stream=True 42 | ) 43 | 44 | # Check if the request was successful 45 | if response.status_code == 200: 46 | try: 47 | current_event = None 48 | event_data = [] 49 | for line in response.iter_lines(): 50 | if line: # filter out keep-alive new lines 51 | # print(line.decode('utf-8')) 52 | line = line.decode('utf-8') 53 | """ 54 | event: data 55 | data: {"content":"","additional_kwargs":{},"type":"AIMessageChunk","example":false} 56 | event: data 57 | data: {"content":"Sure","additional_kwargs":{},"type":"AIMessageChunk","example":false} 58 | event: data 59 | ... 60 | """ 61 | if line.startswith("event:"): 62 | current_event = line.split(":", 1)[1].strip() 63 | elif line.startswith("data:"): 64 | data = line.split(":", 1)[1].strip() 65 | try: 66 | parsed_data = json.loads(data) 67 | """ 68 | {'run_id': '9d77c51b-6e0d-457f-a75e-0961f205cc8f'} 69 | {'content': '', 'additional_kwargs': {}, 'type': 'AIMessageChunk', 'example': False} 70 | {'content': 'Sure', 'additional_kwargs': {}, 'type': 'AIMessageChunk', 'example': False} 71 | {'content': ',', 'additional_kwargs': {}, 'type': 'AIMessageChunk', 'example': False} 72 | ... 73 | """ 74 | event_data.append((current_event, parsed_data)) 75 | 76 | except json.JSONDecodeError: 77 | print(f"Error decoding JSON: {data}") 78 | except KeyboardInterrupt: 79 | print("Streaming stopped by user.") 80 | else: 81 | print(f"Failed to stream data: {response.status_code}") 82 | -------------------------------------------------------------------------------- /langserve/sdk_client.py: -------------------------------------------------------------------------------- 1 | from langchain.prompts import ChatPromptTemplate 2 | from langchain.schema.runnable import RunnableMap 3 | from langserve import RemoteRunnable 4 | 5 | openai = RemoteRunnable("http://localhost:8000/openai/") 6 | joke_chain = RemoteRunnable("http://localhost:8000/joke/") 7 | 8 | print(joke_chain.invoke({"topic": "parrots"})) 9 | """ 10 | content="Sure, here's a joke for you:\n\nWhy don't scientists trust parrots?\n\nBecause they always give them quack advice!" 11 | """ 12 | 13 | prompt = ChatPromptTemplate.from_messages( 14 | [("system", "Tell me a long story about {topic}")] 15 | ) 16 | 17 | # Can define custom chains 18 | chain = prompt | RunnableMap({ 19 | "openai": openai 20 | }) 21 | 22 | print(chain.batch([{ "topic": "parrots" }])) 23 | """ 24 | [{'openai': AIMessage(content='Once upon a time, in a lush tropical rainforest, there lived a colorful and vibrant community of parrots. These parrots were renowned for their intelligence, beauty, and lively personalities. They were the guardians of the forest, spreading joy and laughter with their melodious songs and playful behavior.\n\nAt the heart of the parrot community was a wise and respected elder named Ollie. Ollie had a rich plumage of emerald green feathers, with touches of bright red and blue. His eyes twinkled with an ancient wisdom that had been passed down through generations.\n\nOne day, as the parrots gathered around Ollie, he shared a tale from long ago. It was a story about an ancient treasure hidden deep within the forest. This treasure, known as the "Jewel of the Rainforest," was said to possess magical powers that could bring harmony and balance to all living creatures.\n\nThe parrots were intrigued by the tale and decided to embark on a quest to find the Jewel of the Rainforest. They believed that by harnessing its powers, they could protect their home from any harm that might come its way.\n\nLed by Ollie, the parrots set off on their adventure, flying through the dense foliage, and singing songs of courage and determination. Along the way, they encountered various challenges, but their unity and unwavering spirit kept them going.\n\nAs they ventured deeper into the forest, they faced treacherous rivers, towering trees, and hidden predators. However, their sharp minds and agile wings helped them overcome every obstacle. They supported one another, sharing their knowledge and strength.\n\nDuring their journey, the parrots discovered the incredible diversity of the rainforest. They marveled at the exotic flowers, cascading waterfalls, and the symphony of sounds that filled the air. They also encountered other creatures, like mischievous monkeys swinging from the trees and wise old turtles slowly making their way across the forest floor.\n\nOne day, after months of relentless searching, the parrots stumbled upon a hidden cave. Inside, they found an ethereal glow emanating from a magnificent jewel. It was the Jewel of the Rainforest, shimmering with every color imaginable. The parrots knew they had found what they were looking for.\n\nAs they gathered around the jewel, a magical energy enveloped them. They felt a sense of peace and enlightenment wash over them, as if the forest itself had embraced them. From that moment on, the parrots became the true guardians of the rainforest, using the Jewel\'s powers to protect and preserve their home.\n\nWith the Jewel\'s magic, the parrots brought harmony to the ecosystem, ensuring that all creatures lived in balance and peace. They sang their songs of unity, healing the wounds of the forest and spreading happiness throughout.\n\nWord of the parrots\' noble efforts spread far and wide, attracting visitors from across the world who wanted to witness the wonders of the rainforest. People marveled at the parrots\' intelligence, their ability to mimic human speech, and their vibrant colors.\n\nThe parrots became ambassadors of the rainforest, teaching humans the importance of conservation and the need to protect the Earth\'s natural wonders. Their enchanting presence brought joy to all who encountered them, reminding everyone of the beauty and magic that resided within nature.\n\nAnd so, the parrots lived on, their legacy carried through the generations. They continued to guard the rainforest, ensuring its prosperity and safeguarding the Jewel of the Rainforest. Their story became a legend, passed down through time, reminding everyone of the power of unity, wisdom, and the magic that lies within the hearts of parrots.')}] 25 | """ -------------------------------------------------------------------------------- /langserve/server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from fastapi import FastAPI 3 | from langchain.prompts import ChatPromptTemplate 4 | from langchain_openai import ChatOpenAI 5 | from langserve import add_routes 6 | 7 | 8 | app = FastAPI( 9 | title="LangChain Server", 10 | version="1.0", 11 | description="A simple api server using Langchain's Runnable interfaces", 12 | ) 13 | 14 | add_routes( 15 | app, 16 | ChatOpenAI(), 17 | path="/openai", 18 | ) 19 | 20 | model = ChatOpenAI() 21 | prompt = ChatPromptTemplate.from_template("tell me a joke about {topic}") 22 | add_routes( 23 | app, 24 | prompt | model, 25 | path="/joke", 26 | ) 27 | 28 | if __name__ == "__main__": 29 | import uvicorn 30 | 31 | uvicorn.run(app, host="localhost", port=8000) 32 | -------------------------------------------------------------------------------- /langsmith/.python-version: -------------------------------------------------------------------------------- 1 | 3.13 2 | -------------------------------------------------------------------------------- /langsmith/README.md: -------------------------------------------------------------------------------- 1 | # LangSmith 2 | 3 | LangSmith is a tool designed to simplify and enhance the development of applications using the LangChain framework. It provides utilities for debugging, monitoring, and optimizing workflows built with LangChain. 4 | 5 | ## Features 6 | 7 | - **Debugging Tools**: Easily identify and resolve issues in your LangChain workflows. 8 | - **Monitoring**: Track the performance and behavior of your applications in real-time. 9 | - **Optimization**: Analyze and improve the efficiency of your LangChain-based applications. 10 | 11 | -------------------------------------------------------------------------------- /langsmith/create_prompt.py: -------------------------------------------------------------------------------- 1 | from langsmith import Client 2 | from langchain_core.prompts import ChatPromptTemplate 3 | 4 | # Connect to the LangSmith client 5 | 6 | client = Client() 7 | 8 | # Define the prompt 9 | 10 | prompt = ChatPromptTemplate([ 11 | ("system", "You are a helpful chatbot. Answer the question as best as you can. provide the answer within 1 line"), 12 | ("user", "{question}"), 13 | ]) 14 | 15 | # Push the prompt 16 | client.push_prompt("my-fistprompt", object=prompt) 17 | -------------------------------------------------------------------------------- /langsmith/evaluation.py: -------------------------------------------------------------------------------- 1 | from langsmith import wrappers, Client 2 | from pydantic import BaseModel, Field 3 | from openai import OpenAI 4 | 5 | client = Client() 6 | openai_client = wrappers.wrap_openai(OpenAI()) 7 | 8 | # For other dataset creation methods, see: https://docs.smith.langchain.com/evaluation/how_to_guides/manage_datasets_programmatically https://docs.smith.langchain.com/evaluation/how_to_guides/manage_datasets_in_application 9 | 10 | # Create inputs and reference outputs 11 | examples = [ 12 | ( 13 | "Which country is Mount Kilimanjaro located in?", 14 | "Mount Kilimanjaro is located in Tanzania.", 15 | ), 16 | ( 17 | "What is Earth's lowest point?", 18 | "Earth's lowest point is The Dead Sea.", 19 | ), 20 | ] 21 | 22 | inputs = [{"question": input_prompt} for input_prompt, _ in examples] 23 | outputs = [{"answer": output_answer} for _, output_answer in examples] 24 | 25 | # Programmatically create a dataset in LangSmith 26 | dataset = client.create_dataset( 27 | dataset_name = "Sample dataset", 28 | description = "A sample dataset in LangSmith." 29 | ) 30 | 31 | # Add examples to the dataset 32 | client.create_examples(inputs=inputs, outputs=outputs, dataset_id=dataset.id) 33 | 34 | # Define the application logic you want to evaluate inside a target function 35 | # The SDK will automatically send the inputs from the dataset to your target function 36 | def target(inputs: dict) -> dict: 37 | response = openai_client.chat.completions.create( 38 | model="gpt-4o-mini", 39 | messages=[ 40 | { "role": "system", "content": "Answer the following question accurately" }, 41 | { "role": "user", "content": inputs["question"] }, 42 | ] 43 | ) 44 | return { "response": response.choices[0].message.content.strip() } # type: ignore 45 | 46 | # Define instructions for the LLM judge evaluator 47 | instructions = """Evaluate Student Answer against Ground Truth for conceptual similarity and classify true or false: 48 | - False: No conceptual match and similarity 49 | - True: Most or full conceptual match and similarity 50 | - Key criteria: Concept should match, not exact wording. 51 | """ 52 | 53 | # Define output schema for the LLM judge 54 | class Grade(BaseModel): 55 | score: bool = Field(description="Boolean that indicates whether the response is accurate relative to the reference answer") 56 | 57 | # Define LLM judge that grades the accuracy of the response relative to reference output 58 | def accuracy(outputs: dict, reference_outputs: dict) -> bool: 59 | response = openai_client.beta.chat.completions.parse( 60 | model="gpt-4o-mini", 61 | messages=[ 62 | { "role": "system", "content": instructions }, 63 | { "role": "user", "content": f"""Ground Truth answer: {reference_outputs["answer"]}; 64 | Student's Answer: {outputs["response"]}""" 65 | }], 66 | response_format=Grade 67 | ) 68 | return response.choices[0].message.parsed.score # type: ignore 69 | 70 | 71 | # After running the evaluation, a link will be provided to view the results in langsmith 72 | experiment_results = client.evaluate( 73 | target, 74 | data = "Sample dataset", 75 | evaluators = [ 76 | accuracy, # type: ignore 77 | # can add multiple evaluators here 78 | ], 79 | experiment_prefix = "first-eval-in-langsmith", 80 | max_concurrency = 2, 81 | ) # type: ignore 82 | 83 | print(experiment_results) 84 | -------------------------------------------------------------------------------- /langsmith/main.py: -------------------------------------------------------------------------------- 1 | from langchain_openai import ChatOpenAI 2 | 3 | llm = ChatOpenAI() 4 | llm.invoke("What is the population of South Korean?") # type: ignore 5 | 6 | -------------------------------------------------------------------------------- /langsmith/pull_prompt.py: -------------------------------------------------------------------------------- 1 | from langsmith import Client 2 | from openai import OpenAI 3 | from langchain_core.messages import convert_to_openai_messages 4 | 5 | # Connect to LangSmith and OpenAI 6 | 7 | client = Client() 8 | oai_client = OpenAI() 9 | 10 | # Pull the prompt to use 11 | 12 | # You can also specify a specific commit by passing the commit hash "my-prompt:" 13 | 14 | prompt = client.pull_prompt("my-fistprompt") 15 | 16 | # Since our prompt 17 | # only has one variable we could also pass in the value directly 18 | 19 | # The code below is equivalent to formatted_prompt = prompt.invoke("What is the color of the sky?") 20 | 21 | formatted_prompt = prompt.invoke({"question": "What is the color of the sky?"}) 22 | 23 | # Test the prompt 24 | 25 | response = oai_client.chat.completions.create( 26 | model="gpt-4o", 27 | messages=convert_to_openai_messages(formatted_prompt.messages), # type: ignore 28 | ) 29 | print(response) 30 | -------------------------------------------------------------------------------- /langsmith/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "learn-langsmith" 3 | version = "0.1.0" 4 | description = "Add your description here" 5 | readme = "README.md" 6 | requires-python = ">=3.13" 7 | dependencies = [ 8 | "langchain>=0.3.23", 9 | "langchain-openai>=0.3.12", 10 | "pydantic>=2.11.2", 11 | ] 12 | -------------------------------------------------------------------------------- /lcel/Chinook.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimz119/learn-langchain/f0c2604aab7cab7f2c4d44d7bb484fd0dc2463bc/lcel/Chinook.db -------------------------------------------------------------------------------- /lcel/code_writing.py: -------------------------------------------------------------------------------- 1 | # Note as of 02/27/2024 2 | # before you start you need to install the following 3 | # pip install langchain==0.1.9 langchain-openai==0.0.8 4 | from langchain_openai import ChatOpenAI 5 | from langchain.prompts import ( 6 | ChatPromptTemplate, 7 | ) 8 | from langchain.schema import StrOutputParser 9 | from langchain_community.utilities import PythonREPL 10 | 11 | # Introduce PythonREPL 12 | python_repl = PythonREPL() 13 | print(python_repl.run("print(1+1)")) 14 | """ 15 | Python REPL can execute arbitrary code. Use with caution. 16 | 2 17 | """ 18 | 19 | template = """Write some python code to solve the user's problem. 20 | 21 | Return only python code in Markdown format, e.g.: 22 | 23 | ```python 24 | .... 25 | ```""" 26 | prompt = ChatPromptTemplate.from_messages([("system", template), ("human", "{input}")]) 27 | 28 | model = ChatOpenAI() 29 | 30 | def _sanitize_output(text: str): 31 | _, after = text.split("```python") 32 | return after.split("```")[0] 33 | 34 | chain = prompt | model | StrOutputParser() 35 | 36 | print(chain.invoke({"input": "Write the function to sort the list. Then call the function by pasing [1,4,2]"})) 37 | """ 38 | ```python 39 | def sort_list(lst): 40 | return sorted(lst) 41 | 42 | my_list = [1, 4, 2] 43 | sorted_list = sort_list(my_list) 44 | print(sorted_list) 45 | ``` 46 | """ 47 | repl_chain = chain | _sanitize_output | PythonREPL().run 48 | 49 | print(repl_chain.invoke({"input": "Write the function to sort the list. Then call the function by pasing [1,4,2]"})) 50 | """ 51 | Python REPL can execute arbitrary code. Use with caution. 52 | [1, 2, 4] 53 | """ 54 | -------------------------------------------------------------------------------- /lcel/faiss_index/index.faiss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimz119/learn-langchain/f0c2604aab7cab7f2c4d44d7bb484fd0dc2463bc/lcel/faiss_index/index.faiss -------------------------------------------------------------------------------- /lcel/faiss_index/index.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimz119/learn-langchain/f0c2604aab7cab7f2c4d44d7bb484fd0dc2463bc/lcel/faiss_index/index.pkl -------------------------------------------------------------------------------- /lcel/multiple_chains.py: -------------------------------------------------------------------------------- 1 | # Note as of 02/27/2024 2 | # before you start you need to install the following 3 | # pip install langchain==0.1.9 langchain-openai==0.0.8 4 | from operator import itemgetter 5 | from langchain_openai import ChatOpenAI 6 | from langchain.prompts import ChatPromptTemplate 7 | from langchain.schema import StrOutputParser 8 | 9 | # Example1 10 | prompt1 = ChatPromptTemplate.from_template("what is the city {person} is from?") 11 | prompt2 = ChatPromptTemplate.from_template( 12 | "what country is the city {city} in? respond in {language}" 13 | ) 14 | 15 | model = ChatOpenAI() 16 | 17 | chain1 = prompt1 | model | StrOutputParser() 18 | 19 | chain2 = ( 20 | {"city": chain1, "language": itemgetter("language")} 21 | | prompt2 22 | | model 23 | | StrOutputParser() 24 | ) 25 | 26 | print(chain2.invoke({"person": "obama", "language": "english"})) 27 | """ 28 | Barack Obama, the 44th President of the United States, was born in Honolulu, Hawaii, which is located in the United States of America. 29 | """ 30 | 31 | # Example2 32 | from langchain.schema.runnable import RunnablePassthrough 33 | 34 | # Generates a prompt asking for a color based on a given attribute. 35 | prompt1 = ChatPromptTemplate.from_template( 36 | "generate a {attribute} color. Return the name of the color and nothing else:" 37 | ) 38 | 39 | # Asks for a fruit of a specified color. 40 | prompt2 = ChatPromptTemplate.from_template( 41 | "what is a fruit of color: {color}. Return the name of the fruit and nothing else:" 42 | ) 43 | 44 | # Requests the name of a country with a flag containing a certain color. 45 | prompt3 = ChatPromptTemplate.from_template( 46 | "what is a country with a flag that has the color: {color}. Return the name of the country and nothing else:" 47 | ) 48 | 49 | # Forms a prompt asking for the color of a specific fruit and the flag of a specific country 50 | prompt4 = ChatPromptTemplate.from_template( 51 | "What is the color of {fruit} and the flag of {country}?" 52 | ) 53 | 54 | # Extract model message 55 | model_parser = model | StrOutputParser() 56 | 57 | # Generating Color 58 | color_generator = ( 59 | {"attribute": RunnablePassthrough()} | prompt1 | {"color": model_parser} 60 | ) 61 | 62 | # Takes a color and uses prompt2 to ask for a corresponding fruit 63 | color_to_fruit = prompt2 | model_parser 64 | 65 | # uses prompt3 to find a country with a flag containing that color 66 | color_to_country = prompt3 | model_parser 67 | 68 | 69 | question_generator = ( 70 | color_generator | {"fruit": color_to_fruit, "country": color_to_country} | prompt4 71 | ) 72 | 73 | prompt = question_generator.invoke("warm") 74 | print(prompt) 75 | """ 76 | messages=[HumanMessage(content='What is the color of Coral. and the flag of Comoros?')] 77 | """ 78 | 79 | print(model.invoke(prompt)) 80 | """ 81 | content='The color of a pomegranate is typically a deep red or maroon. The flag of Armenia consists of three horizontal bands of equal width - the top band is red, the middle band is blue, and the bottom band is orange.' 82 | """ 83 | 84 | 85 | # Example3 86 | # Branching and Merging 87 | """ 88 | Input 89 | / \ 90 | / \ 91 | Branch1 Branch2 92 | \ / 93 | \ / 94 | Combine 95 | """ 96 | planner = ( 97 | ChatPromptTemplate.from_template("Generate an argument about: {input}") 98 | | ChatOpenAI() 99 | | StrOutputParser() 100 | | {"base_response": RunnablePassthrough()} 101 | ) 102 | 103 | arguments_for = ( 104 | ChatPromptTemplate.from_template( 105 | "List the pros or positive aspects of {base_response}" 106 | ) 107 | | ChatOpenAI() 108 | | StrOutputParser() 109 | ) 110 | arguments_against = ( 111 | ChatPromptTemplate.from_template( 112 | "List the cons or negative aspects of {base_response}" 113 | ) 114 | | ChatOpenAI() 115 | | StrOutputParser() 116 | ) 117 | 118 | final_responder = ( 119 | ChatPromptTemplate.from_messages( 120 | [ 121 | ("ai", "{original_response}"), 122 | ("human", "Pros:\n{results_1}\n\nCons:\n{results_2}"), 123 | ("system", "Generate a final response given the critique"), 124 | ] 125 | ) 126 | | ChatOpenAI() 127 | | StrOutputParser() 128 | ) 129 | 130 | chain = ( 131 | planner 132 | | { 133 | "results_1": arguments_for, 134 | "results_2": arguments_against, 135 | "original_response": itemgetter("base_response"), 136 | } 137 | | final_responder 138 | ) 139 | 140 | print(chain.invoke({"input": "scrum"})) 141 | """ 142 | While Scrum has its pros and cons, it is important to recognize that no project management framework is a one-size-fits-all solution. The cons mentioned should be considered in the context of the specific project and organization. 143 | 144 | For example, while Scrum may have a lack of predictability, this can be mitigated by implementing effective estimation techniques and regularly reassessing and adjusting plans. Additionally, while Scrum relies on team communication, organizations can invest in improving communication practices and tools to address any gaps or issues. 145 | 146 | Similarly, while Scrum may have limitations for large projects, organizations can adapt Scrum by implementing scaled agile frameworks like SAFe or LeSS to address complexities and dependencies. 147 | 148 | Furthermore, while Scrum may prioritize working software over comprehensive documentation, it does not mean that documentation is disregarded entirely. Organizations can establish guidelines and processes to ensure that essential documentation is maintained alongside the iterative development. 149 | 150 | Regarding role clarity, organizations can establish clear role definitions and ensure that team members understand their responsibilities and accountabilities. This can be achieved through effective communication and regular feedback. 151 | 152 | Lastly, while Scrum relies on experienced Scrum Masters, organizations can invest in training and development programs to enhance the skills and knowledge of Scrum Masters, ensuring effective facilitation of the Scrum process. 153 | 154 | In conclusion, while Scrum has its limitations, many of these can be addressed and mitigated through proper implementation, adaptation, and organizational support. It is important to carefully consider the specific project and organizational context to determine if Scrum is the right fit and to make necessary adjustments to maximize its benefits and overcome any potential drawbacks. 155 | """ 156 | -------------------------------------------------------------------------------- /lcel/prompt_llm_function_call.py: -------------------------------------------------------------------------------- 1 | # Note as of 02/27/2024 2 | # before you start you need to install the following 3 | # pip install langchain==0.1.9 langchain-openai==0.0.8 4 | from langchain_openai import ChatOpenAI 5 | from langchain.prompts import ChatPromptTemplate 6 | from langchain.output_parsers.openai_functions import ( 7 | JsonOutputFunctionsParser, 8 | JsonKeyOutputFunctionsParser) 9 | from langchain.schema.runnable import RunnableParallel, RunnablePassthrough 10 | 11 | 12 | prompt = ChatPromptTemplate.from_template("tell me a short joke about {topic}") 13 | model = ChatOpenAI() 14 | 15 | # function call 16 | functions = [ 17 | { 18 | "name": "joke", 19 | "description": "A joke", 20 | "parameters": { 21 | "type": "object", 22 | "properties": { 23 | "setup": {"type": "string", "description": "The setup for the joke"}, 24 | "punchline": { 25 | "type": "string", 26 | "description": "The punchline for the joke", 27 | }, 28 | }, 29 | "required": ["setup", "punchline"], 30 | }, 31 | } 32 | ] 33 | chain = prompt | model.bind(function_call={"name": "joke"}, functions=functions) 34 | # print(chain.invoke({"topic": "ice cream"}, config={})) 35 | """ 36 | content='' additional_kwargs={'function_call': {'arguments': '{\n "setup": "Why did the ice cream go to therapy?",\n "punchline": "Because it was feeling a little melty!"\n}', 'name': 'joke'}} 37 | """ 38 | 39 | # chain = prompt | model.bind(function_call={"name": "joke"}, functions=functions) | JsonOutputFunctionsParser() 40 | # print(chain.invoke({"topic": "ice cream"})) 41 | """ 42 | {'setup': 'Why did the ice cream go to therapy?', 'punchline': 'Because it had too many sprinkles of anxiety!'} 43 | """ 44 | 45 | # chain = prompt | model.bind(function_call={"name": "joke"}, functions=functions) | JsonKeyOutputFunctionsParser(key_name="setup") 46 | # print(chain.invoke({"topic": "ice cream"})) 47 | """ 48 | Why did the ice cream go to therapy? 49 | """ 50 | 51 | map_ = RunnableParallel(topic=RunnablePassthrough()) 52 | chain = ( 53 | map_ 54 | | prompt 55 | | model.bind(function_call={"name": "joke"}, functions=functions) 56 | | JsonKeyOutputFunctionsParser(key_name="setup") 57 | ) 58 | print(chain.invoke("ice cream")) 59 | """ 60 | Why did the ice cream go to therapy? 61 | """ 62 | 63 | chain = ( 64 | {"topic": RunnablePassthrough()} 65 | | prompt 66 | | model.bind(function_call={"name": "joke"}, functions=functions) 67 | | JsonKeyOutputFunctionsParser(key_name="setup") 68 | ) 69 | print(chain.invoke("ice cream")) 70 | """ 71 | Why did the ice cream break up with the cone? 72 | """ -------------------------------------------------------------------------------- /lcel/prompt_llm_output_parser.py: -------------------------------------------------------------------------------- 1 | # Note as of 02/27/2024 2 | # before you start you need to install the following 3 | # pip install langchain==0.1.9 langchain-openai==0.0.8 4 | from langchain_openai import ChatOpenAI 5 | from langchain.prompts import ChatPromptTemplate 6 | from langchain.schema.output_parser import StrOutputParser 7 | 8 | prompt = ChatPromptTemplate.from_template("tell me a short joke about {topic}") 9 | prompt_value = prompt.invoke({"topic": "ice cream"}) 10 | """ 11 | messages=[HumanMessage(content='tell me a short joke about ice cream')] 12 | """ 13 | print(prompt_value.to_string()) 14 | """ 15 | Human: tell me a short joke about ice cream 16 | """ 17 | 18 | model = ChatOpenAI() 19 | message = model.invoke(prompt_value) 20 | """ 21 | content='Why did the ice cream go to therapy? \n\nBecause it had too many scoops of emotions!' 22 | """ 23 | 24 | # from langchain.llms import OpenAI 25 | # llm = OpenAI(model="gpt-3.5-turbo-instruct") 26 | # llm_message = llm.invoke(prompt_value) 27 | """ 28 | Why did the ice cream go to therapy? 29 | 30 | Because it was having a meltdown! 31 | """ 32 | 33 | output_parser = StrOutputParser() 34 | print(output_parser.invoke(message)) 35 | """ 36 | Why did the ice cream go to therapy? 37 | 38 | Because it had a meltdown! 39 | """ 40 | 41 | # similar to unix pipe operator 42 | chain = prompt | model | output_parser 43 | 44 | result = chain.invoke({"topic": "ice cream"}) 45 | print(result) 46 | """ 47 | Why did the ice cream go to therapy? 48 | 49 | Because it had too many sprinkles of anxiety! 50 | # """ 51 | -------------------------------------------------------------------------------- /lcel/querying_sql_db.py: -------------------------------------------------------------------------------- 1 | # Note as of 02/27/2024 2 | # before you start you need to install the following 3 | # pip install langchain==0.1.9 langchain-openai==0.0.8 4 | from langchain.prompts import ChatPromptTemplate 5 | from langchain_community.utilities.sql_database import SQLDatabase 6 | 7 | 8 | template = """Based on the table schema below, write a SQL query that would answer the user's question: 9 | {schema} 10 | 11 | Question: {question} 12 | SQL Query:""" 13 | prompt = ChatPromptTemplate.from_template(template) 14 | 15 | # sqlite3 Chinook.db 16 | # .read Chinook_Sqlite.sql 17 | # download the sql file from the link below 18 | # https://raw.githubusercontent.com/lerocha/chinook-database/master/ChinookDatabase/DataSources/Chinook_Sqlite.sql 19 | 20 | db = SQLDatabase.from_uri("sqlite:///./Chinook.db") 21 | 22 | def get_schema(_): 23 | return db.get_table_info() 24 | 25 | def run_query(query): 26 | return db.run(query) 27 | 28 | from langchain_openai import ChatOpenAI 29 | from langchain.schema import StrOutputParser 30 | from langchain.schema.runnable import RunnablePassthrough 31 | 32 | model = ChatOpenAI() 33 | 34 | sql_response = ( 35 | RunnablePassthrough.assign(schema=get_schema) 36 | | prompt 37 | | model.bind(stop=["\nSQLResult:"]) 38 | | StrOutputParser() 39 | ) 40 | 41 | print(sql_response.invoke({"question": "How many employees are there?"})) 42 | """ 43 | SELECT COUNT(*) FROM Employee 44 | """ 45 | 46 | template = """Based on the table schema below, question, sql query, and sql response, write a natural language response: 47 | {schema} 48 | 49 | Question: {question} 50 | SQL Query: {query} 51 | SQL Response: {response}""" 52 | prompt_response = ChatPromptTemplate.from_template(template) 53 | 54 | full_chain = ( 55 | RunnablePassthrough.assign(query=sql_response) 56 | | RunnablePassthrough.assign( 57 | schema=get_schema, 58 | response=lambda x: run_query(x["query"]), 59 | ) 60 | | prompt_response 61 | | model 62 | ) 63 | 64 | print(full_chain.invoke({"question": "How many employees are there?"})) 65 | """ 66 | content='There are 8 employees.' 67 | """ 68 | -------------------------------------------------------------------------------- /lcel/rag.py: -------------------------------------------------------------------------------- 1 | # Note as of 02/27/2024 2 | # before you start you need to install the following 3 | # pip install langchain==0.1.9 langchain-openai==0.0.8 4 | from operator import itemgetter 5 | from langchain_openai import ChatOpenAI 6 | from langchain_openai import OpenAIEmbeddings 7 | from langchain.prompts import ChatPromptTemplate 8 | from langchain.schema.output_parser import StrOutputParser 9 | from langchain.schema.runnable import RunnableLambda, RunnablePassthrough 10 | from langchain_community.vectorstores.faiss import FAISS 11 | 12 | # It requires `pip install langchain openai faiss-cpu tiktoken` 13 | embedding_model = OpenAIEmbeddings() 14 | vectorstore = FAISS.from_texts( 15 | ["harrison worked at kensho"], embedding=embedding_model 16 | ) 17 | # save 18 | vectorstore.save_local("faiss_index") 19 | 20 | vectorstore_new = FAISS.load_local("faiss_index", embedding_model) 21 | 22 | retriever = vectorstore_new.as_retriever() 23 | 24 | template = """Answer the question based only on the following context: 25 | {context} 26 | 27 | Question: {question} 28 | """ 29 | prompt = ChatPromptTemplate.from_template(template) 30 | 31 | model = ChatOpenAI() 32 | 33 | chain = ( 34 | {"context": retriever, "question": RunnablePassthrough()} 35 | | prompt 36 | | model 37 | | StrOutputParser() 38 | ) 39 | 40 | print(chain.invoke("where did harrison work?")) 41 | """ 42 | Harrison worked at Kensho. 43 | """ 44 | 45 | template = """Answer the question based only on the following context: 46 | {context} 47 | 48 | Question: {question} 49 | 50 | Answer in the following language: {language} 51 | """ 52 | prompt = ChatPromptTemplate.from_template(template) 53 | 54 | chain = ( 55 | { 56 | "context": itemgetter("question") | retriever, 57 | "question": itemgetter("question"), 58 | "language": itemgetter("language"), 59 | } 60 | | prompt 61 | | model 62 | | StrOutputParser() 63 | ) 64 | 65 | print(chain.invoke({"question": "where did harrison work", "language": "italian"})) 66 | """ 67 | Harrison ha lavorato a Kensho. 68 | """ 69 | 70 | # # itemgetter example 71 | # from operator import itemgetter 72 | # # Suppose we have a dictionary 73 | # person = {'name': 'Alice', 'age': 30, 'job': 'Engineer'} 74 | # # We can use itemgetter to create a function that fetches the 'name' from a dictionary 75 | # get_name = itemgetter('name') 76 | # # Now, when we use this function with our dictionary 77 | # name = get_name(person) 78 | # print(name) # Output: Alice 79 | -------------------------------------------------------------------------------- /lcel/runnable_branch.py: -------------------------------------------------------------------------------- 1 | # Note as of 02/27/2024 2 | # before you start you need to install the following 3 | # pip install langchain==0.1.9 langchain-openai==0.0.8 4 | from langchain_openai import ChatOpenAI 5 | from langchain.prompts import PromptTemplate 6 | from langchain.schema.output_parser import StrOutputParser 7 | 8 | chain = ( 9 | PromptTemplate.from_template( 10 | """Given the user question below, classify it as either being about `Strawberry`, `Banana`, or `Other`. 11 | 12 | Do not respond with more than one word. 13 | 14 | 15 | {question} 16 | 17 | 18 | Classification:""" 19 | ) 20 | | ChatOpenAI() 21 | | StrOutputParser() 22 | ) 23 | 24 | print(chain.invoke({"question": "What is the fruit that has red color?"})) 25 | """ 26 | Strawberry 27 | """ 28 | 29 | strawberry_chain = ( 30 | PromptTemplate.from_template( 31 | """You are an expert about strawberry. \ 32 | Always answer questions starting with "As a Strawberry expert ... ". \ 33 | Respond to the following question: 34 | 35 | # Question: {question} 36 | # Answer:""" 37 | ) 38 | | ChatOpenAI() 39 | ) 40 | 41 | banana_chain = ( 42 | PromptTemplate.from_template( 43 | """You are an expert about banana. \ 44 | Always answer questions starting with "As a Banana expert ... ". \ 45 | Respond to the following question: 46 | 47 | # Question: {question} 48 | # Answer:""" 49 | ) 50 | | ChatOpenAI() 51 | ) 52 | 53 | general_chain = ( 54 | PromptTemplate.from_template( 55 | """Respond to the following question: 56 | 57 | Question: {question} 58 | Answer:""" 59 | ) 60 | | ChatOpenAI() 61 | ) 62 | 63 | 64 | from langchain.schema.runnable import RunnableBranch 65 | 66 | # the first element is a condition (a lambda function) and 67 | # the second element is the chain to execute if the condition is true 68 | branch = RunnableBranch( 69 | (lambda x: "strawberry" in x["topic"].lower(), strawberry_chain), # type: ignore 70 | (lambda x: "banana" in x["topic"].lower(), banana_chain), # type: ignore 71 | general_chain, 72 | ) 73 | 74 | # chain is invoked to classify the question, and its output is stored under the key topic. 75 | # The original question is passed through unchanged under the key question. 76 | full_chain = {"topic": chain, "question": lambda x: x["question"]} | branch 77 | 78 | print(full_chain.invoke({"question": "What is the fruit that has red color?"})) 79 | print(full_chain.invoke({"question": "What is the fruit that has yellow color?"})) 80 | print(full_chain.invoke({"question": "What is the fruit that has green color?"})) 81 | """ 82 | content='As a Strawberry expert, I can tell you that strawberries are the fruit that has a vibrant red color.' 83 | content='As a Banana expert, the fruit that has a yellow color is the banana.' 84 | content='The fruit that has a green color is typically an apple or a lime.' 85 | """ -------------------------------------------------------------------------------- /lcel/runnable_lambda.py: -------------------------------------------------------------------------------- 1 | # Note as of 02/27/2024 2 | # before you start you need to install the following 3 | # pip install langchain==0.1.9 langchain-openai==0.0.8 4 | from operator import itemgetter 5 | from langchain_openai import ChatOpenAI 6 | from langchain.prompts import ChatPromptTemplate 7 | from langchain.schema.runnable import RunnableLambda 8 | 9 | def length_function(text): 10 | return len(text) 11 | 12 | def _multiple_length_function(text1, text2): 13 | return len(text1) * len(text2) 14 | 15 | def multiple_length_function(_dict): 16 | return _multiple_length_function(_dict["text1"], _dict["text2"]) 17 | 18 | prompt = ChatPromptTemplate.from_template("what is {a} + {b}") 19 | model = ChatOpenAI() 20 | chain = ( 21 | { 22 | "a": itemgetter("foo") | RunnableLambda(length_function), 23 | "b": {"text1": itemgetter("foo"), "text2": itemgetter("bar")} 24 | | RunnableLambda(multiple_length_function), 25 | } 26 | | prompt 27 | | model 28 | ) 29 | print(chain.invoke({"foo": "bar", "bar": "gah"})) 30 | """ 31 | content='3 + 9 = 12' 32 | """ -------------------------------------------------------------------------------- /lcel/runnable_parallel.py: -------------------------------------------------------------------------------- 1 | # Note as of 02/27/2024 2 | # before you start you need to install the following 3 | # pip install langchain==0.1.9 langchain-openai==0.0.8 4 | from operator import itemgetter 5 | from langchain.schema.runnable import RunnableParallel, RunnablePassthrough 6 | 7 | runnable = RunnableParallel( 8 | passed=RunnablePassthrough() 9 | ) 10 | 11 | print(runnable.invoke({"num": 1})) 12 | """ 13 | {'passed': {'num': 1}} 14 | """ 15 | 16 | runnable = RunnableParallel( 17 | passed=RunnablePassthrough(), 18 | extra=RunnablePassthrough.assign(mult=lambda x: x["num"] * 3), 19 | modified=lambda x: x["num"] + 1, 20 | ) 21 | 22 | print(runnable.invoke({"num": 1})) 23 | """ 24 | {'passed': {'num': 1}, 'extra': {'num': 1, 'mult': 3}, 'modified': 2} 25 | """ 26 | 27 | from langchain_openai import ChatOpenAI 28 | from langchain.prompts import ChatPromptTemplate 29 | from langchain.schema.runnable import RunnableParallel 30 | 31 | model = ChatOpenAI() 32 | 33 | joke_chain = ChatPromptTemplate.from_template("tell me a joke about {topic}") | model 34 | poem_chain = ( 35 | ChatPromptTemplate.from_template("write a 2-line poem about {topic}") | model 36 | ) 37 | 38 | # easy to execute multiple Runnables in parallel 39 | map_chain = RunnableParallel(joke=joke_chain, poem=poem_chain) 40 | 41 | print(map_chain.invoke({"topic": "bear"})) 42 | """ 43 | { 44 | 'joke': AIMessage(content="Why did the bear break up with his girlfriend? \n\nBecause he couldn't bear the relationship any longer!"), 45 | 'poem': AIMessage(content='In the dark woods, a bear roams free,\nA majestic creature, wild and full of mystery.') 46 | } 47 | """ 48 | -------------------------------------------------------------------------------- /llm_chatmodels.py: -------------------------------------------------------------------------------- 1 | # Note as of 02/27/2024 2 | # before you start you need to install the following 3 | # pip install langchain==0.1.9 langchain-openai==0.0.8 4 | from langchain_openai import OpenAI, ChatOpenAI 5 | 6 | # Configure OpenAI API key 7 | # export OPENAI_API_KEY="" 8 | 9 | llm = OpenAI() 10 | chat_model = ChatOpenAI() 11 | 12 | from langchain_core.messages import HumanMessage, SystemMessage 13 | 14 | text = "What would be a good company name for a company that makes colorful socks?" 15 | messages = [SystemMessage(content="You are the teenager"), 16 | HumanMessage(content=text)] 17 | 18 | # Takes in a string, returns a string. 19 | print(llm.invoke(text)) 20 | 21 | # Takes in a list of BaseMessage, returns a BaseMessage. 22 | print(chat_model.invoke(messages)) 23 | -------------------------------------------------------------------------------- /mcp/README.md: -------------------------------------------------------------------------------- 1 | # MCP 2 | - Reference: https://github.com/langchain-ai/langchain-mcp-adapters 3 | 4 | # installation 5 | ```bash 6 | virtualenv --python= venv 7 | source venv/bin/activate 8 | pip install -r requirements.txt 9 | ``` -------------------------------------------------------------------------------- /mcp/client.py: -------------------------------------------------------------------------------- 1 | # Imports Python's built-in asyncio module, which is used to run asynchronous code using async/await. 2 | import asyncio 3 | 4 | # ClientSession: manages a session with an MCP-compliant tool or service. 5 | # StdioServerParameters: used to specify how to start the tool (like a subprocess). 6 | from mcp import ClientSession, StdioServerParameters 7 | 8 | # connects to a tool over standard input/output. 9 | from mcp.client.stdio import stdio_client 10 | 11 | # Imports a helper function that loads tools that support the MCP protocol, making them compatible with LangChain. 12 | from langchain_mcp_adapters.tools import load_mcp_tools 13 | 14 | # Imports a function to create an agent that follows the ReAct pattern (Reasoning + Acting) from LangGraph. 15 | # ReAct agents can use tools and think step-by-step. 16 | from langgraph.prebuilt import create_react_agent 17 | 18 | # Imports the OpenAI chat model interface from LangChain. 19 | from langchain_openai import ChatOpenAI 20 | model = ChatOpenAI(model="gpt-4o") 21 | 22 | # Prepares the parameters to launch a tool server via math_server.py using Python. 23 | server_params = StdioServerParameters( 24 | command="python", 25 | # Make sure to update to the full absolute path to your math_server.py file 26 | args=["/Users/seungjoonlee/git/learn-langchain/mcp/math_server.py"], 27 | ) 28 | 29 | # Defines an asynchronous main function. 30 | async def main(): 31 | # Starts the math_server.py subprocess and establishes a connection using stdin and stdout. 32 | # Returns read and write streams for communication. 33 | async with stdio_client(server_params) as (read, write): 34 | # Wraps the read/write streams in a ClientSession to interact with the tool more easily. 35 | async with ClientSession(read, write) as session: 36 | # Initialize the connection 37 | await session.initialize() 38 | 39 | # Loads the tools exposed by the running math_server.py. 40 | # These tools are now in a format that the LangChain agent can use. 41 | tools = await load_mcp_tools(session) 42 | 43 | # Creates an agent that can reason and call the MCP tools using the GPT-4o model. 44 | agent = create_react_agent(model, tools) 45 | 46 | # Invokes the agent asynchronously and gives it a user message. 47 | # The agent will process the question, possibly call the math tool, and return a response. 48 | agent_response = await agent.ainvoke({"messages": "what's (3 + 5) x 12?"}) 49 | print(agent_response) 50 | 51 | # Run the async main function 52 | if __name__ == "__main__": 53 | asyncio.run(main()) 54 | -------------------------------------------------------------------------------- /mcp/math_server.py: -------------------------------------------------------------------------------- 1 | # Imports FastMCP, a lightweight framework for exposing Python functions as MCP-compatible tools. 2 | # This lets other programs (like agents) call your Python functions over a standard interface (like stdio or HTTP). 3 | from mcp.server.fastmcp import FastMCP 4 | 5 | # Creates an instance of FastMCP and names the tool "Math". 6 | # This name is optional, but it's helpful for debugging or when managing multiple tools. 7 | mcp = FastMCP("Math") 8 | 9 | # The @mcp.tool() decorator exposes this function as a callable tool via MCP. 10 | @mcp.tool() 11 | def add(a: int, b: int) -> int: 12 | """Add two numbers""" 13 | return a + b 14 | 15 | @mcp.tool() 16 | def multiply(a: int, b: int) -> int: 17 | """Multiply two numbers""" 18 | return a * b 19 | 20 | if __name__ == "__main__": 21 | # If this script is run directly, it starts the MCP server using stdio (standard input/output). 22 | # This allows the agent (in your first script) to connect to this tool by launching it as a subprocess 23 | mcp.run(transport="stdio") 24 | -------------------------------------------------------------------------------- /mcp/mcp_doc_messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "messages": [ 3 | { 4 | "type": "HumanMessage", 5 | "content": "how does langchain chatbot works? check the latest documenation. but make it short as 5 lines", 6 | "additional_kwargs": {}, 7 | "response_metadata": {}, 8 | "id": "f5f461cc-05a7-402e-9b81-3d6919930627" 9 | }, 10 | { 11 | "type": "AIMessage", 12 | "content": "", 13 | "additional_kwargs": { 14 | "tool_calls": [ 15 | { 16 | "id": "call_Yy1lavSk3GyKWka30ETksAkw", 17 | "function": { 18 | "arguments": "{}", 19 | "name": "list_doc_sources" 20 | }, 21 | "type": "function" 22 | } 23 | ], 24 | "refusal": null 25 | }, 26 | "response_metadata": { 27 | "token_usage": { 28 | "completion_tokens": 12, 29 | "prompt_tokens": 232, 30 | "total_tokens": 244 31 | }, 32 | "model_name": "gpt-4o-mini-2024-07-18", 33 | "finish_reason": "tool_calls" 34 | }, 35 | "id": "run-cb28ae21-5ae4-4c71-9fa1-cc08f9b2dbdd-0" 36 | }, 37 | { 38 | "type": "ToolMessage", 39 | "content": "LangChain\nURL: https://python.langchain.com/llms.txt\n\n", 40 | "name": "list_doc_sources", 41 | "id": "7405d543-3795-4ed7-b774-318b555ddf38", 42 | "tool_call_id": "call_Yy1lavSk3GyKWka30ETksAkw" 43 | }, 44 | { 45 | "type": "AIMessage", 46 | "content": "", 47 | "additional_kwargs": { 48 | "tool_calls": [ 49 | { 50 | "id": "call_csnsmJMuK3KyMs61ZUiikbTH", 51 | "function": { 52 | "arguments": "{\"url\":\"https://python.langchain.com/llms.txt\"}", 53 | "name": "fetch_docs" 54 | }, 55 | "type": "function" 56 | } 57 | ], 58 | "refusal": null 59 | }, 60 | "response_metadata": { 61 | "token_usage": { 62 | "completion_tokens": 24, 63 | "prompt_tokens": 268, 64 | "total_tokens": 292 65 | }, 66 | "model_name": "gpt-4o-mini-2024-07-18", 67 | "finish_reason": "tool_calls" 68 | }, 69 | "id": "run-97326a71-27b6-44f3-8173-8bb8b8fd903a-0" 70 | }, 71 | { 72 | "type": "ToolMessage", 73 | "content": "# LangChain\n## High level\n[Why LangChain?](https://python.langchain.com/docs/concepts/why_langchain/): considering using LangChain, when building complex AI applications, and when needing to evaluate AI applications This page discusses the main reasons to use LangChain: standardized component interfaces, orchestration capabilities, and observability/evaluation through L...tool calling, and structured outputs. The architecture supports multimodal data, allowing text, images, and other formats in interactions. Additionally, the framework includes memory management for maintaining conversation context. By leveraging these components, developers can create dynamic and interactive chatbot applications effectively.", 74 | "additional_kwargs": { 75 | "refusal": null 76 | }, 77 | "response_metadata": { 78 | "token_usage": { 79 | "completion_tokens": 83, 80 | "prompt_tokens": 26007, 81 | "total_tokens": 26090 82 | }, 83 | "model_name": "gpt-4o-mini-2024-07-18", 84 | "finish_reason": "stop" 85 | }, 86 | "id": "run-3801de5d-bf5c-42c6-9152-4aec779badda-0" 87 | }, 88 | { 89 | "type": "AIMessage", 90 | "content": "LangChain chatbot operates by utilizing chat models that facilitate conversation through structured messaging. It integrates various components such as tools for functionality and memory for context management. The framework supports advanced techniques like tool calling for external functions, structured outputs for formatted responses, and multimodal inputs compatible with text and media. Additionally, it effectively manages chat history to retain context for better interaction. By connecting these elements, LangChain enables the creation of responsive and context-aware conversational agents.", 91 | "additional_kwargs": { 92 | "refusal": null 93 | }, 94 | "response_metadata": { 95 | "token_usage": { 96 | "completion_tokens": 92, 97 | "prompt_tokens": 26007, 98 | "total_tokens": 26099, 99 | "completion_tokens_details": { 100 | "accepted_prediction_tokens": 0, 101 | "audio_tokens": 0, 102 | "reasoning_tokens": 0, 103 | "rejected_prediction_tokens": 0 104 | }, 105 | "prompt_tokens_details": { 106 | "audio_tokens": 0, 107 | "cached_tokens": 0 108 | } 109 | }, 110 | "model_name": "gpt-4o-mini-2024-07-18", 111 | "system_fingerprint": "fp_b376dfbbd5", 112 | "id": "chatcmpl-BIUYRlwT3cKoOREZxe2d6IIk1YWp5", 113 | "finish_reason": "stop", 114 | "logprobs": null 115 | }, 116 | "id": "run-9f71886f-18a7-4f13-ac5c-18dfdbc920fd-0", 117 | "usage_metadata": { 118 | "input_tokens": 26007, 119 | "output_tokens": 92, 120 | "total_tokens": 26099, 121 | "input_token_details": { 122 | "audio": 0, 123 | "cache_read": 0 124 | }, 125 | "output_token_details": { 126 | "audio": 0, 127 | "reasoning": 0 128 | } 129 | } 130 | } 131 | ] 132 | } 133 | -------------------------------------------------------------------------------- /mcp/mcpdoc_server.py: -------------------------------------------------------------------------------- 1 | # Imports Python's built-in asyncio module, which is used to run asynchronous code using async/await. 2 | import asyncio 3 | 4 | # Import MultiServerMCPClient to connect to multiple servers at once. 5 | from langchain_mcp_adapters.client import MultiServerMCPClient 6 | 7 | # Imports a function to create an agent that follows the ReAct pattern (Reasoning + Acting) from LangGraph. 8 | # ReAct agents can use tools and think step-by-step. 9 | from langgraph.prebuilt import create_react_agent 10 | 11 | # Imports the OpenAI chat model interface from LangChain. 12 | from langchain_openai import ChatOpenAI 13 | model = ChatOpenAI(model="gpt-4o-mini") 14 | 15 | # Defines an asynchronous main function. 16 | async def main(): 17 | async with MultiServerMCPClient( 18 | { 19 | "langgraph-docs-mcp": { 20 | "url": "http://localhost:8082/sse", 21 | "transport": "sse", 22 | } # type: ignore 23 | } 24 | ) as client: 25 | agent = create_react_agent(model, client.get_tools()) 26 | langgraph_response = await agent.ainvoke({"messages": "how does langchain chatbot works? check the latest documenation. but make it short as 5 lines"}) 27 | print(langgraph_response) 28 | 29 | # Run the async main function 30 | if __name__ == "__main__": 31 | asyncio.run(main()) 32 | -------------------------------------------------------------------------------- /mcp/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "messages": [ 3 | { 4 | "type": "HumanMessage", 5 | "content": "what's (3 + 5) x 12?", 6 | "additional_kwargs": {}, 7 | "response_metadata": {}, 8 | "id": "ee77cf94-5720-4090-ac63-29c10202b275" 9 | }, 10 | { 11 | "type": "AIMessage", 12 | "content": "", 13 | "additional_kwargs": { 14 | "tool_calls": [ 15 | { 16 | "id": "call_ANj2x44sYmCiLDikYBRzfwnf", 17 | "function": { 18 | "arguments": "{\"a\": 3, \"b\": 5}", 19 | "name": "add" 20 | }, 21 | "type": "function" 22 | }, 23 | { 24 | "id": "call_QbDXKPgWbIFMaDNl3H11uxGM", 25 | "function": { 26 | "arguments": "{\"a\": 8, \"b\": 12}", 27 | "name": "multiply" 28 | }, 29 | "type": "function" 30 | } 31 | ], 32 | "refusal": null 33 | }, 34 | "response_metadata": { 35 | "token_usage": { 36 | "completion_tokens": 51, 37 | "prompt_tokens": 77, 38 | "total_tokens": 128, 39 | "completion_tokens_details": { 40 | "accepted_prediction_tokens": 0, 41 | "audio_tokens": 0, 42 | "reasoning_tokens": 0, 43 | "rejected_prediction_tokens": 0 44 | }, 45 | "prompt_tokens_details": { 46 | "audio_tokens": 0, 47 | "cached_tokens": 0 48 | } 49 | }, 50 | "model_name": "gpt-4o-2024-08-06", 51 | "system_fingerprint": "fp_6dd05565ef", 52 | "id": "chatcmpl-BGI6hIzVveFo1yl3bUbCxZOcTS0ly", 53 | "finish_reason": "tool_calls", 54 | "logprobs": null 55 | }, 56 | "id": "run-4981eb3e-dc9d-48cf-8555-220b8d24f829-0", 57 | "tool_calls": [ 58 | { 59 | "name": "add", 60 | "args": { 61 | "a": 3, 62 | "b": 5 63 | }, 64 | "id": "call_ANj2x44sYmCiLDikYBRzfwnf", 65 | "type": "tool_call" 66 | }, 67 | { 68 | "name": "multiply", 69 | "args": { 70 | "a": 8, 71 | "b": 12 72 | }, 73 | "id": "call_QbDXKPgWbIFMaDNl3H11uxGM", 74 | "type": "tool_call" 75 | } 76 | ], 77 | "usage_metadata": { 78 | "input_tokens": 77, 79 | "output_tokens": 51, 80 | "total_tokens": 128, 81 | "input_token_details": { 82 | "audio": 0, 83 | "cache_read": 0 84 | }, 85 | "output_token_details": { 86 | "audio": 0, 87 | "reasoning": 0 88 | } 89 | } 90 | }, 91 | { 92 | "type": "ToolMessage", 93 | "content": "8", 94 | "name": "add", 95 | "id": "3fed1876-1c6a-47ef-95ea-651e33e47f39", 96 | "tool_call_id": "call_ANj2x44sYmCiLDikYBRzfwnf" 97 | }, 98 | { 99 | "type": "ToolMessage", 100 | "content": "96", 101 | "name": "multiply", 102 | "id": "21102f77-fddd-4a2c-a885-de9b9ec8269a", 103 | "tool_call_id": "call_QbDXKPgWbIFMaDNl3H11uxGM" 104 | }, 105 | { 106 | "type": "AIMessage", 107 | "content": "The result of \\((3 + 5) \\times 12\\) is 96.", 108 | "additional_kwargs": { 109 | "refusal": null 110 | }, 111 | "response_metadata": { 112 | "token_usage": { 113 | "completion_tokens": 22, 114 | "prompt_tokens": 143, 115 | "total_tokens": 165, 116 | "completion_tokens_details": { 117 | "accepted_prediction_tokens": 0, 118 | "audio_tokens": 0, 119 | "reasoning_tokens": 0, 120 | "rejected_prediction_tokens": 0 121 | }, 122 | "prompt_tokens_details": { 123 | "audio_tokens": 0, 124 | "cached_tokens": 0 125 | } 126 | }, 127 | "model_name": "gpt-4o-2024-08-06", 128 | "system_fingerprint": "fp_6dd05565ef", 129 | "id": "chatcmpl-BGI6hmjJs0RLcha8AO4sUT1bEZgo4", 130 | "finish_reason": "stop", 131 | "logprobs": null 132 | }, 133 | "id": "run-4d093615-ecd4-4918-bbe6-533ec590ccca-0", 134 | "usage_metadata": { 135 | "input_tokens": 143, 136 | "output_tokens": 22, 137 | "total_tokens": 165, 138 | "input_token_details": { 139 | "audio": 0, 140 | "cache_read": 0 141 | }, 142 | "output_token_details": { 143 | "audio": 0, 144 | "reasoning": 0 145 | } 146 | } 147 | } 148 | ] 149 | } -------------------------------------------------------------------------------- /mcp/multi_server_client.py: -------------------------------------------------------------------------------- 1 | # Imports Python's built-in asyncio module, which is used to run asynchronous code using async/await. 2 | import asyncio 3 | 4 | # Import MultiServerMCPClient to connect to multiple servers at once. 5 | from langchain_mcp_adapters.client import MultiServerMCPClient 6 | 7 | # Imports a function to create an agent that follows the ReAct pattern (Reasoning + Acting) from LangGraph. 8 | # ReAct agents can use tools and think step-by-step. 9 | from langgraph.prebuilt import create_react_agent 10 | 11 | # Imports the OpenAI chat model interface from LangChain. 12 | from langchain_openai import ChatOpenAI 13 | model = ChatOpenAI(model="gpt-4o-mini") 14 | 15 | # Defines an asynchronous main function. 16 | async def main(): 17 | async with MultiServerMCPClient( 18 | { 19 | # "math": { 20 | # "command": "python", 21 | # # Make sure to update to the full absolute path to your math_server.py file 22 | # "args": ["/Users/seungjoonlee/git/learn-langchain/mcp/math_server.py"], 23 | # "transport": "stdio", 24 | # }, 25 | # "weather": { 26 | # # make sure you start your weather server on port 8000 27 | # "url": "http://localhost:8000/sse", 28 | # "transport": "sse", 29 | # }, 30 | "langgraph-docs-mcp": { 31 | "url": "http://localhost:8082/sse", 32 | "transport": "sse", 33 | } 34 | } # type: ignore 35 | ) as client: 36 | agent = create_react_agent(model, client.get_tools()) 37 | # math_response = await agent.ainvoke({"messages": "what's (3 + 5) x 12?"}) 38 | # print(math_response) 39 | # weather_response = await agent.ainvoke({"messages": "what is the weather in nyc?"}) 40 | # print(weather_response) 41 | langgraph_response = await agent.ainvoke({"messages": "how does langchain chatbot works? check the latest documenation. but make it short as 5 lines"}) 42 | print(langgraph_response) 43 | 44 | # Run the async main function 45 | if __name__ == "__main__": 46 | asyncio.run(main()) 47 | -------------------------------------------------------------------------------- /mcp/requirements.txt: -------------------------------------------------------------------------------- 1 | mcp 2 | httpx 3 | langchain 4 | langchain-core 5 | langchain-community 6 | langchain-groq 7 | langchain-ollama 8 | langchain_mcp_adapters 9 | langchain_openai 10 | langgraph 11 | -------------------------------------------------------------------------------- /mcp/weather_messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "messages": [ 3 | { 4 | "type": "HumanMessage", 5 | "content": "what is the weather in nyc?", 6 | "additional_kwargs": {}, 7 | "response_metadata": {}, 8 | "id": "8b715c11-b901-43f8-b898-a7472dd8334c" 9 | }, 10 | { 11 | "type": "AIMessage", 12 | "content": "", 13 | "additional_kwargs": { 14 | "tool_calls": [ 15 | { 16 | "id": "call_LzlW2VG0SwTep5Qfyj4lxSk0", 17 | "function": { 18 | "arguments": "{\"location\":\"New York City\"}", 19 | "name": "get_weather" 20 | }, 21 | "type": "function" 22 | } 23 | ], 24 | "refusal": null 25 | }, 26 | "response_metadata": { 27 | "token_usage": { 28 | "completion_tokens": 17, 29 | "prompt_tokens": 92, 30 | "total_tokens": 109, 31 | "completion_tokens_details": { 32 | "accepted_prediction_tokens": 0, 33 | "audio_tokens": 0, 34 | "reasoning_tokens": 0, 35 | "rejected_prediction_tokens": 0 36 | }, 37 | "prompt_tokens_details": { 38 | "audio_tokens": 0, 39 | "cached_tokens": 0 40 | } 41 | }, 42 | "model_name": "gpt-4o-2024-08-06", 43 | "system_fingerprint": "fp_6dd05565ef", 44 | "id": "chatcmpl-BGUADKUBkqShPoK0BQg17qwxjNFAQ", 45 | "finish_reason": "tool_calls", 46 | "logprobs": null 47 | }, 48 | "id": "run-5183e6d7-4820-4c6e-9f25-d9e174cf6fd5-0", 49 | "tool_calls": [ 50 | { 51 | "name": "get_weather", 52 | "args": { 53 | "location": "New York City" 54 | }, 55 | "id": "call_LzlW2VG0SwTep5Qfyj4lxSk0", 56 | "type": "tool_call" 57 | } 58 | ], 59 | "usage_metadata": { 60 | "input_tokens": 92, 61 | "output_tokens": 17, 62 | "total_tokens": 109, 63 | "input_token_details": { 64 | "audio": 0, 65 | "cache_read": 0 66 | }, 67 | "output_token_details": { 68 | "audio": 0, 69 | "reasoning": 0 70 | } 71 | } 72 | }, 73 | { 74 | "type": "ToolMessage", 75 | "content": "It's always sunny in New York", 76 | "name": "get_weather", 77 | "id": "53302019-e212-4f0b-b3e0-e8e623949e17", 78 | "tool_call_id": "call_LzlW2VG0SwTep5Qfyj4lxSk0" 79 | }, 80 | { 81 | "type": "AIMessage", 82 | "content": "The weather in New York City is currently sunny.", 83 | "additional_kwargs": { 84 | "refusal": null 85 | }, 86 | "response_metadata": { 87 | "token_usage": { 88 | "completion_tokens": 12, 89 | "prompt_tokens": 122, 90 | "total_tokens": 134, 91 | "completion_tokens_details": { 92 | "accepted_prediction_tokens": 0, 93 | "audio_tokens": 0, 94 | "reasoning_tokens": 0, 95 | "rejected_prediction_tokens": 0 96 | }, 97 | "prompt_tokens_details": { 98 | "audio_tokens": 0, 99 | "cached_tokens": 0 100 | } 101 | }, 102 | "model_name": "gpt-4o-2024-08-06", 103 | "system_fingerprint": "fp_6dd05565ef", 104 | "id": "chatcmpl-BGUAERtv9wg4bgjUEEpfRt8gjBGUR", 105 | "finish_reason": "stop", 106 | "logprobs": null 107 | }, 108 | "id": "run-d0eae9b6-d941-44e9-bb41-89ddd69d5371-0", 109 | "usage_metadata": { 110 | "input_tokens": 122, 111 | "output_tokens": 12, 112 | "total_tokens": 134, 113 | "input_token_details": { 114 | "audio": 0, 115 | "cache_read": 0 116 | }, 117 | "output_token_details": { 118 | "audio": 0, 119 | "reasoning": 0 120 | } 121 | } 122 | } 123 | ] 124 | } -------------------------------------------------------------------------------- /mcp/weather_server.py: -------------------------------------------------------------------------------- 1 | # weather_server.py 2 | from typing import List 3 | from mcp.server.fastmcp import FastMCP 4 | 5 | mcp = FastMCP("Weather") 6 | 7 | @mcp.tool() 8 | async def get_weather(location: str) -> str: 9 | """Get weather for location.""" 10 | return "It's always sunny in New York" 11 | 12 | if __name__ == "__main__": 13 | mcp.run(transport="sse") 14 | -------------------------------------------------------------------------------- /memory/adding_memory.py: -------------------------------------------------------------------------------- 1 | # Note as of 02/27/2024 2 | # before you start you need to install the following 3 | # pip install langchain==0.1.9 langchain-openai==0.0.8 4 | from operator import itemgetter 5 | from langchain_openai import ChatOpenAI 6 | from langchain.memory import ConversationBufferMemory 7 | from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder 8 | from langchain.schema.runnable import RunnableLambda, RunnablePassthrough 9 | 10 | model = ChatOpenAI() 11 | prompt = ChatPromptTemplate.from_messages( 12 | [ 13 | ("system", "You are a helpful chatbot"), 14 | MessagesPlaceholder(variable_name="history"), 15 | ("human", "{input}"), 16 | ] 17 | ) 18 | 19 | memory = ConversationBufferMemory(return_messages=True) 20 | 21 | chain = ( 22 | RunnablePassthrough.assign( 23 | history=RunnableLambda(memory.load_memory_variables) | itemgetter("history") 24 | ) 25 | | prompt 26 | | model 27 | ) 28 | 29 | inputs = {"input": "Hello, My name is Joon"} 30 | response = chain.invoke(inputs) 31 | print(response) 32 | """ 33 | content="Hello Joon! It's nice to meet you. How can I assist you today?" 34 | """ 35 | memory.save_context(inputs, {"output": response.content}) # type: ignore 36 | 37 | print(memory.load_memory_variables({})) 38 | """ 39 | {'history': [HumanMessage(content='Hello, My name is Joon'), AIMessage(content='Hello Joon! How can I assist you today?')]} 40 | """ 41 | 42 | inputs = {"input": "whats my name"} 43 | response = chain.invoke(inputs) 44 | print(response) 45 | """ 46 | content='Your name is Joon. How can I assist you further, Joon? 47 | """ 48 | -------------------------------------------------------------------------------- /memory/conversation_buffer_window_memory.py: -------------------------------------------------------------------------------- 1 | from langchain.memory import ConversationBufferWindowMemory 2 | 3 | memory = ConversationBufferWindowMemory(k=1) 4 | memory.save_context({"input": "hi"}, {"output": "whats up"}) 5 | memory.save_context({"input": "not much you"}, {"output": "not much"}) 6 | 7 | print(memory.load_memory_variables({})) 8 | """ 9 | {'history': 'Human: not much you\nAI: not much'} 10 | """ 11 | 12 | memory = ConversationBufferWindowMemory(k=1, return_messages=True) 13 | memory.save_context({"input": "hi"}, {"output": "whats up"}) 14 | memory.save_context({"input": "not much you"}, {"output": "not much"}) 15 | 16 | print(memory.load_memory_variables({})) 17 | """ 18 | {'history': [HumanMessage(content='not much you'), AIMessage(content='not much')]} 19 | """ 20 | 21 | -------------------------------------------------------------------------------- /memory/conversation_summary_buffer_memory.py: -------------------------------------------------------------------------------- 1 | # Note as of 02/27/2024 2 | # before you start you need to install the following 3 | # pip install langchain==0.1.9 langchain-openai==0.0.8 4 | from langchain_openai import OpenAI 5 | from langchain.memory import ConversationSummaryBufferMemory 6 | 7 | llm = OpenAI() 8 | 9 | memory = ConversationSummaryBufferMemory(llm=llm, max_token_limit=10) 10 | memory.save_context({"input": "hi"}, {"output": "whats up"}) 11 | memory.save_context({"input": "not much you"}, {"output": "not much"}) 12 | 13 | print(memory.load_memory_variables({})) 14 | """ 15 | {'history': "System: \nThe human greets the AI and the AI responds asking what's up.\nHuman: not much you\nAI: not much"} 16 | """ 17 | 18 | memory = ConversationSummaryBufferMemory( 19 | llm=llm, max_token_limit=10, return_messages=True 20 | ) 21 | memory.save_context({"input": "hi"}, {"output": "whats up"}) 22 | memory.save_context({"input": "not much you"}, {"output": "not much"}) 23 | 24 | print(memory.load_memory_variables({})) 25 | """ 26 | {'history': [ 27 | SystemMessage(content="\nThe human greets the AI. The AI responds by asking what's up."), 28 | HumanMessage(content='not much you'), 29 | AIMessage(content='not much')]} 30 | """ 31 | 32 | messages = memory.chat_memory.messages 33 | previous_summary = "" 34 | print(memory.predict_new_summary(messages, previous_summary)) 35 | """ 36 | The human and AI exchange pleasantries and express that there is not much going on. 37 | """ 38 | -------------------------------------------------------------------------------- /memory/conversation_summary_memory.py: -------------------------------------------------------------------------------- 1 | # Note as of 02/27/2024 2 | # before you start you need to install the following 3 | # pip install langchain==0.1.9 langchain-openai==0.0.8 4 | from langchain.memory import ConversationSummaryMemory, ChatMessageHistory 5 | from langchain_openai import OpenAI 6 | 7 | memory = ConversationSummaryMemory(llm=OpenAI(temperature=0)) 8 | memory.save_context({"input": "hi"}, {"output": "whats up"}) 9 | 10 | print(memory.load_memory_variables({})) 11 | """ 12 | {'history': '\nThe human greets the AI, to which the AI responds.'} 13 | """ 14 | 15 | memory = ConversationSummaryMemory(llm=OpenAI(temperature=0), return_messages=True) 16 | memory.save_context({"input": "hi"}, {"output": "whats up"}) 17 | 18 | # print(memory.load_memory_variables({})) 19 | """ 20 | {'history': [SystemMessage(content='\nThe human greets the AI, to which the AI responds.')]} 21 | """ 22 | 23 | # Directly use the memory 24 | messages = memory.chat_memory.messages 25 | """ 26 | [HumanMessage(content='hi'), AIMessage(content='whats up')] 27 | """ 28 | previous_summary = "" 29 | print(memory.predict_new_summary(messages, previous_summary)) 30 | """ 31 | The human greets the AI, to which the AI responds. 32 | """ 33 | 34 | # Initializing with messages/existing summary 35 | history = ChatMessageHistory() 36 | history.add_user_message("hi") 37 | history.add_ai_message("hi there!") 38 | 39 | memory = ConversationSummaryMemory.from_messages( 40 | llm=OpenAI(temperature=0), 41 | chat_memory=history, 42 | return_messages=True 43 | ) 44 | print(memory.buffer) 45 | """ 46 | The human greets the AI, to which the AI responds with a friendly greeting. 47 | """ 48 | 49 | # summary initialization 50 | memory = ConversationSummaryMemory( 51 | llm=OpenAI(temperature=0), 52 | buffer="The human asks what the AI thinks of artificial intelligence. The AI thinks artificial intelligence is a force for good because it will help humans reach their full potential.", 53 | chat_memory=history, 54 | return_messages=True 55 | ) 56 | print(memory.buffer) 57 | """ 58 | The human asks what the AI thinks of artificial intelligence. The AI thinks artificial intelligence is a force for good because it will help humans reach their full potential. 59 | """ -------------------------------------------------------------------------------- /memory/entity.py: -------------------------------------------------------------------------------- 1 | # Note as of 02/27/2024 2 | # before you start you need to install the following 3 | # pip install langchain==0.1.9 langchain-openai==0.0.8 4 | from langchain_openai import OpenAI 5 | from langchain.memory import ConversationEntityMemory 6 | 7 | llm = OpenAI(temperature=0) 8 | memory = ConversationEntityMemory(llm=llm) 9 | _input = {"input": "Deven & Sam are working on a hackathon project"} 10 | memory.load_memory_variables(_input) 11 | memory.save_context( 12 | _input, 13 | {"output": " That sounds like a great project! What kind of project are they working on?"} 14 | ) 15 | 16 | print(memory.load_memory_variables({"input": 'who is Sam'})) 17 | """ 18 | {'history': 'Human: Deven & Sam are working on a hackathon project\n 19 | AI: That sounds like a great project! What kind of project are they working on?', 20 | 'entities': {'Sam': 'Sam is working on a hackathon project with Deven.'}} 21 | """ 22 | 23 | print(memory.load_memory_variables({"input": 'who is Deven'})) 24 | """ 25 | {'history': 'Human: Deven & Sam are working on a hackathon project\n 26 | AI: That sounds like a great project! What kind of project are they working on?', 27 | 'entities': { 28 | 'Deven': 'Deven is working on a hackathon project with Sam.', 29 | 'Sam': 'Sam is working on a hackathon project with Deven.'}} 30 | """ -------------------------------------------------------------------------------- /memory/memory_example.py: -------------------------------------------------------------------------------- 1 | from langchain.memory import ConversationBufferMemory 2 | 3 | memory = ConversationBufferMemory() 4 | memory.chat_memory.add_user_message("hi!") 5 | memory.chat_memory.add_ai_message("what's up?") 6 | 7 | print(memory.load_memory_variables({})) 8 | """ 9 | {'history': "Human: hi!\nAI: what's up?"} 10 | """ 11 | 12 | # memory key change 13 | memory = ConversationBufferMemory(memory_key="chat_history") 14 | memory.chat_memory.add_user_message("hi!") 15 | memory.chat_memory.add_ai_message("what's up?") 16 | 17 | print(memory.load_memory_variables({})) 18 | """ 19 | {'chat_history': "Human: hi!\nAI: what's up?"} 20 | """ 21 | 22 | # return in str type or list type 23 | memory = ConversationBufferMemory(return_messages=True) 24 | memory.chat_memory.add_user_message("hi!") 25 | memory.chat_memory.add_ai_message("what's up?") 26 | 27 | print(memory.load_memory_variables({})) 28 | """ 29 | {'history': [HumanMessage(content='hi!'), AIMessage(content="what's up?")]} 30 | """ -------------------------------------------------------------------------------- /output_parser.py: -------------------------------------------------------------------------------- 1 | from langchain.schema import BaseOutputParser 2 | 3 | class CommaSeparatedListOutputParser(BaseOutputParser): 4 | """Parse the output of an LLM call to a comma-separated list.""" 5 | 6 | 7 | def parse(self, text: str): 8 | """Parse the output of an LLM call.""" 9 | return text.strip().split(", ") 10 | 11 | # When LLM returns the response in text format 12 | print(CommaSeparatedListOutputParser().parse("hi, bye")) 13 | # >> ['hi', 'bye'] -------------------------------------------------------------------------------- /output_parsers/auto_fixing_parser.py: -------------------------------------------------------------------------------- 1 | # Note as of 02/27/2024 2 | # before you start you need to install the following 3 | # pip install langchain==0.1.9 langchain-openai==0.0.8 4 | from langchain_openai import ChatOpenAI 5 | from langchain.output_parsers import PydanticOutputParser 6 | from langchain.pydantic_v1 import BaseModel, Field 7 | from typing import List 8 | 9 | class Actor(BaseModel): 10 | name: str = Field(description="name of an actor") 11 | film_names: List[str] = Field(description="list of names of films they starred in") 12 | 13 | actor_query = "Generate the filmography for a random actor." 14 | 15 | parser = PydanticOutputParser(pydantic_object=Actor) 16 | 17 | # assuming we got the misformmated response i.e single quote instead of double quote 18 | misformatted = "{'name': 'Tom Hanks', 'film_names': ['Forrest Gump']}" 19 | try: 20 | parser.parse(misformatted) 21 | """ 22 | Traceback (most recent call last): 23 | File "/Users/seungjoonlee/git/learn-langchain/venv/lib/python3.10/site-packages/langchain/output_parsers/pydantic.py", line 27, in parse 24 | json_object = json.loads(json_str, strict=False) 25 | File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/json/__init__.py", line 359, in loads 26 | return cls(**kw).decode(s) 27 | File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/json/decoder.py", line 337, in decode 28 | obj, end = self.raw_decode(s, idx=_w(s, 0).end()) 29 | File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/json/decoder.py", line 353, in raw_decode 30 | obj, end = self.scan_once(s, idx) 31 | json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1) 32 | 33 | During handling of the above exception, another exception occurred: 34 | 35 | Traceback (most recent call last): 36 | File "/Users/seungjoonlee/git/learn-langchain/output_parsers/auto_fixing_parser.py", line 15, in 37 | print(parser.parse(misformatted)) 38 | File "/Users/seungjoonlee/git/learn-langchain/venv/lib/python3.10/site-packages/langchain/output_parsers/pydantic.py", line 33, in parse 39 | raise OutputParserException(msg, llm_output=text) 40 | langchain.schema.output_parser.OutputParserException: Failed to parse Actor from completion {'name': 'Tom Hanks', 'film_names': ['Forrest Gump']}. Got: Expecting property name enclosed in double quotes: line 1 column 2 (char 1) 41 | """ 42 | except Exception as e: 43 | print("Got an error!!!!! trying with OutputFixingParser...") 44 | from langchain.output_parsers import OutputFixingParser 45 | 46 | new_parser = OutputFixingParser.from_llm(parser=parser, llm=ChatOpenAI()) 47 | print(new_parser.parse(misformatted)) 48 | """ 49 | name='Tom Hanks' film_names=['Forrest Gump'] 50 | """ 51 | -------------------------------------------------------------------------------- /output_parsers/datetime_parser.py: -------------------------------------------------------------------------------- 1 | # Note as of 02/27/2024 2 | # before you start you need to install the following 3 | # pip install langchain==0.1.9 langchain-openai==0.0.8 4 | from langchain.chains import LLMChain 5 | from langchain_openai import OpenAI 6 | from langchain.output_parsers import DatetimeOutputParser 7 | from langchain.prompts import PromptTemplate 8 | 9 | output_parser = DatetimeOutputParser() 10 | # print(output_parser.get_format_instructions()) 11 | """ 12 | Write a datetime string that matches the following pattern: '%Y-%m-%dT%H:%M:%S.%fZ'. 13 | 14 | Examples: 0845-05-01T23:51:26.179657Z, 0953-10-03T20:38:54.550930Z, 0188-02-08T08:37:30.449473Z 15 | 16 | Return ONLY this string, no other words! 17 | """ 18 | template = """Answer the users question: 19 | 20 | {question} 21 | 22 | {format_instructions}""" 23 | prompt = PromptTemplate.from_template( 24 | template, 25 | partial_variables={"format_instructions": output_parser.get_format_instructions()}, 26 | ) 27 | chain = LLMChain(prompt=prompt, llm=OpenAI()) 28 | output_dict = chain.invoke("around when was bitcoin founded?") 29 | print(output_parser.parse(output_dict['text'])) 30 | """ 31 | 2009-01-03 18:15:05 32 | """ -------------------------------------------------------------------------------- /output_parsers/enum_parser.py: -------------------------------------------------------------------------------- 1 | from langchain.output_parsers.enum import EnumOutputParser 2 | from enum import Enum 3 | 4 | class Colors(Enum): 5 | RED = "red" 6 | GREEN = "green" 7 | BLUE = "blue" 8 | 9 | parser = EnumOutputParser(enum=Colors) 10 | # # Colors.RED 11 | # parser.parse("red") 12 | 13 | # # Can handle spaces 14 | # parser.parse(" green") 15 | 16 | # # And new lines 17 | # parser.parse("blue\n") 18 | 19 | # # And raises errors when appropriate 20 | # parser.parse("yellow") 21 | 22 | # """ 23 | # Traceback (most recent call last): 24 | # File "/Users/seungjoonlee/git/learn-langchain/venv/lib/python3.10/site-packages/langchain/output_parsers/enum.py", line 27, in parse 25 | # return self.enum(response.strip()) 26 | # File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/enum.py", line 385, in __call__ 27 | # return cls.__new__(cls, value) 28 | # File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/enum.py", line 710, in __new__ 29 | # raise ve_exc 30 | # ValueError: 'yellow' is not a valid Colors 31 | 32 | # During handling of the above exception, another exception occurred: 33 | 34 | # Traceback (most recent call last): 35 | # File "/Users/seungjoonlee/git/learn-langchain/output_parsers/enum_parser.py", line 20, in 36 | # parser.parse("yellow") 37 | # File "/Users/seungjoonlee/git/learn-langchain/venv/lib/python3.10/site-packages/langchain/output_parsers/enum.py", line 29, in parse 38 | # raise OutputParserException( 39 | # langchain.schema.output_parser.OutputParserException: Response 'yellow' is not one of the expected values: ['red', 'green', 'blue'] 40 | # """ -------------------------------------------------------------------------------- /output_parsers/list_parser.py: -------------------------------------------------------------------------------- 1 | # Note as of 02/27/2024 2 | # before you start you need to install the following 3 | # pip install langchain==0.1.9 langchain-openai==0.0.8 4 | from langchain.output_parsers import CommaSeparatedListOutputParser 5 | from langchain.prompts import PromptTemplate 6 | from langchain_openai import OpenAI 7 | 8 | output_parser = CommaSeparatedListOutputParser() 9 | 10 | format_instructions = output_parser.get_format_instructions() 11 | """ 12 | Your response should be a list of comma separated values, eg: `foo, bar, baz` 13 | """ 14 | # check what to expect to LLM 15 | prompt = PromptTemplate( 16 | template="List five {subject}.\n{format_instructions}", 17 | input_variables=["subject"], 18 | partial_variables={"format_instructions": format_instructions} 19 | ) 20 | 21 | model = OpenAI(temperature=0) 22 | 23 | _input = prompt.format(subject="ice cream flavors") 24 | output = model.invoke(_input) 25 | 26 | print(output_parser.parse(output)) 27 | # 모델 버전이 바뀐 후로는 28 | # ['1. Chocolate\n2. Vanilla\n3. Strawberry\n4. Mint chocolate chip\n5. Cookies and cream'] 29 | # 으로 아웃풋이 됩니다. 30 | -------------------------------------------------------------------------------- /output_parsers/pydantic_parser_actor.py: -------------------------------------------------------------------------------- 1 | # Note as of 02/27/2024 2 | # before you start you need to install the following 3 | # pip install langchain==0.1.9 langchain-openai==0.0.8 4 | from typing import List 5 | from langchain_openai import OpenAI 6 | from langchain.output_parsers import PydanticOutputParser 7 | from langchain.prompts import PromptTemplate 8 | from langchain.pydantic_v1 import BaseModel, Field 9 | 10 | model_name = "gpt-3.5-turbo-instruct" 11 | temperature = 0.0 12 | model = OpenAI(model_name=model_name, temperature=temperature) 13 | 14 | # Here's another example, but with a compound typed field. 15 | class Actor(BaseModel): 16 | name: str = Field(description="name of an actor") 17 | film_names: List[str] = Field(description="list of names of films they starred in") 18 | 19 | 20 | actor_query = "Generate the filmography for a random actor." 21 | 22 | parser = PydanticOutputParser(pydantic_object=Actor) 23 | 24 | print(parser.get_format_instructions()) 25 | """ 26 | The output should be formatted as a JSON instance that conforms to the JSON schema below. 27 | 28 | As an example, for the schema {"properties": {"foo": {"title": "Foo", "description": "a list of strings", "type": "array", "items": {"type": "string"}}}, "required": ["foo"]} 29 | the object {"foo": ["bar", "baz"]} is a well-formatted instance of the schema. The object {"properties": {"foo": ["bar", "baz"]}} is not well-formatted. 30 | 31 | Here is the output schema: 32 | ``` 33 | {"properties": {"name": {"title": "Name", "description": "name of an actor", "type": "string"}, "film_names": {"title": "Film Names", "description": "list of names of films they starred in", "type": "array", "items": {"type": "string"}}}, "required": ["name", "film_names"]} 34 | ``` 35 | """ 36 | prompt = PromptTemplate( 37 | template="Answer the user query.\n{format_instructions}\n{query}\n", 38 | input_variables=["query"], 39 | partial_variables={"format_instructions": parser.get_format_instructions()}, 40 | ) 41 | 42 | _input = prompt.format_prompt(query=actor_query) 43 | 44 | output = model.invoke(_input.to_string()) 45 | """ 46 | Output: 47 | {"name": "Tom Hanks", "film_names": ["Forrest Gump", "Saving Private Ryan", "Cast Away", "The Green Mile", "Apollo 13", "Toy Story", "Toy Story 2", "Toy Story 3", "The Da Vinci Code", "Catch Me If You Can"]} 48 | name='Tom Hanks' film_names=['Forrest Gump', 'Saving Private Ryan', 'Cast Away', 'The Green Mile', 'Apollo 13', 'Toy Story', 'Toy Story 2', 'Toy Story 3', 'The Da Vinci Code', 'Catch Me If You Can'] 49 | """ 50 | m = parser.parse(output) 51 | print(m) 52 | """ 53 | name='Tom Hanks' film_names=['Forrest Gump', 'Saving Private Ryan', 'The Green Mile', 'Cast Away', 'Toy Story'] 54 | """ 55 | print(m.name) -------------------------------------------------------------------------------- /output_parsers/pydantic_parser_joke.py: -------------------------------------------------------------------------------- 1 | # Note as of 02/27/2024 2 | # before you start you need to install the following 3 | # pip install langchain==0.1.9 langchain-openai==0.0.8 4 | from typing import List 5 | from langchain_openai import OpenAI 6 | from langchain.output_parsers import PydanticOutputParser 7 | from langchain.prompts import PromptTemplate 8 | from langchain.pydantic_v1 import BaseModel, Field, validator 9 | 10 | model_name = "gpt-3.5-turbo-instruct" 11 | temperature = 0.0 12 | model = OpenAI(model_name=model_name, temperature=temperature) 13 | 14 | # Define your desired data structure. 15 | class Joke(BaseModel): 16 | setup: str = Field(description="question to set up a joke") 17 | punchline: str = Field(description="answer to resolve the joke") 18 | 19 | # You can add custom validation logic easily with Pydantic. 20 | @validator("setup") 21 | def question_ends_with_question_mark(cls, field): 22 | if field[-1] != "?": 23 | raise ValueError("Badly formed question!") 24 | return field 25 | 26 | 27 | # And a query intented to prompt a language model to populate the data structure. 28 | joke_query = "Tell me a joke." 29 | 30 | # Set up a parser + inject instructions into the prompt template. 31 | parser = PydanticOutputParser(pydantic_object=Joke) 32 | 33 | # print(parser.get_format_instructions()) 34 | """ 35 | The output should be formatted as a JSON instance that conforms to the JSON schema below. 36 | 37 | As an example, for the schema {"properties": {"foo": {"title": "Foo", "description": "a list of strings", "type": "array", "items": {"type": "string"}}}, "required": ["foo"]} 38 | the object {"foo": ["bar", "baz"]} is a well-formatted instance of the schema. The object {"properties": {"foo": ["bar", "baz"]}} is not well-formatted. 39 | 40 | Here is the output schema: 41 | ``` 42 | {"properties": {"setup": {"title": "Setup", "description": "question to set up a joke", "type": "string"}, "punchline": {"title": "Punchline", "description": "answer to resolve the joke", "type": "string"}}, "required": ["setup", "punchline"]} 43 | ``` 44 | """ 45 | 46 | prompt = PromptTemplate( 47 | template="Answer the user query.\n{format_instructions}\n{query}\n", 48 | input_variables=["query"], 49 | partial_variables={"format_instructions": parser.get_format_instructions()}, 50 | ) 51 | 52 | _input = prompt.format_prompt(query=joke_query) 53 | 54 | output = model.invoke(_input.to_string()) 55 | print(output) 56 | output = '{"setup": "Why did the chicken cross the road?", "punchline": "To get to the other side!"}' 57 | """ 58 | {"setup": "Why did the chicken cross the road?", "punchline": "To get to the other side!"} 59 | """ 60 | m = parser.parse(output) 61 | print(m) 62 | """ 63 | setup='Why did the chicken cross the road?' punchline='To get to the other side! 64 | """ 65 | print(m.setup) 66 | """ 67 | Why did the chicken cross the road? 68 | """ 69 | -------------------------------------------------------------------------------- /output_parsers/structured_output_parser.py: -------------------------------------------------------------------------------- 1 | from langchain.output_parsers import StructuredOutputParser, ResponseSchema 2 | from langchain.prompts import PromptTemplate, ChatPromptTemplate, HumanMessagePromptTemplate 3 | from langchain.llms import OpenAI 4 | from langchain.chat_models import ChatOpenAI 5 | 6 | response_schemas = [ 7 | ResponseSchema(name="answer", description="answer to the user's question"), 8 | ResponseSchema(name="source", description="source used to answer the user's question, should be a website.") 9 | ] 10 | output_parser = StructuredOutputParser.from_response_schemas(response_schemas) 11 | 12 | format_instructions = output_parser.get_format_instructions() 13 | # The output should be a markdown code snippet formatted in the following schema, including the leading and trailing "```json" and "```": 14 | """ 15 | ```json 16 | { 17 | "answer": string // answer to the user's question 18 | "source": string // source used to answer the user's question, should be a website. 19 | } 20 | ``` 21 | """ 22 | 23 | prompt = PromptTemplate( 24 | template="answer the users question as best as possible.\n{format_instructions}\n{question}", 25 | input_variables=["question"], 26 | partial_variables={"format_instructions": format_instructions} 27 | ) 28 | 29 | model = OpenAI(temperature=0) 30 | 31 | _input = prompt.format_prompt(question="what's the capital of france?") 32 | output = model(_input.to_string()) 33 | """ 34 | ```json 35 | { 36 | "answer": "Paris", 37 | "source": "https://www.worldatlas.com/articles/what-is-the-capital-of-france.html" 38 | } 39 | ``` 40 | """ 41 | 42 | chat_model = ChatOpenAI(temperature=0) 43 | 44 | prompt = ChatPromptTemplate( 45 | messages=[ 46 | HumanMessagePromptTemplate.from_template("answer the users question as best as possible.\n{format_instructions}\n{question}") 47 | ], 48 | input_variables=["question"], 49 | partial_variables={"format_instructions": format_instructions} 50 | ) 51 | 52 | _input = prompt.format_prompt(question="what's the capital of france?") 53 | output = chat_model(_input.to_messages()) 54 | output_parser.parse(output.content) 55 | # {'answer': 'The capital of France is Paris.', 'source': 'https://en.wikipedia.org/wiki/Paris'} 56 | -------------------------------------------------------------------------------- /prompt_template.py: -------------------------------------------------------------------------------- 1 | # Note as of 02/27/2024 2 | # before you start you need to install the following 3 | # pip install langchain==0.1.9 langchain-openai==0.0.8 4 | from langchain.prompts import PromptTemplate 5 | 6 | prompt = PromptTemplate.from_template("What is a good name for a company that makes {product}?") 7 | prompt_output = prompt.format(product="colorful socks") 8 | print(prompt_output) 9 | # What is a good name for a company that makes colorful socks? 10 | 11 | from langchain.prompts.chat import ChatPromptTemplate 12 | 13 | template = "You are a helpful assistant that translates {input_language} to {output_language}." 14 | human_template = "{text}" 15 | 16 | chat_prompt = ChatPromptTemplate.from_messages([ 17 | ("system", template), # role 18 | ("human", human_template), # content 19 | ]) 20 | 21 | chat_prompt_output = chat_prompt.format_messages( 22 | input_language="English", 23 | output_language="French", 24 | text="I love programming.") 25 | print(chat_prompt_output) 26 | # [SystemMessage(content='You are a helpful assistant that translates English to French.'), 27 | # HumanMessage(content='I love programming.')] 28 | 29 | 30 | from langchain_openai import ChatOpenAI 31 | 32 | chat_model = ChatOpenAI() 33 | print(chat_model.invoke(chat_prompt_output)) 34 | -------------------------------------------------------------------------------- /pydantic_example/quickstart.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from typing import Tuple 3 | from pydantic import BaseModel 4 | 5 | 6 | class Delivery(BaseModel): 7 | timestamp: datetime 8 | dimensions: Tuple[int, int] 9 | 10 | 11 | m = Delivery(timestamp='2020-01-02T03:04:05Z', dimensions=['10', '20']) # type: ignore 12 | print(repr(m.timestamp)) 13 | #> datetime.datetime(2020, 1, 2, 3, 4, 5, tzinfo=TzInfo(UTC)) 14 | print(m.dimensions) 15 | #> (10, 20) 16 | -------------------------------------------------------------------------------- /pydantic_example/validation_fail.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from pydantic import BaseModel, PositiveInt 3 | from pydantic import ValidationError 4 | 5 | 6 | class User(BaseModel): 7 | id: int 8 | name: str = 'John Doe' 9 | signup_ts: datetime | None 10 | tastes: dict[str, PositiveInt] 11 | 12 | external_data = {'id': 'not an int', 'tastes': {}} 13 | 14 | try: 15 | User(**external_data) 16 | except ValidationError as e: 17 | print(e.errors()) 18 | """ 19 | [ 20 | { 21 | 'type': 'int_parsing', 22 | 'loc': ('id',), 23 | 'msg': 'Input should be a valid integer, unable to parse string as an integer', 24 | 'input': 'not an int', 25 | 'url': 'https://errors.pydantic.dev/2/v/int_parsing', 26 | }, 27 | { 28 | 'type': 'missing', 29 | 'loc': ('signup_ts',), 30 | 'msg': 'Field required', 31 | 'input': {'id': 'not an int', 'tastes': {}}, 32 | 'url': 'https://errors.pydantic.dev/2/v/missing', 33 | }, 34 | ] 35 | """ 36 | -------------------------------------------------------------------------------- /pydantic_example/validation_successful.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from pydantic import BaseModel, PositiveInt 3 | 4 | class User(BaseModel): 5 | id: int 6 | name: str = 'John Doe' 7 | signup_ts: datetime | None 8 | tastes: dict[str, PositiveInt] 9 | 10 | external_data = { 11 | 'id': 123, 12 | 'signup_ts': '2019-06-01 12:22', 13 | 'tastes': { 14 | 'wine': 9, 15 | b'cheese': 7, 16 | 'cabbage': '1', 17 | }, 18 | } 19 | 20 | user = User(**external_data) 21 | 22 | print(user.id) 23 | #> 123 24 | print(user.model_dump()) 25 | """ 26 | { 27 | 'id': 123, 28 | 'name': 'John Doe', 29 | 'signup_ts': datetime.datetime(2019, 6, 1, 12, 22), 30 | 'tastes': {'wine': 9, 'cheese': 7, 'cabbage': 1}, 31 | } 32 | """ 33 | -------------------------------------------------------------------------------- /quickstart.py: -------------------------------------------------------------------------------- 1 | # Note as of 02/27/2024 2 | # before you start you need to install the following 3 | # pip install langchain==0.1.9 langchain-openai==0.0.8 4 | from typing import List 5 | from langchain_openai import ChatOpenAI 6 | from langchain.prompts import ChatPromptTemplate 7 | from langchain.schema import BaseOutputParser 8 | 9 | class CommaSeparatedListOutputParser(BaseOutputParser[List[str]]): 10 | """Parse the output of an LLM call to a comma-separated list.""" 11 | def parse(self, text: str) -> List[str]: 12 | """Parse the output of an LLM call.""" 13 | return text.strip().split(", ") 14 | 15 | template = """You are a helpful assistant who generates comma separated lists. 16 | A user will pass in a category, and you should generate 5 objects in that category in a comma separated list. 17 | ONLY return a comma separated list, and nothing more.""" 18 | human_template = "{text}" 19 | 20 | chat_prompt = ChatPromptTemplate.from_messages([ 21 | ("system", template), 22 | ("human", human_template), 23 | ]) 24 | 25 | # LangChain Expression Language(LCEL) 26 | chain = chat_prompt | ChatOpenAI() | CommaSeparatedListOutputParser() 27 | print(chain.invoke({"text": "colors"})) 28 | # ['red', 'blue', 'green', 'yellow', 'purple'] 29 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | langchain==0.1.9 2 | langchain-community==0.0.24 3 | langchain-core==0.1.27 4 | langchain-openai==0.0.8 5 | -------------------------------------------------------------------------------- /retriever/context_compression.py: -------------------------------------------------------------------------------- 1 | # Note as of 02/27/2024 2 | # before you start you need to install the following 3 | # pip install langchain==0.1.9 langchain-openai==0.0.8 4 | from langchain_community.document_loaders import TextLoader 5 | from langchain_openai import OpenAI 6 | from langchain.retrievers import ContextualCompressionRetriever 7 | from langchain_openai import OpenAIEmbeddings 8 | from langchain.text_splitter import CharacterTextSplitter 9 | from langchain.retrievers.document_compressors import LLMChainExtractor 10 | from langchain_community.vectorstores.chroma import Chroma 11 | 12 | data = TextLoader('./state_of_the_union.txt').load() 13 | 14 | # Split 15 | text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0) 16 | splits = text_splitter.split_documents(data) 17 | 18 | # VectorDB 19 | embedding = OpenAIEmbeddings() 20 | vectordb = Chroma.from_documents(documents=splits, embedding=embedding) 21 | 22 | # Helper function for printing docs 23 | def pretty_print_docs(docs): 24 | print(f"\n{'-' * 100}\n".join([f"Document {i+1}:\n\n" + d.page_content for i, d in enumerate(docs)])) 25 | 26 | retriever = vectordb.as_retriever() 27 | docs = retriever.get_relevant_documents("What did the president say about Ketanji Brown Jackson") 28 | # pretty_print_docs(docs) 29 | """ 30 | Document 1: 31 | 32 | Tonight. I call on the Senate to: Pass the Freedom to Vote Act. Pass the John Lewis Voting Rights Act. And while you’re at it, pass the Disclose Act so Americans can know who is funding our elections. 33 | 34 | Tonight, I’d like to honor someone who has dedicated his life to serve this country: Justice Stephen Breyer—an Army veteran, Constitutional scholar, and retiring Justice of the United States Supreme Court. Justice Breyer, thank you for your service. 35 | 36 | One of the most serious constitutional responsibilities a President has is nominating someone to serve on the United States Supreme Court. 37 | 38 | And I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson. One of our nation’s top legal minds, who will continue Justice Breyer’s legacy of excellence. 39 | ... 40 | """ 41 | 42 | llm = OpenAI(temperature=0) 43 | compressor = LLMChainExtractor.from_llm(llm) 44 | compression_retriever = ContextualCompressionRetriever( 45 | base_compressor=compressor, 46 | base_retriever=retriever) 47 | 48 | compressed_docs = compression_retriever.get_relevant_documents( 49 | "What did the president say about Ketanji Brown Jackson") 50 | pretty_print_docs(compressed_docs) 51 | """ 52 | Document 1: 53 | 54 | "One of the most serious constitutional responsibilities a President has is nominating someone to serve on the United States Supreme Court. 55 | 56 | And I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson. One of our nation’s top legal minds, who will continue Justice Breyer’s legacy of excellence." 57 | """ 58 | -------------------------------------------------------------------------------- /retriever/multi_query_retriever.py: -------------------------------------------------------------------------------- 1 | # Note as of 02/27/2024 2 | # before you start you need to install the following 3 | # pip install langchain==0.1.9 langchain-openai==0.0.8 4 | 5 | # Build a sample vectorDB 6 | from langchain_community.document_loaders import WebBaseLoader 7 | from langchain_openai import OpenAIEmbeddings 8 | from langchain.text_splitter import RecursiveCharacterTextSplitter 9 | from langchain_community.vectorstores.chroma import Chroma 10 | 11 | # Load blog post 12 | loader = WebBaseLoader("https://lilianweng.github.io/posts/2023-06-23-agent/") 13 | data = loader.load() 14 | 15 | # Split 16 | text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=0) 17 | splits = text_splitter.split_documents(data) 18 | 19 | # VectorDB 20 | embedding = OpenAIEmbeddings() 21 | vectordb = Chroma.from_documents(documents=splits, embedding=embedding) 22 | 23 | from langchain_openai import ChatOpenAI 24 | from langchain.retrievers.multi_query import MultiQueryRetriever 25 | 26 | question = "What are the approaches to Task Decomposition?" 27 | llm = ChatOpenAI(temperature=0) 28 | retriever_from_llm = MultiQueryRetriever.from_llm( 29 | retriever=vectordb.as_retriever(), llm=llm) 30 | 31 | # Set logging for the queries 32 | import logging 33 | 34 | logging.basicConfig() 35 | logging.getLogger("langchain.retrievers.multi_query").setLevel(logging.INFO) 36 | 37 | unique_docs = retriever_from_llm.get_relevant_documents(query=question) 38 | """ 39 | INFO:langchain.retrievers.multi_query:Generated queries: [ 40 | '1. How can Task Decomposition be achieved through different methods?', 41 | '2. What strategies are commonly used for breaking down tasks in Task Decomposition?', 42 | '3. What are the various techniques employed in Task Decomposition to simplify complex tasks?'] 43 | """ 44 | print(len(unique_docs)) 45 | """ 46 | 6 47 | """ -------------------------------------------------------------------------------- /serialization/example_json.py: -------------------------------------------------------------------------------- 1 | # All prompts are loaded through the `load_prompt` function. 2 | from langchain.prompts import load_prompt, PromptTemplate 3 | 4 | 5 | template = "Tell me a {adjective} joke about {content}." 6 | prompt = PromptTemplate(template=template, input_variables=["adjective", "content"]) 7 | # input_variables=['adjective', 'content'] template='Tell me a {adjective} joke about {content}.' 8 | 9 | # save the prompt in JSON 10 | prompt.save("simple_prompt.json") 11 | 12 | # load the prompt from JSON file 13 | prompt = load_prompt("simple_prompt.json") 14 | print(prompt.format(adjective="funny", content="chickens")) -------------------------------------------------------------------------------- /serialization/example_yaml.py: -------------------------------------------------------------------------------- 1 | # All prompts are loaded through the `load_prompt` function. 2 | from langchain.prompts import load_prompt, PromptTemplate 3 | 4 | 5 | template = "Tell me a {adjective} joke about {content}." 6 | prompt = PromptTemplate(template=template, input_variables=["adjective", "content"]) 7 | # input_variables=['adjective', 'content'] template='Tell me a {adjective} joke about {content}.' 8 | 9 | # save the prompt in YAML 10 | prompt.save("simple_prompt.yaml") 11 | 12 | # load the prompt from YAML file 13 | prompt = load_prompt("simple_prompt.yaml") 14 | print(prompt.format(adjective="funny", content="chickens")) 15 | -------------------------------------------------------------------------------- /serialization/simple_prompt.json: -------------------------------------------------------------------------------- 1 | { 2 | "input_variables": [ 3 | "adjective", 4 | "content" 5 | ], 6 | "input_types": {}, 7 | "output_parser": null, 8 | "partial_variables": {}, 9 | "template": "Tell me a {adjective} joke about {content}.", 10 | "template_format": "f-string", 11 | "validate_template": false, 12 | "_type": "prompt" 13 | } -------------------------------------------------------------------------------- /serialization/simple_prompt.yaml: -------------------------------------------------------------------------------- 1 | _type: prompt 2 | input_types: {} 3 | input_variables: 4 | - adjective 5 | - content 6 | output_parser: null 7 | partial_variables: {} 8 | template: Tell me a {adjective} joke about {content}. 9 | template_format: f-string 10 | validate_template: false 11 | -------------------------------------------------------------------------------- /serve.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from typing import List 3 | 4 | from fastapi import FastAPI 5 | from langchain.prompts import ChatPromptTemplate 6 | from langchain.chat_models import ChatOpenAI 7 | from langchain.schema import BaseOutputParser 8 | from langserve import add_routes 9 | 10 | # 1. Chain definition 11 | 12 | class CommaSeparatedListOutputParser(BaseOutputParser[List[str]]): 13 | """Parse the output of an LLM call to a comma-separated list.""" 14 | 15 | 16 | def parse(self, text: str) -> List[str]: 17 | """Parse the output of an LLM call.""" 18 | return text.strip().split(", ") 19 | 20 | template = """You are a helpful assistant who generates comma separated lists. 21 | A user will pass in a category, and you should generate 5 objects in that category in a comma separated list. 22 | ONLY return a comma separated list, and nothing more.""" 23 | human_template = "{text}" 24 | 25 | chat_prompt = ChatPromptTemplate.from_messages([ 26 | ("system", template), 27 | ("human", human_template), 28 | ]) 29 | category_chain = chat_prompt | ChatOpenAI() | CommaSeparatedListOutputParser() 30 | 31 | # 2. App definition 32 | app = FastAPI( 33 | title="LangChain Server", 34 | version="1.0", 35 | description="A simple api server using Langchain's Runnable interfaces", 36 | ) 37 | 38 | # 3. Adding chain route 39 | add_routes( 40 | app, 41 | category_chain, 42 | path="/category_chain", 43 | ) 44 | 45 | if __name__ == "__main__": 46 | import uvicorn 47 | 48 | uvicorn.run(app, host="localhost", port=8000) -------------------------------------------------------------------------------- /simple_prompt.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": null, 3 | "input_variables": [ 4 | "adjective", 5 | "content" 6 | ], 7 | "input_types": {}, 8 | "output_parser": null, 9 | "partial_variables": {}, 10 | "metadata": null, 11 | "tags": null, 12 | "template": "Tell me a {adjective} joke about {content}.", 13 | "template_format": "f-string", 14 | "validate_template": false, 15 | "_type": "prompt" 16 | } -------------------------------------------------------------------------------- /simple_prompt.yaml: -------------------------------------------------------------------------------- 1 | _type: prompt 2 | input_types: {} 3 | input_variables: 4 | - adjective 5 | - content 6 | metadata: null 7 | name: null 8 | output_parser: null 9 | partial_variables: {} 10 | tags: null 11 | template: Tell me a {adjective} joke about {content}. 12 | template_format: f-string 13 | validate_template: false 14 | -------------------------------------------------------------------------------- /text_embedding/csv_sample.csv: -------------------------------------------------------------------------------- 1 | name,age,country 2 | Neville Hardy,56,Niue 3 | Dacia Cohen,74,Falkland Islands (Malvinas) 4 | Kathey Daniel,10,Slovenia 5 | Mallie Welch,12,Equatorial Guinea 6 | Katia Bryant,14,Ghana 7 | Laurice Robertson,53,Saudi Arabia 8 | Minh Barrett,27,French Southern Territories 9 | Latashia Perez,52,Finland 10 | Elvina Ross,68,New Zealand -------------------------------------------------------------------------------- /text_embedding/text_embedding.py: -------------------------------------------------------------------------------- 1 | # Note as of 02/27/2024 2 | # before you start you need to install the following 3 | # pip install langchain==0.1.9 langchain-openai==0.0.8 4 | from langchain_openai import OpenAIEmbeddings 5 | 6 | # requires `pip install openai` 7 | embeddings_model = OpenAIEmbeddings() 8 | 9 | # Embed list of texts 10 | embeddings = embeddings_model.embed_documents( 11 | [ 12 | "Hi there!", 13 | "Oh, hello!", 14 | "What's your name?", 15 | "My friends call me World", 16 | "Hello World!" 17 | ] 18 | ) 19 | print(len(embeddings)) 20 | """ 21 | 5 22 | """ 23 | 24 | # length of 1536 25 | print(len(embeddings[0])) 26 | print(len(embeddings[1])) 27 | """ 28 | [... 0.022085255981691917, 0.025720015991018582, 0.008734743689027272, -0.006709843137048811, -0.022764415491192392, -0.00257671800269355, 0.010677894145694868, 0.0001446357869742665, -0.02568228625240111, -0.010438930752548039, -0.002831402818756228, -0.012992066737283132, 0.0015925658455746433, -0.021569597594135712, 0.011853846242120273, 0.015771589535625893, 0.006238204640524732, 0.02429881221167677, 0.014086268272736402, -0.024575506274763608, -0.021129402409104603, 0.007653119664435697, 0.006021250727232698, -0.02475158583889211, -0.012853719705739713, 0.018048030525951612, -0.0018441062839218978, -0.008445472115078739, -0.006885921304193508, 0.00240850043059146, 0.00827568270336489, -0.008030431020448483, -0.004181860777053302, 0.0010344603379206113, 0.007552503768493557, 0.01879007479579295, 0.008451761336170855, -0.014249769394680672, -0.03264995904888929, 0.004728961544779937, -0.0020343339179553677, -0.024927663540375542, -0.006565207350074544, -0.014765427782236877 ...] 29 | """ 30 | 31 | # Use Document Loader 32 | from langchain_community.document_loaders import CSVLoader 33 | loader = CSVLoader(file_path='./csv_sample.csv') 34 | data = loader.load() 35 | print(data) 36 | embeddings = embeddings_model.embed_documents( 37 | [ 38 | text.page_content for text in data 39 | ] 40 | ) 41 | print(len(embeddings)) 42 | """ 43 | 9 44 | """ 45 | 46 | # Embed single query 47 | # Embed a single piece of text for the purpose of comparing to other embedded pieces of texts. 48 | embedded_query = embeddings_model.embed_query("What was the name mentioned in the conversation?") 49 | print(embedded_query[:5]) 50 | """ 51 | [0.005354681365594307, -0.0005715346531097274, 0.03887590993433691, -0.0029596003572924623, -0.00896628532870428] 52 | """ -------------------------------------------------------------------------------- /vector_stores/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimz119/learn-langchain/f0c2604aab7cab7f2c4d44d7bb484fd0dc2463bc/vector_stores/.DS_Store -------------------------------------------------------------------------------- /vector_stores/fortune_500_db/3ac7dac4-a1b8-4432-9306-3bec6614ef1c/data_level0.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimz119/learn-langchain/f0c2604aab7cab7f2c4d44d7bb484fd0dc2463bc/vector_stores/fortune_500_db/3ac7dac4-a1b8-4432-9306-3bec6614ef1c/data_level0.bin -------------------------------------------------------------------------------- /vector_stores/fortune_500_db/3ac7dac4-a1b8-4432-9306-3bec6614ef1c/header.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimz119/learn-langchain/f0c2604aab7cab7f2c4d44d7bb484fd0dc2463bc/vector_stores/fortune_500_db/3ac7dac4-a1b8-4432-9306-3bec6614ef1c/header.bin -------------------------------------------------------------------------------- /vector_stores/fortune_500_db/3ac7dac4-a1b8-4432-9306-3bec6614ef1c/index_metadata.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimz119/learn-langchain/f0c2604aab7cab7f2c4d44d7bb484fd0dc2463bc/vector_stores/fortune_500_db/3ac7dac4-a1b8-4432-9306-3bec6614ef1c/index_metadata.pickle -------------------------------------------------------------------------------- /vector_stores/fortune_500_db/3ac7dac4-a1b8-4432-9306-3bec6614ef1c/length.bin: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vector_stores/fortune_500_db/3ac7dac4-a1b8-4432-9306-3bec6614ef1c/link_lists.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimz119/learn-langchain/f0c2604aab7cab7f2c4d44d7bb484fd0dc2463bc/vector_stores/fortune_500_db/3ac7dac4-a1b8-4432-9306-3bec6614ef1c/link_lists.bin -------------------------------------------------------------------------------- /vector_stores/fortune_500_db/chroma.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimz119/learn-langchain/f0c2604aab7cab7f2c4d44d7bb484fd0dc2463bc/vector_stores/fortune_500_db/chroma.sqlite3 -------------------------------------------------------------------------------- /vector_stores/quickstart.py: -------------------------------------------------------------------------------- 1 | # Note as of 02/27/2024 2 | # before you start you need to install the following 3 | # pip install langchain==0.1.9 langchain-openai==0.0.8 4 | from langchain_community.document_loaders import CSVLoader 5 | from langchain_openai import OpenAIEmbeddings 6 | from langchain.text_splitter import CharacterTextSplitter 7 | from langchain_community.vectorstores.chroma import Chroma 8 | 9 | # requires `pip install chromadb` 10 | loader = CSVLoader(file_path='./fortune_500_2020.csv') 11 | raw_documents = loader.load() 12 | 13 | text_splitter = CharacterTextSplitter(chunk_size=200, chunk_overlap=0) 14 | documents = text_splitter.split_documents(raw_documents) 15 | openai_embedding = OpenAIEmbeddings() 16 | db = Chroma.from_documents(documents, openai_embedding, persist_directory="./fortune_500_db") 17 | 18 | # save to disk 19 | db.persist() 20 | 21 | db_conn = Chroma(persist_directory="./fortune_500_db", embedding_function=openai_embedding) 22 | query = "What is JPMorgan Revenue?" 23 | docs = db_conn.similarity_search(query) 24 | print(docs) 25 | print(docs[0].page_content) 26 | 27 | # # retriever 28 | # db_conn = Chroma(persist_directory="./fortune_500_db", embedding_function=openai_embedding) 29 | # retriever = db_conn.as_retriever() 30 | # result = retriever.get_relevant_documents('walmart') 31 | # print(result[0].page_content) 32 | # """ 33 | # rank: 1 34 | # company: Walmart 35 | # no_of_employees: 2,200,000.00 36 | # rank_change: None 37 | # revenues: 523,964.00 38 | # revenue_change: 0.02 39 | # profits: 14,881.00 40 | # profit_change: 1.23 41 | # assets: 236,495.00 42 | # market_value: 321,803.30 43 | # """ --------------------------------------------------------------------------------