{ "cells": [ { "cell_type": "markdown", "id": "7087f58f-b14b-497e-9734-7357b1c71ecb", "metadata": {}, "source": [ "### Many of these demos are taken from LangChain's own tutorials, re-arranged in order to show off the abtractions more than the specific tasks" ] }, { "cell_type": "code", "execution_count": 1, "id": "9c323b08-e422-4f9a-9fba-daa861b4c6f6", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Note: you may need to restart the kernel to use updated packages.\n" ] } ], "source": [ "pip install -qU pip" ] }, { "cell_type": "code", "execution_count": 2, "id": "79c9a955-5c7c-4a75-aed1-8701cae2acc9", "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Note: you may need to restart the kernel to use updated packages.\n" ] } ], "source": [ "pip install -qU langchain pydantic" ] }, { "cell_type": "code", "execution_count": 3, "id": "04bcaa88-d12a-4cde-94a9-78968c9852e8", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Pydantic: 2.11.7\n", "LangChain: 0.3.26\n" ] } ], "source": [ "import pydantic\n", "import langchain\n", "\n", "print(\"Pydantic:\", pydantic.__version__)\n", "print(\"LangChain:\", langchain.__version__)" ] }, { "cell_type": "code", "execution_count": 4, "id": "aa1488e9-1dd3-4b3a-82d4-e1b3b0392116", "metadata": {}, "outputs": [], "source": [ "# From \"Python Epiphanies\" by Stuart Williams\n", "def dir_no_under(obj):\n", " return [n for n in dir(obj) if not (n.startswith('_') or n.isupper())]" ] }, { "cell_type": "code", "execution_count": 5, "id": "713c4cae-f495-4747-85e3-2cba9cba5534", "metadata": { "scrolled": true }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/langchain/chains/api/base.py:57: LangChainDeprecationWarning: As of langchain-core 0.3.0, LangChain uses pydantic v2 internally. The langchain_core.pydantic_v1 module was a compatibility shim for pydantic v1, and should no longer be used. Please update the code to import from Pydantic directly.\n", "\n", "For example, replace imports like: `from langchain_core.pydantic_v1 import BaseModel`\n", "with: `from pydantic import BaseModel`\n", "or the v1 compatibility namespace if you are working in a code base that has not been fully upgraded to pydantic 2 yet. \tfrom pydantic.v1 import BaseModel\n", "\n", " from langchain_community.utilities.requests import TextRequestsWrapper\n", "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py:918: UserWarning: Mixing V1 models and V2 models (or constructs, like `TypeAdapter`) is not supported. Please upgrade `TextRequestsWrapper` to V2.\n", " warn(\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "1327\n", "['langchain._api', 'langchain._api.deprecation', 'langchain._api.interactive_env', 'langchain._api.module_import', 'langchain._api.path', 'langchain.adapters', 'langchain.adapters.openai', 'langchain.agents', 'langchain.agents.agent', 'langchain.agents.agent_iterator']\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/pkgutil.py:107: LangChainPendingDeprecationWarning: langchain.indexes.prompts will be removed in the future.If you're relying on these prompts, please open an issue on GitHub to explain your use case.\n", " yield from walk_packages(path, info.name+'.', onerror)\n", "/var/folders/xy/r9y8j91j4353ssxws9z9px140000gn/T/ipykernel_80780/3447361753.py:5: LangChainDeprecationWarning: As of langchain-core 0.3.0, LangChain uses pydantic v2 internally. The langchain.pydantic_v1 module was a compatibility shim for pydantic v1, and should no longer be used. Please update the code to import from Pydantic directly.\n", "\n", "For example, replace imports like: `from langchain.pydantic_v1 import BaseModel`\n", "with: `from pydantic import BaseModel`\n", "or the v1 compatibility namespace if you are working in a code base that has not been fully upgraded to pydantic 2 yet. \tfrom pydantic.v1 import BaseModel\n", "\n", " modules = [modname for _, modname, _ in pkgutil.walk_packages(langchain.__path__, prefix=\"langchain.\")]\n" ] } ], "source": [ "import pkgutil\n", "\n", "# Get a list of all the langchain modules\n", "\n", "modules = [modname for _, modname, _ in pkgutil.walk_packages(langchain.__path__, prefix=\"langchain.\")]\n", "print(len(modules))\n", "print(modules[:10])" ] }, { "cell_type": "code", "execution_count": 6, "id": "22526bd3-900d-4d8f-a154-1c60cbec8a9a", "metadata": { "scrolled": true }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py:918: UserWarning: Mixing V1 models and V2 models (or constructs, like `TypeAdapter`) is not supported. Please upgrade `ArxivAPIWrapper` to V2.\n", " warn(\n", "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py:918: UserWarning: Mixing V1 models and V2 models (or constructs, like `TypeAdapter`) is not supported. Please upgrade `BingSearchAPIWrapper` to V2.\n", " warn(\n", "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py:918: UserWarning: Mixing V1 models and V2 models (or constructs, like `TypeAdapter`) is not supported. Please upgrade `DataForSeoAPIWrapper` to V2.\n", " warn(\n", "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py:918: UserWarning: Mixing V1 models and V2 models (or constructs, like `TypeAdapter`) is not supported. Please upgrade `DuckDuckGoSearchAPIWrapper` to V2.\n", " warn(\n", "/var/folders/xy/r9y8j91j4353ssxws9z9px140000gn/T/ipykernel_80780/735884134.py:13: LangChainDeprecationWarning: As of langchain-core 0.3.0, LangChain uses pydantic v2 internally. The langchain.pydantic_v1 module was a compatibility shim for pydantic v1, and should no longer be used. Please update the code to import from Pydantic directly.\n", "\n", "For example, replace imports like: `from langchain.pydantic_v1 import BaseModel`\n", "with: `from pydantic import BaseModel`\n", "or the v1 compatibility namespace if you are working in a code base that has not been fully upgraded to pydantic 2 yet. \tfrom pydantic.v1 import BaseModel\n", "\n", " module = importlib.import_module(mod)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "864\n", "[('langchain.agents.AgentExecutor', ), ('langchain.agents.AgentOutputParser', ), ('langchain.agents.MRKLChain', ), ('langchain.agents.ReActChain', ), ('langchain.agents.SelfAskWithSearchChain', ), ('langchain.agents.Tool', ), ('langchain.agents.agent.AgentExecutor', ), ('langchain.agents.agent.AgentOutputParser', ), ('langchain.agents.agent.BaseLanguageModel', ), ('langchain.agents.agent.BaseOutputParser', )]\n" ] } ], "source": [ "# I said the core thing in langchain is a Runnable\n", "# Now let's find the Runnables within those modules by importing and inspecting each module\n", "import importlib\n", "import langchain.chains\n", "import inspect\n", "from langchain_core.runnables import Runnable\n", "\n", "# These classes cause pydantic errors... Haven't been upgraded?\n", "ignored_classes = ['NextStepOutput', 'RetrieverOutput', 'RETURN_VAL_TYPE']\n", "\n", "def find_runnables_in_module(mod):\n", " runnable_classes = []\n", " module = importlib.import_module(mod)\n", " for name, obj in inspect.getmembers(module, inspect.isclass):\n", " if name in ignored_classes:\n", " continue\n", " if issubclass(obj, Runnable) and obj is not Runnable:\n", " runnable_classes.append((mod + \".\" + name, obj))\n", " return runnable_classes\n", "\n", "all_runnables = []\n", "for mod in modules:\n", " all_runnables.extend(find_runnables_in_module(mod))\n", "\n", "print(len(all_runnables))\n", "print(all_runnables[:10])" ] }, { "cell_type": "code", "execution_count": 1, "id": "5321ec9c-ee8b-4b0b-ad47-7230b4987400", "metadata": { "scrolled": true }, "outputs": [ { "ename": "NameError", "evalue": "name 'all_runnables' is not defined", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", "Cell \u001b[0;32mIn[1], line 2\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;66;03m# Let's try and categorize those runnables, to get a feel for what types of things are Runnable\u001b[39;00m\n\u001b[0;32m----> 2\u001b[0m groups \u001b[38;5;241m=\u001b[39m [name\u001b[38;5;241m.\u001b[39msplit(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m.\u001b[39m\u001b[38;5;124m'\u001b[39m)[\u001b[38;5;241m1\u001b[39m] \u001b[38;5;28;01mfor\u001b[39;00m name, \u001b[38;5;28mcls\u001b[39m \u001b[38;5;129;01min\u001b[39;00m \u001b[43mall_runnables\u001b[49m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124m.base.\u001b[39m\u001b[38;5;124m'\u001b[39m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m name]\n", "\u001b[0;31mNameError\u001b[0m: name 'all_runnables' is not defined" ] } ], "source": [ "# Let's try and categorize those runnables, to get a feel for what types of things are Runnable\n", "groups = [name.split('.')[1] for name, cls in all_runnables if '.base.']" ] }, { "cell_type": "code", "execution_count": 8, "id": "b9c9dccf-ff0a-4de3-9498-59e1bb97f916", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[('chains', 339),\n", " ('agents', 179),\n", " ('evaluation', 107),\n", " ('output_parsers', 68),\n", " ('retrievers', 55),\n", " ('schema', 45),\n", " ('prompts', 20),\n", " ('memory', 10),\n", " ('smith', 8),\n", " ('indexes', 6),\n", " ('runnables', 6),\n", " ('tools', 6),\n", " ('chat_models', 4),\n", " ('llms', 4),\n", " ('model_laboratory', 4),\n", " ('base_language', 1),\n", " ('hub', 1),\n", " ('vectorstores', 1)]" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from collections import Counter\n", "counts = Counter(groups)\n", "sorted_counts = counts.most_common()\n", "sorted_counts" ] }, { "cell_type": "markdown", "id": "57587222-192b-4ff4-83b5-9c796af232a8", "metadata": {}, "source": [ "# LLMs" ] }, { "cell_type": "code", "execution_count": 9, "id": "2d2a7000-90e2-4fe2-9821-5953bccb18c4", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['langchain.llms.BaseLLM',\n", " 'langchain.llms.base.BaseLLM',\n", " 'langchain.llms.base.BaseLanguageModel',\n", " 'langchain.llms.base.LLM']" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[name for name, cls in all_runnables if name.startswith('langchain.llms')]" ] }, { "cell_type": "code", "execution_count": null, "id": "5dcb2888-6fc8-4f37-9d57-8609e5257ab4", "metadata": {}, "outputs": [], "source": [ "# These are clearly just the base classes for LLMs where is the list of actual LLMs?" ] }, { "cell_type": "code", "execution_count": null, "id": "61afa26b-c78e-4255-bc8a-840a32c7c30d", "metadata": {}, "outputs": [], "source": [ "# unfortunately they are all in different packages, so we have to consult the documentation." ] }, { "cell_type": "markdown", "id": "c7ea6be8-b8f8-41b8-8575-be041ec96081", "metadata": {}, "source": [ "[LLM integrations](https://python.langchain.com/docs/integrations/llms)" ] }, { "cell_type": "code", "execution_count": 10, "id": "46d3515a-a596-4eef-ba99-9b332c35d8e4", "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Note: you may need to restart the kernel to use updated packages.\n" ] } ], "source": [ "pip install -qU langchain-openai" ] }, { "cell_type": "code", "execution_count": null, "id": "8be0eeb0-3ba8-4f40-a941-2c02a15c24af", "metadata": {}, "outputs": [], "source": [ "# NOTE: you will need to add your own key" ] }, { "cell_type": "code", "execution_count": 11, "id": "479002a4-4da6-4396-addb-bca3a8cf4993", "metadata": {}, "outputs": [], "source": [ "import os\n", "os.environ[\"OPENAI_API_KEY\"] = \"\"" ] }, { "cell_type": "code", "execution_count": 12, "id": "106981e6-151c-4c91-94d6-bd3f1e1ed8b9", "metadata": {}, "outputs": [], "source": [ "from langchain_openai import OpenAI" ] }, { "cell_type": "code", "execution_count": 13, "id": "332ff031-d20d-43bd-a164-130877c82bca", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(langchain_openai.llms.base.OpenAI,\n", " langchain_openai.llms.base.BaseOpenAI,\n", " langchain_core.language_models.llms.BaseLLM,\n", " langchain_core.language_models.base.BaseLanguageModel[str],\n", " langchain_core.language_models.base.BaseLanguageModel,\n", " langchain_core.runnables.base.RunnableSerializable[Union[PromptValue, str, Sequence[Union[BaseMessage, list[str], tuple[str, str], str, dict[str, Any]]]], ~LanguageModelOutputVar],\n", " langchain_core.runnables.base.RunnableSerializable,\n", " langchain_core.load.serializable.Serializable,\n", " pydantic.main.BaseModel,\n", " langchain_core.runnables.base.Runnable,\n", " typing.Generic,\n", " abc.ABC,\n", " object)" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "OpenAI.__mro__" ] }, { "cell_type": "markdown", "id": "0037984b-42d5-43ed-aca9-5f7e982bf20f", "metadata": {}, "source": [ "help(OpenAI)" ] }, { "cell_type": "code", "execution_count": 14, "id": "fab82c74-3d20-48c7-90c4-6eb2d1495588", "metadata": {}, "outputs": [], "source": [ "llm = OpenAI(\n", " model=\"gpt-3.5-turbo-instruct\",\n", " temperature=0.7\n", ")" ] }, { "cell_type": "code", "execution_count": 15, "id": "14861594-9c6d-45b3-a00c-dc1259f25076", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "str" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "response = llm.invoke(\"What is data engineering?\")\n", "type(response)" ] }, { "cell_type": "code", "execution_count": 16, "id": "fd026fde-0a77-4a9a-8e25-34bad506d634", "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "\n", "Data engineering is the process of designing, building, and maintaining the infrastructure and systems that are used to collect, store, process, and analyze large amounts of data. This involves creating data pipelines, data warehouses, and data lakes to ensure that data is available and accessible for analysis and decision-making. Data engineers work closely with data scientists and analysts to ensure that the data is accurate, reliable, and relevant for the organization's needs. They also use programming languages and tools to clean, transform, and organize data for efficient analysis. Data engineering plays a crucial role in the field of big data and data analytics, as it enables businesses and organizations to make data-driven decisions and gain insights from their data. \n" ] } ], "source": [ "print(response)" ] }, { "cell_type": "code", "execution_count": null, "id": "b227837c-03f8-488a-b605-0c3eebc869cf", "metadata": {}, "outputs": [], "source": [ "## how about stream and batch, and ainvoke, and astream and abatch" ] }, { "cell_type": "code", "execution_count": 17, "id": "78a6e6f7-7f4e-43d5-865a-92f919c2e9f2", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "\n", "|Data| engineering| is| the| process| of| designing|,| building|,| and| maintaining| systems| and infrastructure for| managing| large| volumes| of data.| It involves understanding| the| business| needs| and| requirements|,| sourcing and| collecting| data| from| various| sources|,| organizing| and| transforming| the| data| into| usable| formats|,| and| ensuring| its| availability|,| reliability|,| and| security| for analysis and| decision| making|.| Data| engineers| also| work| on| optimizing| data| pipelines|,| developing| data| architecture|,| and| implementing| data| quality| and| governance| processes|.| They| use a variety of| tools| and| technologies|,| such| as| databases|,| data| warehouses|,| E|TL| (|extract|,| transform|,| load|)| tools|,| and| cloud| computing|,| to support the| data needs of| an| organization|.||" ] } ], "source": [ "for token in llm.stream(\"What is data engineering?\"):\n", " print(token, end=\"|\")" ] }, { "cell_type": "code", "execution_count": 18, "id": "6b55654a-a9bc-4a6f-ac69-b5ea48cfdd0e", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Note: you may need to restart the kernel to use updated packages.\n" ] } ], "source": [ "pip install -qU asyncio" ] }, { "cell_type": "code", "execution_count": 19, "id": "ed31131e-59e5-4889-9e71-6f65e3ec4e49", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Requirement already satisfied: anyio<4 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (3.7.1)\n", "Requirement already satisfied: idna>=2.8 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from anyio<4) (3.4)\n", "Requirement already satisfied: sniffio>=1.1 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from anyio<4) (1.3.0)\n", "Requirement already satisfied: exceptiongroup in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from anyio<4) (1.1.1)\n", "Note: you may need to restart the kernel to use updated packages.\n" ] } ], "source": [ "pip install \"anyio<4\"" ] }, { "cell_type": "code", "execution_count": 20, "id": "135098a3-73db-4a46-a081-266c6f898153", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "\n", "Why did the AI cross the road?\n", "\n", "To get to the other data center!\n" ] } ], "source": [ "response = await llm.ainvoke(\"Tell me a funny joke about AI.\")\n", "print(response)" ] }, { "cell_type": "code", "execution_count": null, "id": "53605bf7-9470-4c01-b57b-60982b397024", "metadata": {}, "outputs": [], "source": [ "# OK, but this is supposed to be an abstraction, so let's try another one" ] }, { "cell_type": "code", "execution_count": 21, "id": "e961db28-7f1d-49c7-add3-b867cc44f828", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Note: you may need to restart the kernel to use updated packages.\n" ] } ], "source": [ "pip install -qU langchain-google-genai" ] }, { "cell_type": "code", "execution_count": 22, "id": "07c1ebac-6b11-4bdf-a9c4-7b56e98d01c1", "metadata": {}, "outputs": [], "source": [ "from langchain_google_genai import GoogleGenerativeAI" ] }, { "cell_type": "code", "execution_count": null, "id": "b50801a1-031a-4bae-9d3f-4c816e7af22e", "metadata": {}, "outputs": [], "source": [ "# Unfornately not all the models are constructed in the same .\n", "# It LOOKs like it is but this time, this one is not just an API key, I need Application Default Credentials.\n", "# Which is both more secure, and more of a pain in the ass in a notebook." ] }, { "cell_type": "code", "execution_count": 23, "id": "a8455337-a28a-4469-9f63-5b05fd62bc5d", "metadata": {}, "outputs": [ { "ename": "DefaultCredentialsError", "evalue": "Your default credentials were not found. To set up Application Default Credentials, see https://cloud.google.com/docs/authentication/external/set-up-adc for more information.", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mDefaultCredentialsError\u001b[0m Traceback (most recent call last)", "Cell \u001b[0;32mIn[23], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m llm \u001b[38;5;241m=\u001b[39m \u001b[43mGoogleGenerativeAI\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 2\u001b[0m \u001b[43m \u001b[49m\u001b[43mmodel\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mgemini-pro\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 3\u001b[0m \u001b[43m \u001b[49m\u001b[43mtemperature\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m0.7\u001b[39;49m\n\u001b[1;32m 4\u001b[0m \u001b[43m)\u001b[49m\n", "File \u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/langchain_google_genai/llms.py:60\u001b[0m, in \u001b[0;36mGoogleGenerativeAI.__init__\u001b[0;34m(self, **kwargs)\u001b[0m\n\u001b[1;32m 53\u001b[0m suggestion \u001b[38;5;241m=\u001b[39m (\n\u001b[1;32m 54\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m Did you mean: \u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;132;01m{\u001b[39;00msuggestions[\u001b[38;5;241m0\u001b[39m]\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m?\u001b[39m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m suggestions \u001b[38;5;28;01melse\u001b[39;00m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 55\u001b[0m )\n\u001b[1;32m 56\u001b[0m logger\u001b[38;5;241m.\u001b[39mwarning(\n\u001b[1;32m 57\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mUnexpected argument \u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;132;01m{\u001b[39;00marg\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 58\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mprovided to GoogleGenerativeAI.\u001b[39m\u001b[38;5;132;01m{\u001b[39;00msuggestion\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 59\u001b[0m )\n\u001b[0;32m---> 60\u001b[0m \u001b[38;5;28;43msuper\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[38;5;21;43m__init__\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", "File \u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/langchain_core/load/serializable.py:130\u001b[0m, in \u001b[0;36mSerializable.__init__\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m 128\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__init__\u001b[39m(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;241m*\u001b[39margs: Any, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs: Any) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 129\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"\"\"\"\u001b[39;00m \u001b[38;5;66;03m# noqa: D419\u001b[39;00m\n\u001b[0;32m--> 130\u001b[0m \u001b[38;5;28;43msuper\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[38;5;21;43m__init__\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", " \u001b[0;31m[... skipping hidden 1 frame]\u001b[0m\n", "File \u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/langchain_google_genai/llms.py:66\u001b[0m, in \u001b[0;36mGoogleGenerativeAI.validate_environment\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 62\u001b[0m \u001b[38;5;129m@model_validator\u001b[39m(mode\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mafter\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 63\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mvalidate_environment\u001b[39m(\u001b[38;5;28mself\u001b[39m) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m Self:\n\u001b[1;32m 64\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Validates params and passes them to google-generativeai package.\"\"\"\u001b[39;00m\n\u001b[0;32m---> 66\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mclient \u001b[38;5;241m=\u001b[39m \u001b[43mChatGoogleGenerativeAI\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 67\u001b[0m \u001b[43m \u001b[49m\u001b[43mapi_key\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mgoogle_api_key\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 68\u001b[0m \u001b[43m \u001b[49m\u001b[43mcredentials\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcredentials\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 69\u001b[0m \u001b[43m \u001b[49m\u001b[43mtemperature\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtemperature\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 70\u001b[0m \u001b[43m \u001b[49m\u001b[43mtop_p\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtop_p\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 71\u001b[0m \u001b[43m \u001b[49m\u001b[43mtop_k\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtop_k\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 72\u001b[0m \u001b[43m \u001b[49m\u001b[43mmax_tokens\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmax_output_tokens\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 73\u001b[0m \u001b[43m \u001b[49m\u001b[43mtimeout\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtimeout\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 74\u001b[0m \u001b[43m \u001b[49m\u001b[43mmodel\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmodel\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 75\u001b[0m \u001b[43m \u001b[49m\u001b[43mclient_options\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mclient_options\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 76\u001b[0m \u001b[43m \u001b[49m\u001b[43mtransport\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtransport\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 77\u001b[0m \u001b[43m \u001b[49m\u001b[43madditional_headers\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43madditional_headers\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 78\u001b[0m \u001b[43m \u001b[49m\u001b[43msafety_settings\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msafety_settings\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 79\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 81\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\n", "File \u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/langchain_google_genai/chat_models.py:1108\u001b[0m, in \u001b[0;36mChatGoogleGenerativeAI.__init__\u001b[0;34m(self, **kwargs)\u001b[0m\n\u001b[1;32m 1101\u001b[0m suggestion \u001b[38;5;241m=\u001b[39m (\n\u001b[1;32m 1102\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m Did you mean: \u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;132;01m{\u001b[39;00msuggestions[\u001b[38;5;241m0\u001b[39m]\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m?\u001b[39m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m suggestions \u001b[38;5;28;01melse\u001b[39;00m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 1103\u001b[0m )\n\u001b[1;32m 1104\u001b[0m logger\u001b[38;5;241m.\u001b[39mwarning(\n\u001b[1;32m 1105\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mUnexpected argument \u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;132;01m{\u001b[39;00marg\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 1106\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mprovided to ChatGoogleGenerativeAI.\u001b[39m\u001b[38;5;132;01m{\u001b[39;00msuggestion\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 1107\u001b[0m )\n\u001b[0;32m-> 1108\u001b[0m \u001b[38;5;28;43msuper\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[38;5;21;43m__init__\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", "File \u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/langchain_core/load/serializable.py:130\u001b[0m, in \u001b[0;36mSerializable.__init__\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m 128\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__init__\u001b[39m(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;241m*\u001b[39margs: Any, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs: Any) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 129\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"\"\"\"\u001b[39;00m \u001b[38;5;66;03m# noqa: D419\u001b[39;00m\n\u001b[0;32m--> 130\u001b[0m \u001b[38;5;28;43msuper\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[38;5;21;43m__init__\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", " \u001b[0;31m[... skipping hidden 1 frame]\u001b[0m\n", "File \u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/langchain_google_genai/chat_models.py:1169\u001b[0m, in \u001b[0;36mChatGoogleGenerativeAI.validate_environment\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 1167\u001b[0m google_api_key \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mgoogle_api_key\n\u001b[1;32m 1168\u001b[0m transport: Optional[\u001b[38;5;28mstr\u001b[39m] \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mtransport\n\u001b[0;32m-> 1169\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mclient \u001b[38;5;241m=\u001b[39m \u001b[43mgenaix\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mbuild_generative_service\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 1170\u001b[0m \u001b[43m \u001b[49m\u001b[43mcredentials\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcredentials\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1171\u001b[0m \u001b[43m \u001b[49m\u001b[43mapi_key\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mgoogle_api_key\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1172\u001b[0m \u001b[43m \u001b[49m\u001b[43mclient_info\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mclient_info\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1173\u001b[0m \u001b[43m \u001b[49m\u001b[43mclient_options\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mclient_options\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1174\u001b[0m \u001b[43m \u001b[49m\u001b[43mtransport\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mtransport\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1175\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1176\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39masync_client_running \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[1;32m 1177\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\n", "File \u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/langchain_google_genai/_genai_extension.py:276\u001b[0m, in \u001b[0;36mbuild_generative_service\u001b[0;34m(credentials, api_key, client_options, client_info, transport)\u001b[0m\n\u001b[1;32m 262\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mbuild_generative_service\u001b[39m(\n\u001b[1;32m 263\u001b[0m credentials: Optional[credentials\u001b[38;5;241m.\u001b[39mCredentials] \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[1;32m 264\u001b[0m api_key: Optional[\u001b[38;5;28mstr\u001b[39m] \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 267\u001b[0m transport: Optional[\u001b[38;5;28mstr\u001b[39m] \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[1;32m 268\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m v1betaGenerativeServiceClient:\n\u001b[1;32m 269\u001b[0m config \u001b[38;5;241m=\u001b[39m _prepare_config(\n\u001b[1;32m 270\u001b[0m credentials\u001b[38;5;241m=\u001b[39mcredentials,\n\u001b[1;32m 271\u001b[0m api_key\u001b[38;5;241m=\u001b[39mapi_key,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 274\u001b[0m client_info\u001b[38;5;241m=\u001b[39mclient_info,\n\u001b[1;32m 275\u001b[0m )\n\u001b[0;32m--> 276\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mv1betaGenerativeServiceClient\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mconfig\u001b[49m\u001b[43m)\u001b[49m\n", "File \u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/google/ai/generativelanguage_v1beta/services/generative_service/client.py:697\u001b[0m, in \u001b[0;36mGenerativeServiceClient.__init__\u001b[0;34m(self, credentials, transport, client_options, client_info)\u001b[0m\n\u001b[1;32m 688\u001b[0m transport_init: Union[\n\u001b[1;32m 689\u001b[0m Type[GenerativeServiceTransport],\n\u001b[1;32m 690\u001b[0m Callable[\u001b[38;5;241m.\u001b[39m\u001b[38;5;241m.\u001b[39m\u001b[38;5;241m.\u001b[39m, GenerativeServiceTransport],\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 694\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m cast(Callable[\u001b[38;5;241m.\u001b[39m\u001b[38;5;241m.\u001b[39m\u001b[38;5;241m.\u001b[39m, GenerativeServiceTransport], transport)\n\u001b[1;32m 695\u001b[0m )\n\u001b[1;32m 696\u001b[0m \u001b[38;5;66;03m# initialize with the provided callable or the passed in class\u001b[39;00m\n\u001b[0;32m--> 697\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_transport \u001b[38;5;241m=\u001b[39m \u001b[43mtransport_init\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 698\u001b[0m \u001b[43m \u001b[49m\u001b[43mcredentials\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcredentials\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 699\u001b[0m \u001b[43m \u001b[49m\u001b[43mcredentials_file\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_client_options\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcredentials_file\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 700\u001b[0m \u001b[43m \u001b[49m\u001b[43mhost\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_api_endpoint\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 701\u001b[0m \u001b[43m \u001b[49m\u001b[43mscopes\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_client_options\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mscopes\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 702\u001b[0m \u001b[43m \u001b[49m\u001b[43mclient_cert_source_for_mtls\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_client_cert_source\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 703\u001b[0m \u001b[43m \u001b[49m\u001b[43mquota_project_id\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_client_options\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mquota_project_id\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 704\u001b[0m \u001b[43m \u001b[49m\u001b[43mclient_info\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mclient_info\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 705\u001b[0m \u001b[43m \u001b[49m\u001b[43malways_use_jwt_access\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[1;32m 706\u001b[0m \u001b[43m \u001b[49m\u001b[43mapi_audience\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_client_options\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mapi_audience\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 707\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 709\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124masync\u001b[39m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mstr\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_transport):\n\u001b[1;32m 710\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m CLIENT_LOGGING_SUPPORTED \u001b[38;5;129;01mand\u001b[39;00m _LOGGER\u001b[38;5;241m.\u001b[39misEnabledFor(\n\u001b[1;32m 711\u001b[0m std_logging\u001b[38;5;241m.\u001b[39mDEBUG\n\u001b[1;32m 712\u001b[0m ): \u001b[38;5;66;03m# pragma: NO COVER\u001b[39;00m\n", "File \u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/google/ai/generativelanguage_v1beta/services/generative_service/transports/grpc.py:234\u001b[0m, in \u001b[0;36mGenerativeServiceGrpcTransport.__init__\u001b[0;34m(self, host, credentials, credentials_file, scopes, channel, api_mtls_endpoint, client_cert_source, ssl_channel_credentials, client_cert_source_for_mtls, quota_project_id, client_info, always_use_jwt_access, api_audience)\u001b[0m\n\u001b[1;32m 229\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_ssl_channel_credentials \u001b[38;5;241m=\u001b[39m grpc\u001b[38;5;241m.\u001b[39mssl_channel_credentials(\n\u001b[1;32m 230\u001b[0m certificate_chain\u001b[38;5;241m=\u001b[39mcert, private_key\u001b[38;5;241m=\u001b[39mkey\n\u001b[1;32m 231\u001b[0m )\n\u001b[1;32m 233\u001b[0m \u001b[38;5;66;03m# The base transport sets the host, credentials and scopes\u001b[39;00m\n\u001b[0;32m--> 234\u001b[0m \u001b[38;5;28;43msuper\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[38;5;21;43m__init__\u001b[39;49m\u001b[43m(\u001b[49m\n\u001b[1;32m 235\u001b[0m \u001b[43m \u001b[49m\u001b[43mhost\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mhost\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 236\u001b[0m \u001b[43m \u001b[49m\u001b[43mcredentials\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcredentials\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 237\u001b[0m \u001b[43m \u001b[49m\u001b[43mcredentials_file\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcredentials_file\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 238\u001b[0m \u001b[43m \u001b[49m\u001b[43mscopes\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mscopes\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 239\u001b[0m \u001b[43m \u001b[49m\u001b[43mquota_project_id\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mquota_project_id\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 240\u001b[0m \u001b[43m \u001b[49m\u001b[43mclient_info\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mclient_info\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 241\u001b[0m \u001b[43m \u001b[49m\u001b[43malways_use_jwt_access\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43malways_use_jwt_access\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 242\u001b[0m \u001b[43m \u001b[49m\u001b[43mapi_audience\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mapi_audience\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 243\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 245\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_grpc_channel:\n\u001b[1;32m 246\u001b[0m \u001b[38;5;66;03m# initialize with the provided callable or the default channel\u001b[39;00m\n\u001b[1;32m 247\u001b[0m channel_init \u001b[38;5;241m=\u001b[39m channel \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;28mtype\u001b[39m(\u001b[38;5;28mself\u001b[39m)\u001b[38;5;241m.\u001b[39mcreate_channel\n", "File \u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/google/ai/generativelanguage_v1beta/services/generative_service/transports/base.py:100\u001b[0m, in \u001b[0;36mGenerativeServiceTransport.__init__\u001b[0;34m(self, host, credentials, credentials_file, scopes, quota_project_id, client_info, always_use_jwt_access, api_audience, **kwargs)\u001b[0m\n\u001b[1;32m 96\u001b[0m credentials, _ \u001b[38;5;241m=\u001b[39m google\u001b[38;5;241m.\u001b[39mauth\u001b[38;5;241m.\u001b[39mload_credentials_from_file(\n\u001b[1;32m 97\u001b[0m credentials_file, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mscopes_kwargs, quota_project_id\u001b[38;5;241m=\u001b[39mquota_project_id\n\u001b[1;32m 98\u001b[0m )\n\u001b[1;32m 99\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m credentials \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_ignore_credentials:\n\u001b[0;32m--> 100\u001b[0m credentials, _ \u001b[38;5;241m=\u001b[39m \u001b[43mgoogle\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mauth\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdefault\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 101\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mscopes_kwargs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mquota_project_id\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mquota_project_id\u001b[49m\n\u001b[1;32m 102\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 103\u001b[0m \u001b[38;5;66;03m# Don't apply audience if the credentials file passed from user.\u001b[39;00m\n\u001b[1;32m 104\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mhasattr\u001b[39m(credentials, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mwith_gdch_audience\u001b[39m\u001b[38;5;124m\"\u001b[39m):\n", "File \u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/google/auth/_default.py:685\u001b[0m, in \u001b[0;36mdefault\u001b[0;34m(scopes, request, quota_project_id, default_scopes)\u001b[0m\n\u001b[1;32m 677\u001b[0m _LOGGER\u001b[38;5;241m.\u001b[39mwarning(\n\u001b[1;32m 678\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mNo project ID could be determined. Consider running \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 679\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m`gcloud config set project` or setting the \u001b[39m\u001b[38;5;132;01m%s\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 680\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124menvironment variable\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[1;32m 681\u001b[0m environment_vars\u001b[38;5;241m.\u001b[39mPROJECT,\n\u001b[1;32m 682\u001b[0m )\n\u001b[1;32m 683\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m credentials, effective_project_id\n\u001b[0;32m--> 685\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m exceptions\u001b[38;5;241m.\u001b[39mDefaultCredentialsError(_CLOUD_SDK_MISSING_CREDENTIALS)\n", "\u001b[0;31mDefaultCredentialsError\u001b[0m: Your default credentials were not found. To set up Application Default Credentials, see https://cloud.google.com/docs/authentication/external/set-up-adc for more information." ] } ], "source": [ "llm = GoogleGenerativeAI(\n", " model=\"gemini-pro\",\n", " temperature=0.7\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "7e67fd7a-eb44-49a7-9d02-2ac976bf95b4", "metadata": {}, "outputs": [], "source": [ "# lets try another" ] }, { "cell_type": "code", "execution_count": 24, "id": "2cdd4f7c-53b4-4648-a2a7-546f935c18f5", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Note: you may need to restart the kernel to use updated packages.\n" ] } ], "source": [ "pip install -qU langchain-anthropic" ] }, { "cell_type": "code", "execution_count": null, "id": "c8fbafbb-01a4-4afc-93dd-3309c79b9599", "metadata": {}, "outputs": [], "source": [ "# You will need to fill in your OWN key" ] }, { "cell_type": "code", "execution_count": 25, "id": "eb5824a8-1e45-4361-a75b-d60d7aaf5db2", "metadata": {}, "outputs": [], "source": [ "import os\n", "os.environ[\"ANTHROPIC_API_KEY\"] = \"\"" ] }, { "cell_type": "code", "execution_count": 26, "id": "38a1c2b0-24ca-4773-bae4-f1006aa1d232", "metadata": {}, "outputs": [], "source": [ "from langchain_anthropic import AnthropicLLM" ] }, { "cell_type": "code", "execution_count": null, "id": "b34b5c4d-5324-4f9a-8b14-feef1d0457ca", "metadata": {}, "outputs": [], "source": [ "# How do I list the models that I can use, all libraries do this ina different way, unfortunately" ] }, { "cell_type": "code", "execution_count": 27, "id": "08902b4c-7464-40ac-bd51-a5940cbe83fc", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['claude-opus-4-20250514',\n", " 'claude-sonnet-4-20250514',\n", " 'claude-3-7-sonnet-20250219',\n", " 'claude-3-5-sonnet-20241022',\n", " 'claude-3-5-haiku-20241022',\n", " 'claude-3-5-sonnet-20240620',\n", " 'claude-3-haiku-20240307',\n", " 'claude-3-opus-20240229']" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import anthropic\n", "\n", "client = anthropic.Anthropic()\n", "\n", "[model.id for model in client.models.list()]" ] }, { "cell_type": "code", "execution_count": null, "id": "c3e4997f-2878-4122-8a64-63e66b14dd4a", "metadata": {}, "outputs": [], "source": [ "# This one is once again going to fail, doesn't matter which model I pass... Documentation out of date." ] }, { "cell_type": "code", "execution_count": 28, "id": "083da982-3b66-4f31-8053-23f6f061182e", "metadata": { "scrolled": true }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/langchain_anthropic/llms.py:166: UserWarning: This Anthropic LLM is deprecated. Please use `from langchain_anthropic import ChatAnthropic` instead\n", " warnings.warn(\n" ] } ], "source": [ "llm = AnthropicLLM(\n", " temperature=0.7,\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "1dd504b7-cf25-4b45-8f67-14a68052a6b3", "metadata": {}, "outputs": [], "source": [ "# Let me try NVIDIA" ] }, { "cell_type": "code", "execution_count": 29, "id": "d5978ed0-7e0e-4490-9788-4026db1163f2", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Note: you may need to restart the kernel to use updated packages.\n" ] } ], "source": [ "pip install -qU langchain-nvidia-ai-endpoints" ] }, { "cell_type": "code", "execution_count": null, "id": "83efa5a9-391e-4b72-88b5-3424d0e18edd", "metadata": {}, "outputs": [], "source": [ "# NOTE: you will need your own key" ] }, { "cell_type": "code", "execution_count": 30, "id": "8cffa3ed-fc7b-499c-92aa-4a14f3f82b04", "metadata": {}, "outputs": [], "source": [ "import os\n", "os.environ[\"NVIDIA_API_KEY\"] = \"\"" ] }, { "cell_type": "code", "execution_count": 31, "id": "50a0aea6-55d7-4128-85eb-534cd719a1e8", "metadata": {}, "outputs": [], "source": [ "from langchain_nvidia_ai_endpoints import NVIDIA" ] }, { "cell_type": "code", "execution_count": 32, "id": "936adeb3-b630-4c5e-8b2c-16bcde400faa", "metadata": {}, "outputs": [], "source": [ "llm = NVIDIA(\n", " temperature=0.7,\n", ")" ] }, { "cell_type": "code", "execution_count": 33, "id": "e1e432c7-fb0a-455b-8131-8160095c23d6", "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " | Data Engineering vs Data Science\n", "\n", "Data engineering is an interdisciplinary blend of software engineering\n" ] } ], "source": [ "response = llm.invoke(\"What is data engineering?\")\n", "print(response)" ] }, { "cell_type": "code", "execution_count": null, "id": "cfef67a0-6195-4c08-97d5-0267c74fb914", "metadata": {}, "outputs": [], "source": [ "# So the summary is IF you can manage to contruct one, they all work the same, following the laws of abstraction" ] }, { "cell_type": "markdown", "id": "1367ec02-e894-4ab4-bc81-ca80eea96136", "metadata": {}, "source": [ "# Chat_models" ] }, { "cell_type": "markdown", "id": "c4db0ad7-28bc-4847-821a-fdd4bc15776c", "metadata": {}, "source": [ "[Chat_model integrations](https://python.langchain.com/docs/integrations/chat/)" ] }, { "cell_type": "code", "execution_count": 34, "id": "efc83a04-354d-49ed-bb88-8ba057a1e52c", "metadata": {}, "outputs": [], "source": [ "from langchain_openai import ChatOpenAI" ] }, { "cell_type": "code", "execution_count": 35, "id": "a3f8b6c4-4077-4901-b853-242ec968b9be", "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "(langchain_openai.chat_models.base.ChatOpenAI,\n", " langchain_openai.chat_models.base.BaseChatOpenAI,\n", " langchain_core.language_models.chat_models.BaseChatModel,\n", " langchain_core.language_models.base.BaseLanguageModel[BaseMessage],\n", " langchain_core.language_models.base.BaseLanguageModel,\n", " langchain_core.runnables.base.RunnableSerializable[Union[PromptValue, str, Sequence[Union[BaseMessage, list[str], tuple[str, str], str, dict[str, Any]]]], ~LanguageModelOutputVar],\n", " langchain_core.runnables.base.RunnableSerializable,\n", " langchain_core.load.serializable.Serializable,\n", " pydantic.main.BaseModel,\n", " langchain_core.runnables.base.Runnable,\n", " typing.Generic,\n", " abc.ABC,\n", " object)" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ChatOpenAI.__mro__" ] }, { "cell_type": "code", "execution_count": 36, "id": "50c277ac-0e96-4007-9992-7cc688aa6427", "metadata": {}, "outputs": [], "source": [ "chat_model = ChatOpenAI(\n", " model=\"gpt-4-turbo\",\n", " temperature=0.7\n", ")" ] }, { "cell_type": "code", "execution_count": 37, "id": "7185949e-1480-4e9b-90d4-1cb7246c5917", "metadata": {}, "outputs": [], "source": [ "response = chat_model.invoke(\"Write me a haiku about SQL\")" ] }, { "cell_type": "code", "execution_count": 38, "id": "c080e371-ace9-4c9d-8f8e-fc9269d18260", "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "langchain_core.messages.ai.AIMessage" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(response)" ] }, { "cell_type": "code", "execution_count": null, "id": "1e78a2d0-00b0-4813-888a-32035702d7d1", "metadata": {}, "outputs": [], "source": [ "# We passed it a string, but we got back an AIMessage\n", "# Since this is no longer a string, we can't just print it, we need to use the content property" ] }, { "cell_type": "code", "execution_count": 39, "id": "3712f89e-dad6-4599-8f40-18ffc93fff38", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Fields join and query,\n", "Data flows like river streams,\n", "Tables met in sync.\n" ] } ], "source": [ "print(response.content)" ] }, { "cell_type": "code", "execution_count": null, "id": "b4470c23-7d48-4b8d-a832-ee1407bcebc9", "metadata": {}, "outputs": [], "source": [ "# We have stumbled upon another abstraction - a message, what other types of messages are there?" ] }, { "cell_type": "code", "execution_count": 40, "id": "c170bb79-6318-4905-aff2-b638647aa03a", "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "AIMessage:\n", " Message from an AI.\n", "\n", "AIMessage is returned from a chat model as a response to a prompt.\n", "\n", "This message represents the output of the model and consists of both\n", "the raw output as returned by the model together standardized fields\n", "(e.g., tool calls, usage metadata) added by the LangChain framework.\n", "\n", "AIMessageChunk:\n", " Message chunk from an AI.\n", "\n", "BaseMessageChunk:\n", " Message chunk, which can be concatenated with other Message chunks.\n", "\n", "ChatMessage:\n", " Message that can be assigned an arbitrary speaker (i.e. role).\n", "\n", "ChatMessageChunk:\n", " Chat Message chunk.\n", "\n", "FunctionMessage:\n", " Message for passing the result of executing a tool back to a model.\n", "\n", "FunctionMessage are an older version of the ToolMessage schema, and\n", "do not contain the tool_call_id field.\n", "\n", "The tool_call_id field is used to associate the tool call request with the\n", "tool call response. This is useful in situations where a chat model is able\n", "to request multiple tool calls in parallel.\n", "\n", "FunctionMessageChunk:\n", " Function Message chunk.\n", "\n", "HumanMessage:\n", " Message from a human.\n", "\n", "HumanMessages are messages that are passed in from a human to the model.\n", "\n", "Example:\n", "\n", " .. code-block:: python\n", "\n", " from langchain_core.messages import HumanMessage, SystemMessage\n", "\n", " messages = [\n", " SystemMessage(\n", " content=\"You are a helpful assistant! Your name is Bob.\"\n", " ),\n", " HumanMessage(\n", " content=\"What is your name?\"\n", " )\n", " ]\n", "\n", " # Instantiate a chat model and invoke it with the messages\n", " model = ...\n", " print(model.invoke(messages))\n", "\n", "HumanMessageChunk:\n", " Human Message chunk.\n", "\n", "RemoveMessage:\n", " Message responsible for deleting other messages.\n", "\n", "SystemMessage:\n", " Message for priming AI behavior.\n", "\n", "The system message is usually passed in as the first of a sequence\n", "of input messages.\n", "\n", "Example:\n", "\n", " .. code-block:: python\n", "\n", " from langchain_core.messages import HumanMessage, SystemMessage\n", "\n", " messages = [\n", " SystemMessage(\n", " content=\"You are a helpful assistant! Your name is Bob.\"\n", " ),\n", " HumanMessage(\n", " content=\"What is your name?\"\n", " )\n", " ]\n", "\n", " # Define a chat model and invoke it with the messages\n", " print(model.invoke(messages))\n", "\n", "SystemMessageChunk:\n", " System Message chunk.\n", "\n", "ToolMessage:\n", " Message for passing the result of executing a tool back to a model.\n", "\n", "ToolMessages contain the result of a tool invocation. Typically, the result\n", "is encoded inside the `content` field.\n", "\n", "Example: A ToolMessage representing a result of 42 from a tool call with id\n", "\n", " .. code-block:: python\n", "\n", " from langchain_core.messages import ToolMessage\n", "\n", " ToolMessage(content='42', tool_call_id='call_Jja7J89XsjrOLA5r!MEOW!SL')\n", "\n", "\n", "Example: A ToolMessage where only part of the tool output is sent to the model\n", " and the full output is passed in to artifact.\n", "\n", " .. versionadded:: 0.2.17\n", "\n", " .. code-block:: python\n", "\n", " from langchain_core.messages import ToolMessage\n", "\n", " tool_output = {\n", " \"stdout\": \"From the graph we can see that the correlation between x and y is ...\",\n", " \"stderr\": None,\n", " \"artifacts\": {\"type\": \"image\", \"base64_data\": \"/9j/4gIcSU...\"},\n", " }\n", "\n", " ToolMessage(\n", " content=tool_output[\"stdout\"],\n", " artifact=tool_output,\n", " tool_call_id='call_Jja7J89XsjrOLA5r!MEOW!SL',\n", " )\n", "\n", "The tool_call_id field is used to associate the tool call request with the\n", "tool call response. This is useful in situations where a chat model is able\n", "to request multiple tool calls in parallel.\n", "\n", "ToolMessageChunk:\n", " Tool Message chunk.\n", "\n" ] } ], "source": [ "import inspect\n", "import langchain_core.messages as messages\n", "\n", "BaseMessage = messages.base.BaseMessage\n", "\n", "derived_classes = []\n", "for name, obj in inspect.getmembers(messages):\n", " if inspect.isclass(obj) and issubclass(obj, BaseMessage) and obj is not BaseMessage:\n", " doc = inspect.getdoc(obj) or \"No description available.\"\n", " derived_classes.append((name, doc))\n", "\n", "for name, doc in derived_classes:\n", " print(f\"{name}:\\n {doc}\\n\")" ] }, { "cell_type": "code", "execution_count": null, "id": "5b17cfbf-d091-41fd-9ed6-a7a74cdb1ae6", "metadata": {}, "outputs": [], "source": [ "# We can also pass messages as input (instead of a string)" ] }, { "cell_type": "code", "execution_count": 41, "id": "81640b97-4635-42b2-b326-1e5a5cffe55f", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Ciao!\n" ] } ], "source": [ "from langchain_core.messages import HumanMessage, SystemMessage\n", "\n", "messages = [\n", " SystemMessage(\"Translate the following from English into Italian\"),\n", " HumanMessage(\"hi!\"),\n", "]\n", "\n", "translated_response = chat_model.invoke(messages)\n", "print(type(translated_response))\n", "print(translated_response.content)" ] }, { "cell_type": "code", "execution_count": null, "id": "db309b81-9a7f-4769-9f2a-fd0dee503536", "metadata": {}, "outputs": [], "source": [ "# Let's try another chat model just to see if we can switch back and forth using this abstraction" ] }, { "cell_type": "code", "execution_count": 42, "id": "23c11d01-ae5f-4a82-92d7-e65b7b8a79b4", "metadata": {}, "outputs": [], "source": [ "from langchain_anthropic import ChatAnthropic\n", "anthropic_model = ChatAnthropic(\n", " model=\"claude-3-5-sonnet-20240620\",\n", " temperature=0.7\n", ")" ] }, { "cell_type": "code", "execution_count": 43, "id": "35c49fc5-1acf-4e0b-8de7-7cebf230928a", "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Here's a haiku about SQL:\n", "\n", "Tables, rows, columns\n", "Query language for data\n", "SQL speaks clearly\n", "\n", "This haiku captures some key elements of SQL:\n", "1. The first line mentions tables, rows, and columns, which are fundamental structures in relational databases.\n", "2. The second line identifies SQL as a query language used for working with data.\n", "3. The third line suggests that SQL provides a clear way to communicate with databases.\n" ] } ], "source": [ "response = anthropic_model.invoke(\"Write me a haiku about SQL\")\n", "print(type(response))\n", "print(response.content)" ] }, { "cell_type": "code", "execution_count": 44, "id": "6a21113c-5d32-414e-88fc-c75ddada370e", "metadata": {}, "outputs": [], "source": [ "from langchain_nvidia_ai_endpoints import ChatNVIDIA\n", "\n", "nvidia_model = ChatNVIDIA(model=\"meta/llama3-70b-instruct\")" ] }, { "cell_type": "code", "execution_count": 45, "id": "542f6c86-2610-4628-8b61-a60b8e8fb893", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Tables joined with care\n", "Data's hidden secrets yield\n", "Query's gentle art\n" ] } ], "source": [ "response = nvidia_model.invoke(\"Write me a haiku about SQL\")\n", "print(type(response))\n", "print(response.content)" ] }, { "cell_type": "code", "execution_count": null, "id": "7d9e57c1-4366-4002-89cd-741432a4d54a", "metadata": {}, "outputs": [], "source": [ "# Is this better than using them directly?\n", "# Once again the interfaces are really simple, construction is the hard part\n", "# It DOES mean that you can pass a constructed model to a function which works independently from the model" ] }, { "cell_type": "markdown", "id": "d65bd25a-d11c-4ca1-92f7-86200bbb9e22", "metadata": {}, "source": [ "# Prompts" ] }, { "cell_type": "code", "execution_count": 126, "id": "77f75c05-54bc-4794-b666-212b9dd46263", "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "['langchain.prompts.BaseChatPromptTemplate',\n", " 'langchain.prompts.BasePromptTemplate',\n", " 'langchain.prompts.ChatPromptTemplate',\n", " 'langchain.prompts.FewShotChatMessagePromptTemplate',\n", " 'langchain.prompts.FewShotPromptTemplate',\n", " 'langchain.prompts.FewShotPromptWithTemplates',\n", " 'langchain.prompts.PipelinePromptTemplate',\n", " 'langchain.prompts.Prompt',\n", " 'langchain.prompts.PromptTemplate',\n", " 'langchain.prompts.StringPromptTemplate',\n", " 'langchain.prompts.chat.BaseChatPromptTemplate',\n", " 'langchain.prompts.chat.ChatPromptTemplate',\n", " 'langchain.prompts.few_shot.FewShotChatMessagePromptTemplate',\n", " 'langchain.prompts.few_shot.FewShotPromptTemplate',\n", " 'langchain.prompts.few_shot_with_templates.FewShotPromptWithTemplates',\n", " 'langchain.prompts.pipeline.PipelinePromptTemplate',\n", " 'langchain.prompts.prompt.Prompt',\n", " 'langchain.prompts.prompt.PromptTemplate']" ] }, "execution_count": 126, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[name for name, cls in all_runnables if name.startswith('langchain.prompts') and not '.base.' in name]" ] }, { "cell_type": "code", "execution_count": 47, "id": "1308e074-bcb0-4318-b9e1-0c66b25904c6", "metadata": {}, "outputs": [], "source": [ "from langchain_core.prompts import ChatPromptTemplate" ] }, { "cell_type": "code", "execution_count": null, "id": "9826e3a2-6bcb-4cac-9541-c6acd31cc1a2", "metadata": { "jp-MarkdownHeadingCollapsed": true }, "outputs": [], "source": [ "# Steps: formulate the prompt, invoke the prompt with parameters, and then pass it into the model" ] }, { "cell_type": "code", "execution_count": 48, "id": "2bf30ecf-016c-421f-a620-b36a41880816", "metadata": {}, "outputs": [], "source": [ "system_template = \"Translate the following from English into {language}\"\n", "prompt_template = ChatPromptTemplate.from_messages(\n", " [(\"system\", system_template), (\"user\", \"{text}\")]\n", ")" ] }, { "cell_type": "code", "execution_count": 49, "id": "c2f195ab-2d82-4faa-90cd-e7d38aeb8bc6", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "ChatPromptValue(messages=[SystemMessage(content='Translate the following from English into Italian', additional_kwargs={}, response_metadata={}), HumanMessage(content='Hello', additional_kwargs={}, response_metadata={})])" ] }, "execution_count": 49, "metadata": {}, "output_type": "execute_result" } ], "source": [ "prompt = prompt_template.invoke({\"language\": \"Italian\", \"text\": \"Hello\"})\n", "prompt" ] }, { "cell_type": "code", "execution_count": 50, "id": "ba81a9f2-ffc8-41c4-9a6d-da1b5eb452c5", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Ciao\n" ] } ], "source": [ "response = chat_model.invoke(prompt)\n", "print(response.content)" ] }, { "cell_type": "code", "execution_count": null, "id": "d98cdb24-948b-4c55-b6b6-1cd3f954d415", "metadata": {}, "outputs": [], "source": [ "# There are also other ways to construct chat models" ] }, { "cell_type": "code", "execution_count": 51, "id": "3430b0e5-f5ca-443e-b292-612a8d98a0ae", "metadata": {}, "outputs": [], "source": [ "from langchain.chat_models import init_chat_model\n", "\n", "model = init_chat_model(\"gpt-4o-mini\", model_provider=\"openai\")" ] }, { "cell_type": "code", "execution_count": null, "id": "05de5302-bf19-4b55-9d62-c19235a803e2", "metadata": {}, "outputs": [], "source": [ "# Let's try and stretch the limit of what these chat_models can do" ] }, { "cell_type": "code", "execution_count": 52, "id": "c9cb6174-c779-4875-8861-fc0a126c8d5b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] }, { "data": { "text/plain": [ "'Hi Scott! How can I assist you today?'" ] }, "execution_count": 52, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from langchain_core.messages import HumanMessage\n", "\n", "response = model.invoke([HumanMessage(content=\"Hi! My name is Scott\")])\n", "print(type(response))\n", "response.content" ] }, { "cell_type": "code", "execution_count": 53, "id": "4e0b9d79-eb28-4cf4-a30b-24409be4d5ad", "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "\"I'm sorry, but I don't have access to personal information about users unless it has been shared with me in the course of our conversation. How can I assist you today?\"" ] }, "execution_count": 53, "metadata": {}, "output_type": "execute_result" } ], "source": [ "response2 = model.invoke([HumanMessage(content=\"What's my name?\")])\n", "response2.content" ] }, { "cell_type": "code", "execution_count": null, "id": "0efe3e12-5896-45a9-a324-986254e8930c", "metadata": {}, "outputs": [], "source": [ "# We seems to be missing some state here..." ] }, { "cell_type": "code", "execution_count": null, "id": "857875e7-68bf-43e1-9a04-64f5f0b57d49", "metadata": {}, "outputs": [], "source": [ "# First, let's roll our own in-memory persistence via a simple list of messages" ] }, { "cell_type": "code", "execution_count": 54, "id": "ab3562b1-9439-4abf-abe2-c68fed26b5d9", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Hi Scott! How can I assist you today?'" ] }, "execution_count": 54, "metadata": {}, "output_type": "execute_result" } ], "source": [ "messages = [HumanMessage(content=\"Hi! I'm Scott\")] # this is our state object\n", "responseMessage = model.invoke(messages)\n", "responseMessage.content" ] }, { "cell_type": "code", "execution_count": 55, "id": "32ec6ea1-32c0-4c3a-8f74-22a799707953", "metadata": {}, "outputs": [], "source": [ "messages.append(responseMessage) # add it to the state" ] }, { "cell_type": "code", "execution_count": 56, "id": "2f29e60b-d27c-4ceb-82ab-478ba5208748", "metadata": {}, "outputs": [], "source": [ "messages.append(HumanMessage(content=\"What's my name?\"))" ] }, { "cell_type": "code", "execution_count": 57, "id": "34dd53b8-98fb-486a-b71e-fd0cc9b0f347", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Your name is Scott. How can I help you today, Scott?'" ] }, "execution_count": 57, "metadata": {}, "output_type": "execute_result" } ], "source": [ "responseMessage2 = model.invoke(messages) # send the entire state\n", "responseMessage2.content" ] }, { "cell_type": "markdown", "id": "b6922573-3218-4c2b-9196-049900a33d0f", "metadata": {}, "source": [ "# LangGraph" ] }, { "cell_type": "code", "execution_count": 58, "id": "baff0b88-95c1-4b39-8b89-2d344978cb55", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Note: you may need to restart the kernel to use updated packages.\n" ] } ], "source": [ "pip install -qU langgraph" ] }, { "cell_type": "code", "execution_count": null, "id": "d62d4f4a-b5f1-40a4-b3b1-eda782a90e6a", "metadata": {}, "outputs": [], "source": [ "# LangGraph according to LangChain\n", "# langgraph: Orchestration framework for combining LangChain components into production-ready applications with \n", "# persistence, streaming, and other key features. " ] }, { "cell_type": "code", "execution_count": null, "id": "67c10fb1-2948-43f0-ad38-647b8fac4816", "metadata": {}, "outputs": [], "source": [ "# LangGraph is essentially a state machine. Each node takes in a state, performs an operation, and returns the new state." ] }, { "cell_type": "code", "execution_count": null, "id": "435705d2-4451-4b1a-acfd-99c5ab72420c", "metadata": {}, "outputs": [], "source": [ "# Let's start with the lowest level way to define our own state, so we can see what is going on" ] }, { "cell_type": "code", "execution_count": 59, "id": "16c80960-68ca-4b1b-835e-ffa160b95ccc", "metadata": {}, "outputs": [], "source": [ "from typing import Annotated\n", "from typing_extensions import TypedDict\n", "\n", "from langgraph.graph.message import add_messages\n", "\n", "class State(TypedDict):\n", " # Messages have the type \"list\". The `add_messages` function\n", " # in the annotation defines how this state key should be updated\n", " # (in this case, it appends messages to the list, rather than overwriting them)\n", " messages: Annotated[list, add_messages]" ] }, { "cell_type": "code", "execution_count": null, "id": "bb11364c-c837-4f12-86cf-d5db253b466b", "metadata": {}, "outputs": [], "source": [ "# just FYI - there is already a predifined state object called MessagesState which is exactly this.\n", "# from langgraph.graph import MessagesState" ] }, { "cell_type": "code", "execution_count": null, "id": "598566e3-f68b-432d-89e4-de479336ba70", "metadata": {}, "outputs": [], "source": [ "# We have defined what our state looks like, how do we use it the graph?" ] }, { "cell_type": "code", "execution_count": 64, "id": "e073605c-458b-4ba2-bc36-c954559dfd35", "metadata": {}, "outputs": [], "source": [ "from langgraph.graph import StateGraph\n", "graph_builder = StateGraph(State)" ] }, { "cell_type": "code", "execution_count": null, "id": "c72f6184-926b-4b20-9747-f25e94af3ea3", "metadata": {}, "outputs": [], "source": [ "# we have our chat model from above, let's create a node (takes a State, performs an operation, returns updated State)" ] }, { "cell_type": "code", "execution_count": 65, "id": "41bdf9e0-f4ef-435f-9c7a-ad6e29ca9d87", "metadata": {}, "outputs": [], "source": [ "def chatbot(state: State):\n", " return {\"messages\": [model.invoke(state[\"messages\"])]}" ] }, { "cell_type": "code", "execution_count": null, "id": "90798165-fc0a-4c96-bf16-04b4778f42ee", "metadata": {}, "outputs": [], "source": [ "# OK, we have defined our node, how do we build a graph with that node in it?" ] }, { "cell_type": "code", "execution_count": 66, "id": "2fe2bc04-cec2-458d-b8c4-8fa8a9f372c8", "metadata": {}, "outputs": [], "source": [ "from langgraph.graph import START\n", "\n", "graph_builder.add_node(\"chat\", chatbot) # \"chat\" is the label of the nnode\n", "graph_builder.add_edge(START, \"chat\") # when we add an edge, we pass it two labels\n", "graph = graph_builder.compile()" ] }, { "cell_type": "code", "execution_count": null, "id": "d3e3b632-4843-4607-873d-f7271f975eeb", "metadata": {}, "outputs": [], "source": [ "# what is somewhat cool about these graphs is we can see them" ] }, { "cell_type": "code", "execution_count": 67, "id": "4450a4a3-8144-4687-9872-9b13ff0e98fd", "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAGoAAADqCAIAAADF80cYAAAAAXNSR0IArs4c6QAAFKFJREFUeJztnXl8FEW+wKun574zkwk5hpDLYBISjgkEwawI4VpQFmS5VXR5PkDxIbosyooKojwXVhEUiHggLypPnlwhEPWBEu4ASTgiCQkk5Jock0zmnunu6f1jMB/EuZLqyUzY+v416equ+eU71d3VVdVVGE3TANFTWMEOoG+D9EGB9EGB9EGB9EGB9EHBhjxeW2MzGyibmbJZKIroG3UgnIPxhThfhItleL8BfJissJ7V+25dNd+8aq6+bJLI2VIFhy/C+SIWh9s3yjLhcNrMTquZMugIcyeZOFicMEgUlybqQVbd1tdSZ//p2xbC7hyYKU0aIparOD341tBB30rcKDFWXDDyBKwxf45QqXndOrwb+iiCPvFda+11S9YkRUqWtEfRhi7XzhjOH9UlpIsfmany/yh/9VlN1KHcxn4D+I880Y3c+xYUQZ/Y19rWYJ/6H9ECMe7PIX7p0zU5Dm5vGDImbOijcibiDGku/thx+WTntMXRikiuz5196zN3kt9srMueHp48TMJckCFNxQXj6fy2WStiRVIfZdDHvZJ0OA/uaMzIlv37uAMADMyUpD0kO5TbQJE+ypYPfeeOtstVnOETFIyG1wcYMVEhlrPPF7Z7382bvs424nqxMWd+JNOx9Q0mLIj85bzB2EF62cebvpP724ZPUHC4WABi6wNw+axhj4YV7W/1so9HfZ1tRFuTPX20LDCx9Q0ysuXNtXYvBdCjvhslpvTRMqxvPIYFChYO0kfLbpQYPe7gKaGqzDggpSePgTCMGTNGq9V296hvvvlm7dq1gYkIDEgRVpWaPKW612fSk1YjpYzyXW9kkPr6epPJY6BeKC8vD0A4d1CpeYZ20tP5677BqqnG1t2HZ/+haTovL6+goKC2tjYxMXHkyJGLFy++ePHikiVLAABTp04dM2bMxo0bq6qq9u7dW1xcrNVqExMTn3jiiWnTpgEAKisr582bt3nz5rfeeisiIkIgEJSUlAAADh48+NVXXyUnJzMecISa11Jnl4S5ceVen91MCSSwTYGeyMvL27Vr18KFCxMTExsbGz/66COZTDZ//vz333//pZdeys/Pj4yMBABs2rSpubn51VdfxTCsurp63bp1sbGxQ4cO5XK5AICd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", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from IPython.display import Image, display\n", "\n", "display(Image(graph.get_graph().draw_mermaid_png()))" ] }, { "cell_type": "code", "execution_count": null, "id": "af99c275-b49e-4a50-9a04-e0690a6097d5", "metadata": {}, "outputs": [], "source": [ "# Why did we have to connect START and \"chat\", but not \"chat\" and END?" ] }, { "cell_type": "code", "execution_count": null, "id": "78fb4144-f5a0-4f65-b887-56fb4ede1ad4", "metadata": {}, "outputs": [], "source": [ "# graphs are also runnable, so we can invoke, stream, or batch them" ] }, { "cell_type": "code", "execution_count": null, "id": "e84f3d14-463a-4ab9-8fbd-04255ff53028", "metadata": {}, "outputs": [], "source": [ "# let's create a little helper that calls the graph and streams the results of the AIMessage" ] }, { "cell_type": "code", "execution_count": 68, "id": "ece0cc37-5c55-4a41-96b1-df62de1b6c85", "metadata": {}, "outputs": [], "source": [ "from langchain_core.messages import AIMessage\n", "\n", "def stream_graph_updates(user_input: str):\n", " for chunk, metadata in graph.stream({\"messages\": [{\"role\": \"user\", \"content\": user_input}]}, stream_mode=\"messages\"):\n", " if isinstance(chunk, AIMessage): # Filter to just model responses\n", " print(chunk.content, end=\"|\")" ] }, { "cell_type": "code", "execution_count": null, "id": "9a6659fc-b47a-4321-a57f-350a84eae40a", "metadata": {}, "outputs": [], "source": [ "# now lets put that in a loop so we can call it over and over again until we are done with the conversation" ] }, { "cell_type": "code", "execution_count": 69, "id": "edb7cec3-5b8f-410d-8fc7-f809f56d3f48", "metadata": {}, "outputs": [ { "name": "stdin", "output_type": "stream", "text": [ "User: Hi my name is Scott\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "|Hi| Scott|!| How| can| I| assist| you| today|?||" ] }, { "name": "stdin", "output_type": "stream", "text": [ "User: what is my name?\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "|I'm| sorry|,| but| I| don't| have| access| to| personal| information| about| users|.| Therefore|,| I| don't| know| your| name| unless| you| tell| me|.| How| can| I| assist| you| today|?||" ] }, { "name": "stdin", "output_type": "stream", "text": [ "User: q\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Goodbye!\n" ] } ], "source": [ "while True:\n", " user_input = input(\"User: \")\n", " if user_input.lower() in [\"quit\", \"exit\", \"q\"]:\n", " print(\"Goodbye!\")\n", " break\n", " stream_graph_updates(user_input)" ] }, { "cell_type": "code", "execution_count": null, "id": "c46c9e54-a5e4-4d57-9b0f-9d6a54d1d3cd", "metadata": {}, "outputs": [], "source": [ "# What the hell! After all that work!?!? Turns out once we have the graph, it is an easy fix" ] }, { "cell_type": "code", "execution_count": 70, "id": "6d471fb4-4bf1-4cd1-9d34-29b0fdebb889", "metadata": {}, "outputs": [], "source": [ "from langgraph.checkpoint.memory import MemorySaver" ] }, { "cell_type": "code", "execution_count": 71, "id": "2ab946f9-321d-48a2-9aa8-4974d1a5aea3", "metadata": {}, "outputs": [], "source": [ "graph = graph_builder.compile(checkpointer=MemorySaver())" ] }, { "cell_type": "code", "execution_count": null, "id": "cf52a72a-73dd-4be0-b957-f9a82c53181d", "metadata": {}, "outputs": [], "source": [ "# Name let's run the same loop as before " ] }, { "cell_type": "code", "execution_count": 72, "id": "b1713ddc-1da8-4201-8116-1efdc238ad19", "metadata": { "scrolled": true }, "outputs": [ { "name": "stdin", "output_type": "stream", "text": [ "User: Hi my name is Scott\n" ] }, { "ename": "ValueError", "evalue": "Checkpointer requires one or more of the following 'configurable' keys: thread_id, checkpoint_ns, checkpoint_id", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", "Cell \u001b[0;32mIn[72], line 6\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mGoodbye!\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 5\u001b[0m \u001b[38;5;28;01mbreak\u001b[39;00m\n\u001b[0;32m----> 6\u001b[0m \u001b[43mstream_graph_updates\u001b[49m\u001b[43m(\u001b[49m\u001b[43muser_input\u001b[49m\u001b[43m)\u001b[49m\n", "Cell \u001b[0;32mIn[68], line 4\u001b[0m, in \u001b[0;36mstream_graph_updates\u001b[0;34m(user_input)\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mstream_graph_updates\u001b[39m(user_input: \u001b[38;5;28mstr\u001b[39m):\n\u001b[0;32m----> 4\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m chunk, metadata \u001b[38;5;129;01min\u001b[39;00m graph\u001b[38;5;241m.\u001b[39mstream({\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmessages\u001b[39m\u001b[38;5;124m\"\u001b[39m: [{\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mrole\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124muser\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcontent\u001b[39m\u001b[38;5;124m\"\u001b[39m: user_input}]}, stream_mode\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmessages\u001b[39m\u001b[38;5;124m\"\u001b[39m):\n\u001b[1;32m 5\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(chunk, AIMessage): \u001b[38;5;66;03m# Filter to just model responses\u001b[39;00m\n\u001b[1;32m 6\u001b[0m \u001b[38;5;28mprint\u001b[39m(chunk\u001b[38;5;241m.\u001b[39mcontent, end\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m|\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n", "File \u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/langgraph/pregel/__init__.py:2436\u001b[0m, in \u001b[0;36mPregel.stream\u001b[0;34m(self, input, config, stream_mode, print_mode, output_keys, interrupt_before, interrupt_after, checkpoint_during, debug, subgraphs)\u001b[0m\n\u001b[1;32m 2420\u001b[0m run_manager \u001b[38;5;241m=\u001b[39m callback_manager\u001b[38;5;241m.\u001b[39mon_chain_start(\n\u001b[1;32m 2421\u001b[0m \u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[1;32m 2422\u001b[0m \u001b[38;5;28minput\u001b[39m,\n\u001b[1;32m 2423\u001b[0m name\u001b[38;5;241m=\u001b[39mconfig\u001b[38;5;241m.\u001b[39mget(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mrun_name\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mget_name()),\n\u001b[1;32m 2424\u001b[0m run_id\u001b[38;5;241m=\u001b[39mconfig\u001b[38;5;241m.\u001b[39mget(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mrun_id\u001b[39m\u001b[38;5;124m\"\u001b[39m),\n\u001b[1;32m 2425\u001b[0m )\n\u001b[1;32m 2426\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 2427\u001b[0m \u001b[38;5;66;03m# assign defaults\u001b[39;00m\n\u001b[1;32m 2428\u001b[0m (\n\u001b[1;32m 2429\u001b[0m stream_modes,\n\u001b[1;32m 2430\u001b[0m output_keys,\n\u001b[1;32m 2431\u001b[0m interrupt_before_,\n\u001b[1;32m 2432\u001b[0m interrupt_after_,\n\u001b[1;32m 2433\u001b[0m checkpointer,\n\u001b[1;32m 2434\u001b[0m store,\n\u001b[1;32m 2435\u001b[0m cache,\n\u001b[0;32m-> 2436\u001b[0m ) \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_defaults\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 2437\u001b[0m \u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 2438\u001b[0m \u001b[43m \u001b[49m\u001b[43mstream_mode\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream_mode\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 2439\u001b[0m \u001b[43m \u001b[49m\u001b[43mprint_mode\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mprint_mode\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 2440\u001b[0m \u001b[43m \u001b[49m\u001b[43moutput_keys\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43moutput_keys\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 2441\u001b[0m \u001b[43m \u001b[49m\u001b[43minterrupt_before\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43minterrupt_before\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 2442\u001b[0m \u001b[43m \u001b[49m\u001b[43minterrupt_after\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43minterrupt_after\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 2443\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 2444\u001b[0m \u001b[38;5;66;03m# set up subgraph checkpointing\u001b[39;00m\n\u001b[1;32m 2445\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mcheckpointer \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mTrue\u001b[39;00m:\n", "File \u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/langgraph/pregel/__init__.py:2330\u001b[0m, in \u001b[0;36mPregel._defaults\u001b[0;34m(self, config, stream_mode, print_mode, output_keys, interrupt_before, interrupt_after)\u001b[0m\n\u001b[1;32m 2328\u001b[0m checkpointer \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mcheckpointer\n\u001b[1;32m 2329\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m checkpointer \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m config\u001b[38;5;241m.\u001b[39mget(CONF):\n\u001b[0;32m-> 2330\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[1;32m 2331\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mCheckpointer requires one or more of the following \u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mconfigurable\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 2332\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mkeys: thread_id, checkpoint_ns, checkpoint_id\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 2333\u001b[0m )\n\u001b[1;32m 2334\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m CONFIG_KEY_STORE \u001b[38;5;129;01min\u001b[39;00m config\u001b[38;5;241m.\u001b[39mget(CONF, {}):\n\u001b[1;32m 2335\u001b[0m store: BaseStore \u001b[38;5;241m|\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;241m=\u001b[39m config[CONF][CONFIG_KEY_STORE]\n", "\u001b[0;31mValueError\u001b[0m: Checkpointer requires one or more of the following 'configurable' keys: thread_id, checkpoint_ns, checkpoint_id" ] } ], "source": [ "while True:\n", " user_input = input(\"User: \")\n", " if user_input.lower() in [\"quit\", \"exit\", \"q\"]:\n", " print(\"Goodbye!\")\n", " break\n", " stream_graph_updates(user_input)" ] }, { "cell_type": "code", "execution_count": null, "id": "fc09fc29-2c35-4a5a-9da4-96ab715c3ed8", "metadata": {}, "outputs": [], "source": [ "# Checkpointer requires one or more of the following 'configurable' keys" ] }, { "cell_type": "code", "execution_count": 73, "id": "c8364084-4e3c-4289-a30f-c7ea78779030", "metadata": {}, "outputs": [], "source": [ "config = {\"configurable\": {\"thread_id\": \"abc123\"}}" ] }, { "cell_type": "code", "execution_count": 74, "id": "7d9d58db-1749-44dd-befa-18168907c4b0", "metadata": {}, "outputs": [], "source": [ "# Modify the loop to pass the thread ID\n", "\n", "def stream_graph_updates(user_input: str, config):\n", " for chunk, metadata in graph.stream({\"messages\": [{\"role\": \"user\", \"content\": user_input}]}, config, stream_mode=\"messages\"):\n", " if isinstance(chunk, AIMessage): # Filter to just model responses\n", " print(chunk.content, end=\"|\")" ] }, { "cell_type": "code", "execution_count": 75, "id": "2aa57ff3-1104-47fc-9a5f-769342ea80ca", "metadata": { "scrolled": true }, "outputs": [], "source": [ "# while we are at it, let's put that loop in a function so we don't have to keep typing it\n", "def run_chat(config):\n", " while True:\n", " user_input = input(\"User: \")\n", " if user_input.lower() in [\"quit\", \"exit\", \"q\"]:\n", " print(\"Goodbye!\")\n", " break\n", " stream_graph_updates(user_input, config)" ] }, { "cell_type": "code", "execution_count": 76, "id": "fbcc4c99-95be-4906-af8e-6d954f3f8ced", "metadata": {}, "outputs": [ { "name": "stdin", "output_type": "stream", "text": [ "User: Hello my name is Scott\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "|Hello|,| Scott|!| How| can| I| assist| you| today|?||" ] }, { "name": "stdin", "output_type": "stream", "text": [ "User: what is my name?\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "|Your| name| is| Scott|.| How| can| I| help| you| today|?||" ] }, { "name": "stdin", "output_type": "stream", "text": [ "User: q\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Goodbye!\n" ] } ], "source": [ "config = {\"configurable\": {\"thread_id\": \"abc123\"}}\n", "run_chat(config)" ] }, { "cell_type": "code", "execution_count": null, "id": "9a78ae64-db7b-493a-b847-878c21a71416", "metadata": {}, "outputs": [], "source": [ "# Victory!!! ... but wait there is more ..." ] }, { "cell_type": "code", "execution_count": 77, "id": "b2a5a5af-dd57-402e-93a6-f7d1166e405f", "metadata": {}, "outputs": [ { "name": "stdin", "output_type": "stream", "text": [ "User: what is my name?\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "|I'm| sorry|,| but| I| don't| have| access| to| your| personal| information|,| so| I| can't| know| your| name|.| If| you'd| like| to| share| it|,| feel| free|!||" ] }, { "name": "stdin", "output_type": "stream", "text": [ "User: q\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Goodbye!\n" ] } ], "source": [ "config = {\"configurable\": {\"thread_id\": \"bcd234\"}}\n", "run_chat(config)" ] }, { "cell_type": "code", "execution_count": 78, "id": "2fd7d52e-51d0-4a56-8a4c-6b69b2853d21", "metadata": {}, "outputs": [ { "name": "stdin", "output_type": "stream", "text": [ "User: Hello\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "|Hello| again|,| Scott|!| How| can| I| assist| you| today|?||" ] }, { "name": "stdin", "output_type": "stream", "text": [ "User: q\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Goodbye!\n" ] } ], "source": [ "config = {\"configurable\": {\"thread_id\": \"abc123\"}}\n", "run_chat(config)" ] }, { "cell_type": "code", "execution_count": null, "id": "3eddd3a8-9dd4-4d66-9b1e-1eccb3e088ea", "metadata": {}, "outputs": [], "source": [ "# The checkpointer actually gives us other benefits, like letting us look at state" ] }, { "cell_type": "code", "execution_count": 79, "id": "4803d259-4eaf-4b4c-b8b5-5ab65eb95a31", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "StateSnapshot(values={'messages': [HumanMessage(content='Hello my name is Scott', additional_kwargs={}, response_metadata={}, id='63390ff0-a0e4-4669-a8c2-c60c3bc38162'), AIMessage(content='Hello, Scott! How can I assist you today?', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_62a23a81ef', 'service_tier': 'default'}, id='run--99c69d3d-be24-489a-a3ab-ddf5e91664f6'), HumanMessage(content='what is my name?', additional_kwargs={}, response_metadata={}, id='47cb732c-bf96-4d94-80f0-29e3d138ca4d'), AIMessage(content='Your name is Scott. How can I help you today?', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_34a54ae93c', 'service_tier': 'default'}, id='run--5d3edf7b-e9da-4fe6-b9ee-b95811270a6a'), HumanMessage(content='Hello', additional_kwargs={}, response_metadata={}, id='ecf4b932-6648-41ca-85fa-9217d6761cd9'), AIMessage(content='Hello again, Scott! How can I assist you today?', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_34a54ae93c', 'service_tier': 'default'}, id='run--37620662-8aa8-40be-85e2-13dc4c2cf003')]}, next=(), config={'configurable': {'thread_id': 'abc123', 'checkpoint_ns': '', 'checkpoint_id': '1f0545fe-afd3-67d0-8007-b79058b11787'}}, metadata={'source': 'loop', 'step': 7, 'parents': {}, 'thread_id': 'abc123'}, created_at='2025-06-28T20:38:58.957485+00:00', parent_config={'configurable': {'thread_id': 'abc123', 'checkpoint_ns': '', 'checkpoint_id': '1f0545fe-aa69-67c2-8006-f2ed961bdc98'}}, tasks=(), interrupts=())" ] }, "execution_count": 79, "metadata": {}, "output_type": "execute_result" } ], "source": [ "snapshot = graph.get_state(config)\n", "snapshot" ] }, { "cell_type": "code", "execution_count": null, "id": "f5c60b3f-d316-4eca-819e-3d17f2b2b50b", "metadata": {}, "outputs": [], "source": [ "# Let's look at one more limitation of these out of the box chat models - up to date info" ] }, { "cell_type": "code", "execution_count": 80, "id": "beb7dd56-28fd-4572-bd75-7f9c3585e6af", "metadata": {}, "outputs": [ { "name": "stdin", "output_type": "stream", "text": [ "User: What is the date today?\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "|Today's| date| is| November| |2|,| |202|3|.| How| can| I| help| you| further|?||" ] }, { "name": "stdin", "output_type": "stream", "text": [ "User: No I dont think so - it is June 28th 2025\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "|I| apologize| for| any| confusion|.| I| don|’t| have| real|-time| capabilities|,| so| I| provide| information| based| on| the| last| known| data|,| which| included| up| until| October| |202|3|.| If| today| is| indeed| June| |28|,| |202|5|,| then| that| is| the| correct| date| for| you|.| How| can| I| assist| you| further|?||" ] }, { "name": "stdin", "output_type": "stream", "text": [ "User: What is the weather like in San Diego today?\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "|I| can't| provide| real|-time| weather| updates|,| but| you| can| easily| check| the| current| weather| in| San| Diego| by| using| a| weather| website|,| app|,| or| asking| a| voice|-|activated| assistant|.| Is| there| anything| else| you'd| like| to| know|?||" ] }, { "name": "stdin", "output_type": "stream", "text": [ "User: q\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Goodbye!\n" ] } ], "source": [ "config = {\"configurable\": {\"thread_id\": \"abc123\"}}\n", "run_chat(config)" ] }, { "cell_type": "code", "execution_count": null, "id": "4b269a3e-45b8-4982-8e0b-7d739bbc2d0d", "metadata": {}, "outputs": [], "source": [ "# October 6, 2023 ???" ] }, { "cell_type": "markdown", "id": "80df6346-48b4-4e3b-b3b6-8039bc438114", "metadata": {}, "source": [ "# Tools" ] }, { "cell_type": "code", "execution_count": 81, "id": "58c383a5-9c49-43bd-a5af-a4ff5f89a91d", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['langchain.tools.BaseTool',\n", " 'langchain.tools.StructuredTool',\n", " 'langchain.tools.Tool',\n", " 'langchain.tools.base.BaseTool',\n", " 'langchain.tools.base.StructuredTool',\n", " 'langchain.tools.base.Tool']" ] }, "execution_count": 81, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[name for name, cls in all_runnables if name.startswith('langchain.tools')]" ] }, { "cell_type": "markdown", "id": "b6e01d5f-4949-4765-bdbe-f1d93fab180c", "metadata": {}, "source": [ "[Tool integrations](https://python.langchain.com/docs/integrations/tools/)" ] }, { "cell_type": "code", "execution_count": 82, "id": "723e32ce-1978-4122-a66b-9a1737f1f900", "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Requirement already satisfied: langchain-tavily in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (0.2.4)\n", "Collecting langchain-tavily\n", " Downloading langchain_tavily-0.2.5-py3-none-any.whl.metadata (21 kB)\n", "Requirement already satisfied: aiohttp<4.0.0,>=3.11.14 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from langchain-tavily) (3.12.13)\n", "Requirement already satisfied: langchain<0.4.0,>=0.3.20 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from langchain-tavily) (0.3.26)\n", "Requirement already satisfied: langchain-core<0.4.0,>=0.3.15 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from langchain-tavily) (0.3.66)\n", "Requirement already satisfied: requests<3.0.0,>=2.32.3 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from langchain-tavily) (2.32.3)\n", "Requirement already satisfied: aiohappyeyeballs>=2.5.0 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from aiohttp<4.0.0,>=3.11.14->langchain-tavily) (2.6.1)\n", "Requirement already satisfied: aiosignal>=1.1.2 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from aiohttp<4.0.0,>=3.11.14->langchain-tavily) (1.3.1)\n", "Requirement already satisfied: async-timeout<6.0,>=4.0 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from aiohttp<4.0.0,>=3.11.14->langchain-tavily) (4.0.2)\n", "Requirement already satisfied: attrs>=17.3.0 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from aiohttp<4.0.0,>=3.11.14->langchain-tavily) (22.2.0)\n", "Requirement already satisfied: frozenlist>=1.1.1 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from aiohttp<4.0.0,>=3.11.14->langchain-tavily) (1.3.3)\n", "Requirement already satisfied: multidict<7.0,>=4.5 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from aiohttp<4.0.0,>=3.11.14->langchain-tavily) (6.0.4)\n", "Requirement already satisfied: propcache>=0.2.0 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from aiohttp<4.0.0,>=3.11.14->langchain-tavily) (0.3.2)\n", "Requirement already satisfied: yarl<2.0,>=1.17.0 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from aiohttp<4.0.0,>=3.11.14->langchain-tavily) (1.20.1)\n", "Requirement already satisfied: langchain-text-splitters<1.0.0,>=0.3.8 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from langchain<0.4.0,>=0.3.20->langchain-tavily) (0.3.8)\n", "Requirement already satisfied: langsmith>=0.1.17 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from langchain<0.4.0,>=0.3.20->langchain-tavily) (0.3.45)\n", "Requirement already satisfied: pydantic<3.0.0,>=2.7.4 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from langchain<0.4.0,>=0.3.20->langchain-tavily) (2.11.7)\n", "Requirement already satisfied: SQLAlchemy<3,>=1.4 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from langchain<0.4.0,>=0.3.20->langchain-tavily) (2.0.22)\n", "Requirement already satisfied: PyYAML>=5.3 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from langchain<0.4.0,>=0.3.20->langchain-tavily) (6.0.2)\n", "Requirement already satisfied: tenacity!=8.4.0,<10.0.0,>=8.1.0 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from langchain-core<0.4.0,>=0.3.15->langchain-tavily) (8.5.0)\n", "Requirement already satisfied: jsonpatch<2.0,>=1.33 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from langchain-core<0.4.0,>=0.3.15->langchain-tavily) (1.33)\n", "Requirement already satisfied: packaging<25,>=23.2 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from langchain-core<0.4.0,>=0.3.15->langchain-tavily) (24.2)\n", "Requirement already satisfied: typing-extensions>=4.7 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from langchain-core<0.4.0,>=0.3.15->langchain-tavily) (4.12.2)\n", "Requirement already satisfied: jsonpointer>=1.9 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from jsonpatch<2.0,>=1.33->langchain-core<0.4.0,>=0.3.15->langchain-tavily) (3.0.0)\n", "Requirement already satisfied: annotated-types>=0.6.0 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from pydantic<3.0.0,>=2.7.4->langchain<0.4.0,>=0.3.20->langchain-tavily) (0.7.0)\n", "Requirement already satisfied: pydantic-core==2.33.2 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from pydantic<3.0.0,>=2.7.4->langchain<0.4.0,>=0.3.20->langchain-tavily) (2.33.2)\n", "Requirement already satisfied: typing-inspection>=0.4.0 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from pydantic<3.0.0,>=2.7.4->langchain<0.4.0,>=0.3.20->langchain-tavily) (0.4.1)\n", "Requirement already satisfied: charset-normalizer<4,>=2 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from requests<3.0.0,>=2.32.3->langchain-tavily) (2.1.1)\n", "Requirement already satisfied: idna<4,>=2.5 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from requests<3.0.0,>=2.32.3->langchain-tavily) (3.4)\n", "Requirement already satisfied: urllib3<3,>=1.21.1 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from requests<3.0.0,>=2.32.3->langchain-tavily) (1.26.13)\n", "Requirement already satisfied: certifi>=2017.4.17 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from requests<3.0.0,>=2.32.3->langchain-tavily) (2022.12.7)\n", "Requirement already satisfied: httpx<1,>=0.23.0 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from langsmith>=0.1.17->langchain<0.4.0,>=0.3.20->langchain-tavily) (0.28.1)\n", "Requirement already satisfied: orjson<4.0.0,>=3.9.14 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from langsmith>=0.1.17->langchain<0.4.0,>=0.3.20->langchain-tavily) (3.10.7)\n", "Requirement already satisfied: requests-toolbelt<2.0.0,>=1.0.0 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from langsmith>=0.1.17->langchain<0.4.0,>=0.3.20->langchain-tavily) (1.0.0)\n", "Requirement already satisfied: zstandard<0.24.0,>=0.23.0 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from langsmith>=0.1.17->langchain<0.4.0,>=0.3.20->langchain-tavily) (0.23.0)\n", "Requirement already satisfied: anyio in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from httpx<1,>=0.23.0->langsmith>=0.1.17->langchain<0.4.0,>=0.3.20->langchain-tavily) (3.7.1)\n", "Requirement already satisfied: httpcore==1.* in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from httpx<1,>=0.23.0->langsmith>=0.1.17->langchain<0.4.0,>=0.3.20->langchain-tavily) (1.0.6)\n", "Requirement already satisfied: h11<0.15,>=0.13 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from httpcore==1.*->httpx<1,>=0.23.0->langsmith>=0.1.17->langchain<0.4.0,>=0.3.20->langchain-tavily) (0.14.0)\n", "Requirement already satisfied: sniffio>=1.1 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from anyio->httpx<1,>=0.23.0->langsmith>=0.1.17->langchain<0.4.0,>=0.3.20->langchain-tavily) (1.3.0)\n", "Requirement already satisfied: exceptiongroup in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from anyio->httpx<1,>=0.23.0->langsmith>=0.1.17->langchain<0.4.0,>=0.3.20->langchain-tavily) (1.1.1)\n", "Downloading langchain_tavily-0.2.5-py3-none-any.whl (24 kB)\n", "Installing collected packages: langchain-tavily\n", " Attempting uninstall: langchain-tavily\n", " Found existing installation: langchain-tavily 0.2.4\n", " Uninstalling langchain-tavily-0.2.4:\n", " Successfully uninstalled langchain-tavily-0.2.4\n", "Successfully installed langchain-tavily-0.2.5\n", "Note: you may need to restart the kernel to use updated packages.\n" ] } ], "source": [ "pip install -U langchain-tavily" ] }, { "cell_type": "code", "execution_count": null, "id": "50ece80f-2513-4bee-89bc-d1dc59b4d0fd", "metadata": {}, "outputs": [], "source": [ "# NOTE You will need your own key" ] }, { "cell_type": "code", "execution_count": 83, "id": "fc983731-f7a9-4c06-911f-1781e665e1a5", "metadata": {}, "outputs": [], "source": [ "import os\n", "os.environ[\"TAVILY_API_KEY\"] = \"\"" ] }, { "cell_type": "code", "execution_count": 84, "id": "3665eca6-14e0-4d75-9358-a6c93e573e80", "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "TavilySearch(max_results=2, api_wrapper=TavilySearchAPIWrapper(tavily_api_key=SecretStr('**********')))" ] }, "execution_count": 84, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from langchain_tavily import TavilySearch\n", "\n", "tool = TavilySearch(max_results=2)\n", "tool" ] }, { "cell_type": "code", "execution_count": 85, "id": "0423b01e-0d1a-4cf8-b14a-f429dafe35cf", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(langchain_tavily.tavily_search.TavilySearch,\n", " langchain_core.tools.base.BaseTool,\n", " langchain_core.runnables.base.RunnableSerializable[Union[str, dict, ToolCall], Any],\n", " langchain_core.runnables.base.RunnableSerializable,\n", " langchain_core.load.serializable.Serializable,\n", " pydantic.main.BaseModel,\n", " langchain_core.runnables.base.Runnable,\n", " typing.Generic,\n", " abc.ABC,\n", " object)" ] }, "execution_count": 85, "metadata": {}, "output_type": "execute_result" } ], "source": [ "TavilySearch.__mro__" ] }, { "cell_type": "code", "execution_count": null, "id": "f62ffc99-a6a1-4454-9ba4-187189e99f85", "metadata": {}, "outputs": [], "source": [ "# Tools are also runnable" ] }, { "cell_type": "code", "execution_count": 86, "id": "d2aedcd8-8405-488b-b0f9-46af7cd2899d", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'query': \"What's a 'node' in LangGraph?\",\n", " 'follow_up_questions': None,\n", " 'answer': None,\n", " 'images': [],\n", " 'results': [{'title': 'Nodes and Edges | langchain-ai/langgraph-101 | DeepWiki',\n", " 'url': 'https://deepwiki.com/langchain-ai/langgraph-101/2.2-nodes-and-edges',\n", " 'content': 'Nodes and Edges | langchain-ai/langgraph-101 | DeepWiki Nodes and Edges Nodes and Edges What are Nodes and Edges? In LangGraph, a graph is composed of nodes connected by edges to form a directed workflow. Nodes are the workhorses of LangGraph - they are Python functions that receive the current graph state as input, perform operations, and return updates to that state. Edges define the flow of execution between nodes in a LangGraph. graph_builder.add_edge(\"retrieve_documents\", \"generate_response\") Conditional edges use a function to determine the next node based on the current state. Building a Graph with Nodes and Edges graph_builder.add_node(\"retrieve_documents\", retrieve_documents) graph_builder.add_edge(\"retrieve_documents\", \"generate_response\") When designing nodes and edges in LangGraph: Nodes and Edges What are Nodes and Edges? Building a Graph with Nodes and Edges',\n", " 'score': 0.8559598,\n", " 'raw_content': None},\n", " {'title': 'What is LangGraph? - GeeksforGeeks',\n", " 'url': 'https://www.geeksforgeeks.org/machine-learning/what-is-langgraph/',\n", " 'content': 'LangGraph is a Python library that helps you build applications like chatbots or AI agents by organizing their logic step-by-step using state machine model. This step configures your Gemini API key and then we create a simple function ask_gemini that takes user input, sends it to the Gemini model and returns the AI-generated response. Creates a state structure with three fields: question, classification and response which flows through the LangGraph. import matplotlib.pyplot as plt from langgraph.graph import StateGraph\\u200bbuilder = StateGraph(GraphState)builder.add_node(\"classify\", classify)builder.add_node(\"respond\", respond)builder.set_entry_point(\"classify\")builder.add_edge(\"classify\", \"respond\")builder.set_finish_point(\"respond\")app = builder.compile()\\u200bdef visualize_workflow(builder): G = nx.DiGraph()\\u200b for node in builder.nodes: G.add_node(node) for edge in builder.edges: G.add_edge(edge[0], edge[1])\\u200b pos = nx.spring_layout(G) nx.draw(G, pos, with_labels=True, node_size=3000, node_color=\"skyblue\", font_size=12, font_weight=\"bold\", arrows=True) plt.title(\"Langchain Workflow Visualization\") plt.show()\\u200bvisualize_workflow(builder)',\n", " 'score': 0.6991933,\n", " 'raw_content': None}],\n", " 'response_time': 1.92}" ] }, "execution_count": 86, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tool.invoke(\"What's a 'node' in LangGraph?\")" ] }, { "cell_type": "code", "execution_count": null, "id": "5ed1afec-bb55-4e47-8093-74c87e72fdc7", "metadata": {}, "outputs": [], "source": [ "# Now since we have created a tool, let bind that tool to our existing model to create a new model that knows about a tool" ] }, { "cell_type": "code", "execution_count": 87, "id": "0cdb34ad-1132-4b86-acff-78e71a62c711", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "langchain_openai.chat_models.base.ChatOpenAI" ] }, "execution_count": 87, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(model)" ] }, { "cell_type": "code", "execution_count": 88, "id": "93ac204f-8670-4466-8f79-274a877d4f9a", "metadata": {}, "outputs": [], "source": [ "chat_knows_tools = model.bind_tools([tool])" ] }, { "cell_type": "code", "execution_count": 89, "id": "cb4a22c5-c6be-4de3-be91-0937e766aa67", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "langchain_core.runnables.base.RunnableBinding" ] }, "execution_count": 89, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(chat_knows_tools)" ] }, { "cell_type": "code", "execution_count": 90, "id": "00d57be2-9bcf-4610-bb20-be7f10e86889", "metadata": {}, "outputs": [], "source": [ "def chatbot_tools_node(state: State):\n", " return {\"messages\": [chat_knows_tools.invoke(state[\"messages\"])]}" ] }, { "cell_type": "code", "execution_count": null, "id": "77704f91-691d-4f5c-a26b-6900766a1559", "metadata": {}, "outputs": [], "source": [ "# Now let's build a new graph that uses the model that knows about tools, as well as a node for the tool itself" ] }, { "cell_type": "code", "execution_count": 91, "id": "2aabf346-89e6-4ffa-a208-e0640bc99166", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 91, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from langgraph.prebuilt import ToolNode\n", "\n", "graph_builder = StateGraph(State)\n", "\n", "tool_node = ToolNode(tools=[tool])\n", "\n", "graph_builder.add_node(\"chat\", chatbot_tools_node)\n", "graph_builder.add_node(\"tools\", tool_node)" ] }, { "cell_type": "code", "execution_count": null, "id": "61d11272-07b2-4895-9dd9-13e01c10e75b", "metadata": {}, "outputs": [], "source": [ "# We need to connect these two nodes, but when do we know when we should call the tool? We need to build a router..." ] }, { "cell_type": "code", "execution_count": 92, "id": "7c7d1a6f-3c6f-4581-92b2-38a6e2a0174f", "metadata": {}, "outputs": [], "source": [ "from langgraph.graph import END\n", "\n", "# NOTE this is the same as \n", "# from langgraph.prebuilt import tools_condition\n", "def route_tools(\n", " state: State,\n", "):\n", " \"\"\"\n", " Use in the conditional_edge to route to the ToolNode if the last message\n", " has tool calls. Otherwise, route to the end.\n", " \"\"\"\n", " if isinstance(state, list):\n", " ai_message = state[-1]\n", " elif messages := state.get(\"messages\", []):\n", " ai_message = messages[-1]\n", " else:\n", " raise ValueError(f\"No messages found in input state to tool_edge: {state}\")\n", " if hasattr(ai_message, \"tool_calls\") and len(ai_message.tool_calls) > 0:\n", " return \"tools\"\n", " return END\n", "\n", "\n", "# The `tools_condition` function returns \"tools\" if the chatbot asks to use a tool, and \"END\" if\n", "# it is fine directly responding. This conditional routing defines the main agent loop.\n", "graph_builder.add_conditional_edges(\n", " \"chat\",\n", " route_tools, # or tools_condition\n", " # The following dictionary lets you tell the graph to interpret the condition's outputs as a specific node\n", " # It defaults to the identity function, but if you\n", " # want to use a node named something else apart from \"tools\",\n", " # You can update the value of the dictionary to something else\n", " # e.g., \"tools\": \"my_tools\"\n", " {\"tools\": \"tools\", END: END},\n", ")\n", "# Any time a tool is called, we return to the chatbot to decide the next step\n", "graph_builder.add_edge(\"tools\", \"chat\")\n", "graph_builder.add_edge(START, \"chat\")\n", "graph = graph_builder.compile()" ] }, { "cell_type": "code", "execution_count": 93, "id": "d305abe5-6d6f-43f2-8cb9-055e0456c5f1", "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "display(Image(graph.get_graph().draw_mermaid_png()))" ] }, { "cell_type": "code", "execution_count": 94, "id": "8ab8b3a6-921f-453e-80e5-c46eeffa199d", "metadata": {}, "outputs": [ { "name": "stdin", "output_type": "stream", "text": [ "User: What is the weather today in San Diego?\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "Assistant: \n", "\n", "Assistant: {\"query\": \"San Diego weather today\", \"follow_up_questions\": null, \"answer\": null, \"images\": [], \"results\": [{\"title\": \"Weather in San Diego, California\", \"url\": \"https://www.weatherapi.com/\", \"content\": \"{'location': {'name': 'San Diego', 'region': 'California', 'country': 'United States of America', 'lat': 32.7153, 'lon': -117.1564, 'tz_id': 'America/Los_Angeles', 'localtime_epoch': 1751144345, 'localtime': '2025-06-28 13:59'}, 'current': {'last_updated_epoch': 1751143500, 'last_updated': '2025-06-28 13:45', 'temp_c': 21.7, 'temp_f': 71.1, 'is_day': 1, 'condition': {'text': 'Partly Cloudy', 'icon': '//cdn.weatherapi.com/weather/64x64/day/116.png', 'code': 1003}, 'wind_mph': 9.2, 'wind_kph': 14.8, 'wind_degree': 273, 'wind_dir': 'W', 'pressure_mb': 1016.0, 'pressure_in': 30.0, 'precip_mm': 0.0, 'precip_in': 0.0, 'humidity': 71, 'cloud': 25, 'feelslike_c': 21.7, 'feelslike_f': 71.1, 'windchill_c': 23.7, 'windchill_f': 74.7, 'heatindex_c': 25.2, 'heatindex_f': 77.4, 'dewpoint_c': 16.2, 'dewpoint_f': 61.2, 'vis_km': 16.0, 'vis_miles': 9.0, 'uv': 11.0, 'gust_mph': 10.6, 'gust_kph': 17.0}}\", \"score\": 0.7936386, \"raw_content\": null}, {\"title\": \"Changes to Queens bus routes start Sunday as part of MTA network ...\", \"url\": \"https://us.headtopics.com/news/changes-to-queens-bus-routes-start-sunday-as-part-of-mta-70692064\", \"content\": \"San Diego's Weather Forecast for Sunday, June 8, 2025: Mostly cloudy Sunday, warming this week Moses Small joined ABC 10News as a Multimedia Journalist in 2022.\", \"score\": 0.51603514, \"raw_content\": null}], \"response_time\": 2.2}\n", "\n", "Assistant: Today in San Diego, the weather is partly cloudy with a temperature of around 71.1°F (21.7°C). The wind is blowing from the west at approximately 9.2 mph (14.8 km/h), and the humidity is at 71%. There is no expected precipitation and visibility is good at 16 km (9 miles).\n", "\n", "For more detailed information, you can check out [Weather API](https://www.weatherapi.com/).\n" ] }, { "name": "stdin", "output_type": "stream", "text": [ "User: q\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Goodbye!\n" ] } ], "source": [ "def stream_graph_updates(user_input: str):\n", " for event in graph.stream({\"messages\": [{\"role\": \"user\", \"content\": user_input}]}):\n", " for value in event.values():\n", " print(\"\\nAssistant:\", value[\"messages\"][-1].content)\n", "\n", "while True:\n", " user_input = input(\"User: \")\n", " if user_input.lower() in [\"quit\", \"exit\", \"q\"]:\n", " print(\"Goodbye!\")\n", " break\n", " stream_graph_updates(user_input)" ] }, { "cell_type": "markdown", "id": "00740696-34b0-4050-8dca-2c11153538c6", "metadata": {}, "source": [ "# Document Loaders and Text Splitters" ] }, { "cell_type": "code", "execution_count": null, "id": "9a0dbbaf-fb51-4345-b4e7-a1ecae36b418", "metadata": {}, "outputs": [], "source": [ "# All document loaders implement the BaseLoader interface" ] }, { "cell_type": "code", "execution_count": 95, "id": "38eac2eb-1367-43a5-9f7a-f618576731ca", "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "['alazy_load', 'aload', 'lazy_load', 'load', 'load_and_split']" ] }, "execution_count": 95, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from langchain_core.document_loaders.base import BaseLoader\n", "dir_no_under(BaseLoader)" ] }, { "cell_type": "markdown", "id": "0a947e36-fff1-4fe7-84d2-54c96481b724", "metadata": {}, "source": [ "[All Document Loaders](https://python.langchain.com/docs/integrations/document_loaders)" ] }, { "cell_type": "code", "execution_count": 96, "id": "c1517abe-ab19-4767-beee-c975e9689b54", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Requirement already satisfied: pypdf in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (5.6.1)\n", "Requirement already satisfied: typing_extensions>=4.0 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from pypdf) (4.12.2)\n", "Note: you may need to restart the kernel to use updated packages.\n" ] } ], "source": [ "pip install pypdf" ] }, { "cell_type": "code", "execution_count": 97, "id": "cb7b7574-7a50-4061-a047-3fb18c1571ac", "metadata": {}, "outputs": [], "source": [ "from langchain_community.document_loaders import PyPDFLoader\n", "loader = PyPDFLoader('NIPS-2017-attention-is-all-you-need-Paper.pdf')\n", "pdf_docs = loader.load()" ] }, { "cell_type": "code", "execution_count": 98, "id": "0ac631c9-2675-4a1b-a460-52d80c7b5f01", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "11\n" ] } ], "source": [ "print(len(pdf_docs))" ] }, { "cell_type": "code", "execution_count": 99, "id": "2fe75d8b-1507-49d6-a397-dc4640ee8cb6", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Attention Is All You Need\n", "Ashish Vaswani∗\n", "Google Brain\n", "avaswani@google.com\n", "Noam Shazeer∗\n", "Google Brain\n", "noam@google.com\n", "Niki Parmar∗\n", "Google Research\n", "nikip@google.com\n", "Jakob Uszkoreit∗\n", "Google Research\n", "usz\n", "\n", "{'source': 'NIPS-2017-attention-is-all-you-need-Paper.pdf', 'page': 0}\n" ] } ], "source": [ "print(f\"{pdf_docs[0].page_content[:200]}\\n\")\n", "print(pdf_docs[0].metadata)" ] }, { "cell_type": "code", "execution_count": 100, "id": "c64a564c-6d51-47a8-8fd6-6c12d4803de4", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(langchain_core.documents.base.Document,\n", " langchain_core.documents.base.BaseMedia,\n", " langchain_core.load.serializable.Serializable,\n", " pydantic.main.BaseModel,\n", " abc.ABC,\n", " object)" ] }, "execution_count": 100, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from langchain_core.documents import Document\n", "Document.__mro__" ] }, { "cell_type": "code", "execution_count": 101, "id": "5e49b472-3762-4d97-9e6f-9cd592cf5ddd", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['construct',\n", " 'copy',\n", " 'dict',\n", " 'from_orm',\n", " 'get_lc_namespace',\n", " 'id',\n", " 'is_lc_serializable',\n", " 'json',\n", " 'lc_attributes',\n", " 'lc_id',\n", " 'lc_secrets',\n", " 'metadata',\n", " 'page_content',\n", " 'parse_file',\n", " 'parse_obj',\n", " 'parse_raw',\n", " 'schema',\n", " 'schema_json',\n", " 'to_json',\n", " 'to_json_not_implemented',\n", " 'type',\n", " 'update_forward_refs',\n", " 'validate']" ] }, "execution_count": 101, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[str(prop) for prop in dir_no_under(pdf_docs[0]) if not str(prop).startswith('model')]" ] }, { "cell_type": "code", "execution_count": null, "id": "cc810fce-937d-4a02-b0b3-2e7451ccb079", "metadata": {}, "outputs": [], "source": [ "# basically an id, metadata, page_content, and type, then methods that parse and to_json" ] }, { "cell_type": "code", "execution_count": null, "id": "a9970d2e-a1cf-4d40-bb82-c09f146f717f", "metadata": {}, "outputs": [], "source": [ "# If we want to store this document in a database, \n", "# Documents are often too big to store as a single embedding, so we need to break them up.\n", "# This is where text_splitters come into play" ] }, { "cell_type": "code", "execution_count": 102, "id": "6771579d-b42c-4087-9414-cd6cfe76a7e6", "metadata": {}, "outputs": [ { "ename": "NameError", "evalue": "name 'TextSplitter' is not defined", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", "Cell \u001b[0;32mIn[102], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m dir_no_under(\u001b[43mTextSplitter\u001b[49m)\n", "\u001b[0;31mNameError\u001b[0m: name 'TextSplitter' is not defined" ] } ], "source": [ "dir_no_under(TextSplitter)" ] }, { "cell_type": "code", "execution_count": null, "id": "8b3dc6cd-0ca9-4641-a1df-d8cf261dc390", "metadata": {}, "outputs": [], "source": [ "# let's look at the full list of text_splitters" ] }, { "cell_type": "code", "execution_count": 103, "id": "948ddf27-0447-49fc-86e2-53ca5af1e1b4", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Available TextSplitter subclasses:\n", "- CharacterTextSplitter\n", "- JSFrameworkTextSplitter\n", "- KonlpyTextSplitter\n", "- LatexTextSplitter\n", "- MarkdownTextSplitter\n", "- NLTKTextSplitter\n", "- PythonCodeTextSplitter\n", "- RecursiveCharacterTextSplitter\n", "- SentenceTransformersTokenTextSplitter\n", "- SpacyTextSplitter\n", "- TokenTextSplitter\n" ] } ], "source": [ "import langchain_text_splitters\n", "from langchain_text_splitters.base import TextSplitter\n", "import inspect\n", "\n", "splitters = []\n", "\n", "# Iterate through all attributes in the langchain_text_splitters module\n", "for attr_name in dir(langchain_text_splitters):\n", " attr = getattr(langchain_text_splitters, attr_name)\n", " if inspect.isclass(attr) and issubclass(attr, TextSplitter) and attr is not TextSplitter:\n", " splitters.append(attr.__name__)\n", "\n", "print(\"Available TextSplitter subclasses:\")\n", "for splitter in splitters:\n", " print(f\"- {splitter}\")" ] }, { "cell_type": "code", "execution_count": null, "id": "92fc5b64-fbdb-494c-adbb-af4a13754527", "metadata": {}, "outputs": [], "source": [ "# the recursive character text splitter is a good one to use in our case." ] }, { "cell_type": "code", "execution_count": 104, "id": "fe4d6063-85bf-40c5-b4ca-fe9b9e83f9d8", "metadata": {}, "outputs": [ { "ename": "NameError", "evalue": "name 'RecursiveCharacterTextSplitter' is not defined", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", "Cell \u001b[0;32mIn[104], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mRecursiveCharacterTextSplitter\u001b[49m\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__mro__\u001b[39m\n", "\u001b[0;31mNameError\u001b[0m: name 'RecursiveCharacterTextSplitter' is not defined" ] } ], "source": [ "RecursiveCharacterTextSplitter.__mro__" ] }, { "cell_type": "code", "execution_count": 105, "id": "a3953940-d0d0-4fbc-8cf5-6453f758c337", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "43" ] }, "execution_count": 105, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from langchain_text_splitters import RecursiveCharacterTextSplitter\n", "\n", "text_splitter = RecursiveCharacterTextSplitter(\n", " chunk_size=1000, chunk_overlap=200, add_start_index=True\n", ")\n", "all_splits = text_splitter.split_documents(pdf_docs)\n", "\n", "len(all_splits)" ] }, { "cell_type": "markdown", "id": "1727e5c5-809a-42b4-9457-ee2ea540eef7", "metadata": {}, "source": [ "# Embeddings" ] }, { "cell_type": "markdown", "id": "dd66df1e-f443-4807-9485-f62be2e60043", "metadata": {}, "source": [ "[Embedding models](https://python.langchain.com/docs/integrations/text_embedding/)" ] }, { "cell_type": "code", "execution_count": 106, "id": "5718af68-c11d-4b09-be8c-75a98b51790d", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['text-embedding-3-small', 'text-embedding-3-large', 'text-embedding-ada-002']" ] }, "execution_count": 106, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from openai import OpenAI\n", "\n", "# Instantiate the client (reads your OPENAI_API_KEY env var by default, defined above)\n", "client = OpenAI()\n", "\n", "# List all available models\n", "models = client.models.list()\n", "\n", "# Filter for embedding-capable models (usually names contain \"embedding\")\n", "embedding_models = [m.id for m in models.data if \"embedding\" in m.id]\n", "embedding_models" ] }, { "cell_type": "code", "execution_count": 107, "id": "6b5d159e-4a67-4a27-9736-359335093f5c", "metadata": {}, "outputs": [], "source": [ "from langchain_openai import OpenAIEmbeddings" ] }, { "cell_type": "code", "execution_count": null, "id": "a01f99ad-c1fb-4a67-9d35-113908a55261", "metadata": {}, "outputs": [], "source": [ "OpenAIEmbeddings.__mro__" ] }, { "cell_type": "code", "execution_count": 108, "id": "484d4408-6afd-45e1-a437-06d1aa25ec46", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['aembed_documents', 'aembed_query', 'embed_documents', 'embed_query']" ] }, "execution_count": 108, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from langchain_core.embeddings.embeddings import Embeddings\n", "dir_no_under(Embeddings)" ] }, { "cell_type": "code", "execution_count": 109, "id": "06c53b5c-3f3e-4fcd-ad9a-271ac6557b50", "metadata": {}, "outputs": [], "source": [ "embeddings = OpenAIEmbeddings(model=\"text-embedding-ada-002\")" ] }, { "cell_type": "code", "execution_count": 110, "id": "03f61047-55a2-4a39-8c03-becbb2b5ed12", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Generated vectors of length 1536\n", "\n", "[-0.03526603430509567, 0.01840265840291977, 0.03633803501725197, -0.011888199485838413, 0.028669115155935287, 0.012259276583790779, 0.0005415831110440195, -0.015543992631137371, -0.001213730196468532, -0.03314952179789543]\n" ] } ], "source": [ "vector_1 = embeddings.embed_query(all_splits[0].page_content)\n", "vector_2 = embeddings.embed_query(all_splits[1].page_content)\n", "\n", "assert len(vector_1) == len(vector_2)\n", "print(f\"Generated vectors of length {len(vector_1)}\\n\")\n", "print(vector_1[:10])" ] }, { "cell_type": "markdown", "id": "24834f87-f60c-488a-adae-8f23c2cd26ee", "metadata": {}, "source": [ "# Vector stores" ] }, { "cell_type": "markdown", "id": "516a3abf-25b2-437f-b363-9d4e58ad97a3", "metadata": {}, "source": [ "[Vector store integrations](https://python.langchain.com/docs/integrations/vectorstores/)" ] }, { "cell_type": "code", "execution_count": 112, "id": "8981c5ee-530a-4a96-9ff3-bb0160ad4242", "metadata": {}, "outputs": [], "source": [ "from langchain_core.vectorstores import InMemoryVectorStore" ] }, { "cell_type": "code", "execution_count": null, "id": "58b3e57a-1200-4d1c-86a9-2639cafefbe7", "metadata": {}, "outputs": [], "source": [ "InMemoryVectorStore.__mro__" ] }, { "cell_type": "code", "execution_count": 113, "id": "4cab0c2e-9b7e-455e-a98f-2b7ecea620db", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['aadd_documents',\n", " 'aadd_texts',\n", " 'add_documents',\n", " 'add_texts',\n", " 'adelete',\n", " 'afrom_documents',\n", " 'afrom_texts',\n", " 'aget_by_ids',\n", " 'amax_marginal_relevance_search',\n", " 'amax_marginal_relevance_search_by_vector',\n", " 'as_retriever',\n", " 'asearch',\n", " 'asimilarity_search',\n", " 'asimilarity_search_by_vector',\n", " 'asimilarity_search_with_relevance_scores',\n", " 'asimilarity_search_with_score',\n", " 'delete',\n", " 'embeddings',\n", " 'from_documents',\n", " 'from_texts',\n", " 'get_by_ids',\n", " 'max_marginal_relevance_search',\n", " 'max_marginal_relevance_search_by_vector',\n", " 'search',\n", " 'similarity_search',\n", " 'similarity_search_by_vector',\n", " 'similarity_search_with_relevance_scores',\n", " 'similarity_search_with_score']" ] }, "execution_count": 113, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from langchain_core.vectorstores.base import VectorStore\n", "dir_no_under(VectorStore)" ] }, { "cell_type": "code", "execution_count": 114, "id": "1e6b3bab-b1a2-4f6a-b86c-9babefd3518a", "metadata": {}, "outputs": [], "source": [ "vector_store = InMemoryVectorStore(embeddings)" ] }, { "cell_type": "code", "execution_count": 115, "id": "1f64045b-6433-48f8-ab80-f6dadf70d2b1", "metadata": {}, "outputs": [], "source": [ "ids = vector_store.add_documents(documents=all_splits)" ] }, { "cell_type": "code", "execution_count": 116, "id": "adb39c68-a39d-4c0a-a742-f649a92f5e59", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "page_content='6 Results\n", "6.1 Machine Translation\n", "On the WMT 2014 English-to-German translation task, the big transformer model (Transformer (big)\n", "in Table 2) outperforms the best previously reported models (including ensembles) by more than 2.0\n", "BLEU, establishing a new state-of-the-art BLEU score of 28.4. The configuration of this model is\n", "listed in the bottom line of Table 3. Training took 3.5 days on 8 P100 GPUs. Even our base model\n", "surpasses all previously published models and ensembles, at a fraction of the training cost of any of\n", "the competitive models.\n", "On the WMT 2014 English-to-French translation task, our big model achieves a BLEU score of 41.0,\n", "outperforming all of the previously published single models, at less than 1/4 the training cost of the\n", "previous state-of-the-art model. The Transformer (big) model trained for English-to-French used\n", "dropout rate Pdrop = 0.1, instead of 0.3.\n", "For the base models, we used a single model obtained by averaging the last 5 checkpoints, which' metadata={'source': 'NIPS-2017-attention-is-all-you-need-Paper.pdf', 'page': 7, 'start_index': 864}\n" ] } ], "source": [ "results = vector_store.similarity_search(\n", " \"transformer model performing on machine translation\"\n", ")\n", "print(results[0])" ] }, { "cell_type": "code", "execution_count": null, "id": "fab7f4db-6146-4570-b7d1-d61dab52493c", "metadata": {}, "outputs": [], "source": [ "# A vector store is not a runnable, but there is a method called as_retriever which returns one." ] }, { "cell_type": "code", "execution_count": 117, "id": "6e793250-4704-4eab-88b2-5004f7b5d546", "metadata": {}, "outputs": [], "source": [ "retriever = vector_store.as_retriever(\n", " search_type=\"similarity\",\n", " search_kwargs={\"k\": 1},\n", ")" ] }, { "cell_type": "code", "execution_count": 118, "id": "df7e0c76-5f36-4672-b1e7-0ed97233afc0", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[Document(id='27a68934-d91f-4d51-8d52-c28f7a1190b2', metadata={'source': 'NIPS-2017-attention-is-all-you-need-Paper.pdf', 'page': 1, 'start_index': 754}, page_content='Attention mechanisms have become an integral part of compelling sequence modeling and transduc-\\ntion models in various tasks, allowing modeling of dependencies without regard to their distance in\\nthe input or output sequences [2, 16]. In all but a few cases [22], however, such attention mechanisms\\nare used in conjunction with a recurrent network.\\nIn this work we propose the Transformer, a model architecture eschewing recurrence and instead\\nrelying entirely on an attention mechanism to draw global dependencies between input and output.\\nThe Transformer allows for significantly more parallelization and can reach a new state of the art in\\ntranslation quality after being trained for as little as twelve hours on eight P100 GPUs.\\n2 Background\\nThe goal of reducing sequential computation also forms the foundation of the Extended Neural GPU\\n[20], ByteNet [15] and ConvS2S [8], all of which use convolutional neural networks as basic building')]\n", "\n", "[Document(id='5a81f67c-598f-4921-b97c-bc502b381724', metadata={'source': 'NIPS-2017-attention-is-all-you-need-Paper.pdf', 'page': 1, 'start_index': 3915}, page_content='connected layers for both the encoder and decoder, shown in the left and right halves of Figure 1,\\nrespectively.\\n3.1 Encoder and Decoder Stacks\\nEncoder: The encoder is composed of a stack of N = 6 identical layers. Each layer has two\\nsub-layers. The first is a multi-head self-attention mechanism, and the second is a simple, position-\\n2')]\n", "\n" ] } ], "source": [ "responses = retriever.batch([\n", " \"attention mechanism\",\n", " \"encoder decoders\"\n", "])\n", "\n", "for response in responses:\n", " print(response, end=\"\\n\\n\")" ] }, { "cell_type": "code", "execution_count": null, "id": "06c7076d-626c-4e2e-a9a7-3c9b7743038c", "metadata": {}, "outputs": [], "source": [ "# Putting it altogether" ] }, { "cell_type": "code", "execution_count": 119, "id": "ad70fcbe-b9c7-4cbe-933b-a9d235fccabf", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "USER_AGENT environment variable not set, consider setting it to identify your requests.\n", "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/langsmith/client.py:272: LangSmithMissingAPIKeyWarning: API key must be provided when using hosted LangSmith API\n", " warnings.warn(\n" ] } ], "source": [ "import bs4\n", "from langchain import hub\n", "from langchain_community.document_loaders import WebBaseLoader\n", "from langchain_core.documents import Document\n", "from langchain_text_splitters import RecursiveCharacterTextSplitter\n", "from langgraph.graph import START, StateGraph\n", "from typing_extensions import List, TypedDict\n", "\n", "# Load and chunk contents of the blog\n", "loader = WebBaseLoader(\n", " web_paths=(\"https://lilianweng.github.io/posts/2023-06-23-agent/\",),\n", " bs_kwargs=dict(\n", " parse_only=bs4.SoupStrainer(\n", " class_=(\"post-content\", \"post-title\", \"post-header\")\n", " )\n", " ),\n", ")\n", "docs = loader.load()\n", "\n", "text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)\n", "all_splits = text_splitter.split_documents(docs)\n", "\n", "# Index chunks\n", "_ = vector_store.add_documents(documents=all_splits)\n", "\n", "# Define prompt for question-answering\n", "# N.B. for non-US LangSmith endpoints, you may need to specify\n", "# api_url=\"https://api.smith.langchain.com\" in hub.pull.\n", "prompt = hub.pull(\"rlm/rag-prompt\")\n", "\n", "# Define state for application\n", "class State(TypedDict):\n", " question: str\n", " context: List[Document]\n", " answer: str\n", "\n", "\n", "# Define application steps\n", "def retrieve(state: State):\n", " retrieved_docs = vector_store.similarity_search(state[\"question\"])\n", " return {\"context\": retrieved_docs}\n", "\n", "\n", "def generate(state: State):\n", " docs_content = \"\\n\\n\".join(doc.page_content for doc in state[\"context\"])\n", " messages = prompt.invoke({\"question\": state[\"question\"], \"context\": docs_content})\n", " response = model.invoke(messages)\n", " return {\"answer\": response.content}\n", "\n", "\n", "# Compile application and test\n", "graph_builder = StateGraph(State).add_sequence([retrieve, generate])\n", "graph_builder.add_edge(START, \"retrieve\")\n", "graph = graph_builder.compile()" ] }, { "cell_type": "code", "execution_count": 120, "id": "ede8d1ed-b287-4b1c-a4cc-18f91af3fd0f", "metadata": {}, "outputs": [], "source": [ "final_response = graph.invoke({\"question\": \"What is Task Decomposition?\"})" ] }, { "cell_type": "code", "execution_count": 121, "id": "c9f161ad-fa2a-47af-b255-34d8ed34d820", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Task Decomposition is the process of breaking down a complex task into smaller, more manageable steps. Techniques like Chain of Thought (CoT) prompt models to think step by step, aiding in the simplification of tasks. Other methods include using task-specific instructions or integrating external planners for long-horizon planning.\n" ] } ], "source": [ "print(final_response[\"answer\"])" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.9" } }, "nbformat": 4, "nbformat_minor": 5 }