mirror of
https://github.com/hwchase17/langchain.git
synced 2026-02-06 17:20:16 +00:00
Compare commits
71 Commits
eugen/prov
...
rlm/rag_ev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc51af26b6 | ||
|
|
16152b3cdd | ||
|
|
c152fc5733 | ||
|
|
77eba10f47 | ||
|
|
5acc7ba622 | ||
|
|
aab075345e | ||
|
|
9fd36efdb5 | ||
|
|
b48add4353 | ||
|
|
c50099161b | ||
|
|
f7667c614b | ||
|
|
86cf1d3ee1 | ||
|
|
90184255f8 | ||
|
|
7997f3b7f8 | ||
|
|
97b2191e99 | ||
|
|
30b00090ef | ||
|
|
cc3c343673 | ||
|
|
7ea80bcb22 | ||
|
|
60c7a17781 | ||
|
|
4dd05791a2 | ||
|
|
d55a365c6c | ||
|
|
3cbc4693f5 | ||
|
|
676c68d318 | ||
|
|
b66a4f48fa | ||
|
|
450c458f8f | ||
|
|
ece008f117 | ||
|
|
c8391d4ff1 | ||
|
|
4be7ca7b4c | ||
|
|
7d7a08e458 | ||
|
|
0758da8940 | ||
|
|
b507cd222b | ||
|
|
160bcaeb93 | ||
|
|
4b6b0a87b6 | ||
|
|
6dc4f592ba | ||
|
|
38faa74c23 | ||
|
|
3a068b26f3 | ||
|
|
f1248f8d9a | ||
|
|
4808441d29 | ||
|
|
4f75b230ed | ||
|
|
e512d3c6a6 | ||
|
|
d83b720c40 | ||
|
|
525226fb0b | ||
|
|
e7b1a44c5b | ||
|
|
1b272fa2f4 | ||
|
|
93caa568f9 | ||
|
|
ad04585e30 | ||
|
|
a1b105ac00 | ||
|
|
20f5cd7c95 | ||
|
|
6786fa9186 | ||
|
|
9317df7f16 | ||
|
|
57bb940c17 | ||
|
|
fad0962643 | ||
|
|
5395c409cb | ||
|
|
6470b30173 | ||
|
|
b65a1d4cfd | ||
|
|
29282371db | ||
|
|
e6806a08d4 | ||
|
|
f78564d75c | ||
|
|
bac9fb9a7c | ||
|
|
cb29b42285 | ||
|
|
204a16addc | ||
|
|
7cf2d2759d | ||
|
|
2900720cd3 | ||
|
|
d2f4153fe6 | ||
|
|
eafd8c580b | ||
|
|
ec0273fc92 | ||
|
|
a889cd14f3 | ||
|
|
9d302c1b57 | ||
|
|
da707d0755 | ||
|
|
de938a4451 | ||
|
|
56fe4ab382 | ||
|
|
43a98592c1 |
4
.github/scripts/check_diff.py
vendored
4
.github/scripts/check_diff.py
vendored
@@ -53,6 +53,10 @@ if __name__ == "__main__":
|
||||
dirs_to_run["lint"].add("libs/standard-tests")
|
||||
dirs_to_run["test"].add("libs/partners/mistralai")
|
||||
dirs_to_run["test"].add("libs/partners/openai")
|
||||
dirs_to_run["test"].add("libs/partners/anthropic")
|
||||
dirs_to_run["test"].add("libs/partners/ai21")
|
||||
dirs_to_run["test"].add("libs/partners/fireworks")
|
||||
dirs_to_run["test"].add("libs/partners/groq")
|
||||
|
||||
elif file.startswith("libs/cli"):
|
||||
# todo: add cli makefile
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
|
||||
## Tutorials
|
||||
|
||||
### [LangChain v 0.1 by LangChain.ai](https://www.youtube.com/playlist?list=PLfaIDFEXuae0gBSJ9T0w7cu7iJZbH3T31)
|
||||
### [Build with Langchain - Advanced by LangChain.ai](https://www.youtube.com/playlist?list=PLfaIDFEXuae06tclDATrMYY0idsTdLg9v)
|
||||
### [LangGraph by LangChain.ai](https://www.youtube.com/playlist?list=PLfaIDFEXuae16n2TWUkKq5PgJ0w6Pkwtg)
|
||||
|
||||
### [by Greg Kamradt](https://www.youtube.com/playlist?list=PLqZXAkvF1bPNQER9mLmDbntNfSpzdDIU5)
|
||||
### [by Sam Witteveen](https://www.youtube.com/playlist?list=PL8motc6AQftk1Bs42EW45kwYbyJ4jOdiZ)
|
||||
### [by James Briggs](https://www.youtube.com/playlist?list=PLIUOU7oqGTLieV9uTIFMm6_4PXg-hlN6F)
|
||||
@@ -35,6 +39,7 @@
|
||||
- [Udacity](https://www.udacity.com/catalog/all/any-price/any-school/any-skill/any-difficulty/any-duration/any-type/relevance/page-1?searchValue=langchain)
|
||||
- [LinkedIn Learning](https://www.linkedin.com/search/results/learning/?keywords=langchain)
|
||||
- [edX](https://www.edx.org/search?q=langchain)
|
||||
- [freeCodeCamp](https://www.youtube.com/@freecodecamp/search?query=langchain)
|
||||
|
||||
## Short Tutorials
|
||||
|
||||
|
||||
@@ -293,7 +293,7 @@ embeddings = OllamaEmbeddings()
|
||||
Make sure you have the `cohere` package installed and the appropriate environment variables set (these are the same as needed for the LLM).
|
||||
|
||||
```python
|
||||
from langchain_community.embeddings import CohereEmbeddings
|
||||
from langchain_cohere.embeddings import CohereEmbeddings
|
||||
|
||||
embeddings = CohereEmbeddings()
|
||||
```
|
||||
|
||||
559
docs/docs/guides/productionization/evaluation/examples/rag.ipynb
Normal file
559
docs/docs/guides/productionization/evaluation/examples/rag.ipynb
Normal file
@@ -0,0 +1,559 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "2e7db2b1-8f9c-46bd-9c50-b6cfb0a38a22",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# RAG Evaluation\n",
|
||||
"[](https://colab.research.google.com/github/langchain-ai/langchain/blob/master/docs/docs/guides/evaluation/examples/rag.ipynb)\n",
|
||||
"\n",
|
||||
"RAG (Retrieval Augmented Generation) is one of the most popular LLM applications.\n",
|
||||
"\n",
|
||||
"For an in-depth review, see our RAG series of notebooks and videos [here](https://github.com/langchain-ai/rag-from-scratch)).\n",
|
||||
"\n",
|
||||
"## Types of RAG eval\n",
|
||||
"\n",
|
||||
"There are at least 4 types of RAG eval that users of typically interested in:\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"Each of these evals has something in common: it will compare text (e.g., answer vs reference answer, etc).\n",
|
||||
"\n",
|
||||
"We can use various built-in `LangChainStringEvaluator` types for this (see [here](https://docs.smith.langchain.com/evaluation/faq/evaluator-implementations#overview)).\n",
|
||||
"\n",
|
||||
"All `LangChainStringEvaluator` implementations can accept 3 inputs:\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"prediction: The prediction string.\n",
|
||||
"reference: The reference string.\n",
|
||||
"input: The input string.\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"Below, we will use this to perform eval.\n",
|
||||
"\n",
|
||||
"## RAG Chain \n",
|
||||
"\n",
|
||||
"To start, we build a RAG chain. "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "d809e9a0-44bc-4e9f-8eee-732ef077538c",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! pip install langchain-community langchain chromdb tiktoken"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "760cab79-2d5e-4324-ba4a-54b6f4094cb0",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"We build an `index` using a set of LangChain docs."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "6f7c0017-f4dd-4071-aa48-40957ffb4e9d",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"### INDEX\n",
|
||||
"\n",
|
||||
"from bs4 import BeautifulSoup as Soup\n",
|
||||
"from langchain_community.vectorstores import Chroma\n",
|
||||
"from langchain_openai import OpenAIEmbeddings\n",
|
||||
"from langchain.text_splitter import RecursiveCharacterTextSplitter\n",
|
||||
"from langchain_community.document_loaders.recursive_url_loader import RecursiveUrlLoader\n",
|
||||
"\n",
|
||||
"# Load\n",
|
||||
"url = \"https://python.langchain.com/docs/expression_language/\"\n",
|
||||
"loader = RecursiveUrlLoader(url=url, max_depth=20, extractor=lambda x: Soup(x, \"html.parser\").text)\n",
|
||||
"docs = loader.load()\n",
|
||||
"\n",
|
||||
"# Split\n",
|
||||
"text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)\n",
|
||||
"splits = text_splitter.split_documents(docs)\n",
|
||||
"\n",
|
||||
"# Embed\n",
|
||||
"vectorstore = Chroma.from_documents(documents=splits, embedding=OpenAIEmbeddings())\n",
|
||||
"\n",
|
||||
"# Index\n",
|
||||
"retriever = vectorstore.as_retriever()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "c365fb82-78a6-40b6-bd59-daaa1e79d6c8",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Next, we build a `RAG chain` that returns an `answer` and the retrieved documents as `contexts`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "68e249d7-bc6c-4631-b099-6daaeeddf38a",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"### RAG \n",
|
||||
"\n",
|
||||
"import openai\n",
|
||||
"from langsmith import traceable\n",
|
||||
"from langsmith.wrappers import wrap_openai\n",
|
||||
"\n",
|
||||
"class RagBot:\n",
|
||||
" def __init__(self, retriever, model: str = \"gpt-4-turbo-preview\"):\n",
|
||||
" self._retriever = retriever\n",
|
||||
" # Wrapping the client instruments the LLM\n",
|
||||
" self._client = wrap_openai(openai.Client())\n",
|
||||
" self._model = model\n",
|
||||
"\n",
|
||||
" @traceable\n",
|
||||
" def get_answer(self, question: str):\n",
|
||||
" similar = self._retriever.invoke(question)\n",
|
||||
" response = self._client.chat.completions.create(\n",
|
||||
" model=self._model,\n",
|
||||
" messages=[\n",
|
||||
" {\n",
|
||||
" \"role\": \"system\",\n",
|
||||
" \"content\": \"You are a helpful AI assistant.\"\n",
|
||||
" \" Use the following docs to help answer the user's question.\\n\\n\"\n",
|
||||
" f\"## Docs\\n\\n{similar}\",\n",
|
||||
" },\n",
|
||||
" {\"role\": \"user\", \"content\": question},\n",
|
||||
" ],\n",
|
||||
" )\n",
|
||||
" \n",
|
||||
" # Evaluators will expect \"answer\" and \"contexts\"\n",
|
||||
" return {\n",
|
||||
" \"answer\": response.choices[0].message.content,\n",
|
||||
" \"contexts\": [str(doc) for doc in similar],\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
"rag_bot = RagBot(retriever)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "6101d155-a1ab-460c-8c3e-f1f44e09a8b7",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'LangChain Expression Language (LCEL) is a declarative language that simplifies the composition of chains for working with language models and related '"
|
||||
]
|
||||
},
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"response = rag_bot.get_answer(\"What is LCEL?\")\n",
|
||||
"response[\"answer\"][:150]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "432e8ec7-a085-4224-ad38-0087e1d553f1",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## RAG Dataset \n",
|
||||
"\n",
|
||||
"Next, we build a dataset of QA pairs based upon the [documentation](https://python.langchain.com/docs/expression_language/) that we indexed."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "22f0daeb-6a61-4f8d-a4fc-4c7d22b6dc61",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"os.environ['LANGCHAIN_TRACING_V2'] = 'true'\n",
|
||||
"os.environ['LANGCHAIN_ENDPOINT'] = 'https://api.smith.langchain.com'\n",
|
||||
"os.environ['LANGCHAIN_API_KEY'] = <your-api-key>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "0f29304f-d79b-40e9-988a-343732102af9",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langsmith import Client \n",
|
||||
"\n",
|
||||
"# QA\n",
|
||||
"inputs = [\n",
|
||||
" \"How can I directly pass a string to a runnable and use it to construct the input needed for my prompt?\",\n",
|
||||
" \"How can I make the output of my LCEL chain a string?\",\n",
|
||||
" \"How can I apply a custom function to one of the inputs of an LCEL chain?\"\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"outputs = [\n",
|
||||
" \"Use RunnablePassthrough. from langchain_core.runnables import RunnableParallel, RunnablePassthrough; from langchain_core.prompts import ChatPromptTemplate; from langchain_openai import ChatOpenAI; prompt = ChatPromptTemplate.from_template('Tell a joke about: {input}'); model = ChatOpenAI(); runnable = ({'input' : RunnablePassthrough()} | prompt | model); runnable.invoke('flowers')\",\n",
|
||||
" \"Use StrOutputParser. from langchain_openai import ChatOpenAI; from langchain_core.prompts import ChatPromptTemplate; from langchain_core.output_parsers import StrOutputParser; prompt = ChatPromptTemplate.from_template('Tell me a short joke about {topic}'); model = ChatOpenAI(model='gpt-3.5-turbo') #gpt-4 or other LLMs can be used here; output_parser = StrOutputParser(); chain = prompt | model | output_parser\",\n",
|
||||
" \"Use RunnableLambda with itemgetter to extract the relevant key. from operator import itemgetter; from langchain_core.prompts import ChatPromptTemplate; from langchain_core.runnables import RunnableLambda; from langchain_openai import ChatOpenAI; def length_function(text): return len(text); chain = ({'prompt_input': itemgetter('foo') | RunnableLambda(length_function),} | prompt | model); chain.invoke({'foo':'hello world'})\"\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"qa_pairs = [{\"question\": q, \"answer\": a} for q, a in zip(inputs, outputs)]\n",
|
||||
"\n",
|
||||
"# Create dataset\n",
|
||||
"client = Client()\n",
|
||||
"dataset_name = \"RAG_test_LCEL\"\n",
|
||||
"dataset = client.create_dataset(\n",
|
||||
" dataset_name=dataset_name,\n",
|
||||
" description=\"QA pairs about LCEL.\",\n",
|
||||
")\n",
|
||||
"client.create_examples(\n",
|
||||
" inputs=[{\"question\": q} for q in inputs],\n",
|
||||
" outputs=[{\"answer\": a} for a in outputs],\n",
|
||||
" dataset_id=dataset.id,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "92cf3a0f-621f-468d-818d-a6f2d4b53823",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## RAG Evaluators\n",
|
||||
"\n",
|
||||
"### Type 1: Reference Answer\n",
|
||||
"\n",
|
||||
"First, lets consider the case in which we want to compare our RAG chain answer to a reference answer.\n",
|
||||
"\n",
|
||||
"This is shown on the far right (blue) in the top figure.\n",
|
||||
"\n",
|
||||
"#### Eval flow\n",
|
||||
"\n",
|
||||
"We will use a `LangChainStringEvaluator`, as mentioned above.\n",
|
||||
"\n",
|
||||
"For comparing questions and answers, common built-in `LangChainStringEvaluator` options are `QA` and `CoTQA` [here different evaluators](https://docs.smith.langchain.com/evaluation/faq/evaluator-implementations).\n",
|
||||
"\n",
|
||||
"We will use `CoT_QA` as an LLM-as-judge evaluator, which uses the eval prompt defined [here](https://smith.langchain.com/hub/langchain-ai/cot_qa).\n",
|
||||
"\n",
|
||||
"But, all `LangChainStringEvaluator` expose a common interface to pass your inputs:\n",
|
||||
"\n",
|
||||
"1. `question` from the dataset -> `input` \n",
|
||||
"2. `answer` from the dataset -> `reference` \n",
|
||||
"3. `answer` from the LLM -> `prediction` \n",
|
||||
"\n",
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "1cbe0b4a-2a30-4f40-b3aa-5cc67c6a7802",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# RAG chain\n",
|
||||
"def predict_rag_answer(example: dict):\n",
|
||||
" \"\"\"Use this for answer evaluation\"\"\"\n",
|
||||
" response = rag_bot.get_answer(example[\"question\"])\n",
|
||||
" return {\"answer\": response[\"answer\"]}\n",
|
||||
"\n",
|
||||
"def predict_rag_answer_with_context(example: dict):\n",
|
||||
" \"\"\"Use this for evaluation of retrieved documents and hallucinations\"\"\"\n",
|
||||
" response = rag_bot.get_answer(example[\"question\"])\n",
|
||||
" return {\"answer\": response[\"answer\"], \"contexts\": response[\"contexts\"]}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"id": "a7a3827d-a92f-4a7a-a572-5123fbd9c334",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"View the evaluation results for experiment: 'rag-qa-oai-e8604ab3' at:\n",
|
||||
"https://smith.langchain.com/o/1fa8b1f4-fcb9-4072-9aa9-983e35ad61b8/datasets/368734fb-7c14-4e1f-b91a-50d52cb58a07/compare?selectedSessions=a176a91c-a5f0-42ab-b2f4-fedaa1cbf17d\n",
|
||||
"\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"application/vnd.jupyter.widget-view+json": {
|
||||
"model_id": "e459fbab745f4ce4bb399609910a807f",
|
||||
"version_major": 2,
|
||||
"version_minor": 0
|
||||
},
|
||||
"text/plain": [
|
||||
"0it [00:00, ?it/s]"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from langsmith.evaluation import LangChainStringEvaluator, evaluate\n",
|
||||
"\n",
|
||||
"# Evaluator \n",
|
||||
"qa_evalulator = [LangChainStringEvaluator(\"cot_qa\", \n",
|
||||
" prepare_data=lambda run, example: {\n",
|
||||
" \"prediction\": run.outputs[\"answer\"], \n",
|
||||
" \"reference\": run.outputs[\"contexts\"],\n",
|
||||
" \"input\": example.inputs[\"question\"],\n",
|
||||
" } \n",
|
||||
" ))]\n",
|
||||
"dataset_name = \"RAG_test_LCEL\"\n",
|
||||
"experiment_results = evaluate(\n",
|
||||
" predict_rag_answer,\n",
|
||||
" data=dataset_name,\n",
|
||||
" evaluators=qa_evalulator,\n",
|
||||
" experiment_prefix=\"rag-qa-oai\",\n",
|
||||
" metadata={\"variant\": \"LCEL context, gpt-3.5-turbo\"},\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "60ba4123-c691-4aa0-ba76-e567e8aaf09f",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Type 2: Answer Hallucination\n",
|
||||
"\n",
|
||||
"Second, lets consider the case in which we want to compare our RAG chain answer to the retrieved documents.\n",
|
||||
"\n",
|
||||
"This is shown in the red in the top figure.\n",
|
||||
"\n",
|
||||
"#### Eval flow\n",
|
||||
"\n",
|
||||
"We will use a `LangChainStringEvaluator`, as mentioned above.\n",
|
||||
"\n",
|
||||
"For comparing documents and answers, common built-in `LangChainStringEvaluator` options are `Criteria` [here](https://python.langchain.com/docs/guides/productionization/evaluation/string/criteria_eval_chain/#using-reference-labels) because we want to supply custom criteria.\n",
|
||||
"\n",
|
||||
"We will use `labeled_score_string` as an LLM-as-judge evaluator, which uses the eval prompt defined [here](https://smith.langchain.com/hub/wfh/labeled-score-string).\n",
|
||||
"\n",
|
||||
"Here, we only need to use two inputs of the `LangChainStringEvaluator` interface:\n",
|
||||
"\n",
|
||||
"1. `contexts` from LLM chain -> `reference` \n",
|
||||
"2. `answer` from the LLM chain -> `prediction` \n",
|
||||
"\n",
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 18,
|
||||
"id": "7f0872a5-e989-415d-9fed-5846efaa9488",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langsmith.evaluation import LangChainStringEvaluator, evaluate\n",
|
||||
"\n",
|
||||
"answer_hallucination_evaluator = LangChainStringEvaluator(\n",
|
||||
" \"labeled_score_string\", \n",
|
||||
" config={\n",
|
||||
" \"criteria\": { \n",
|
||||
" \"accuracy\": \"\"\"Is the Assistant's Answer grounded in the Ground Truth documentation? A score of 0 means that the\n",
|
||||
" Assistant answer contains is not at all based upon / grounded in the Groun Truth documentation. A score of 5 means \n",
|
||||
" that the Assistant answer contains some information (e.g., a hallucination) that is not captured in the Ground Truth \n",
|
||||
" documentation. A score of 10 means that the Assistant answer is fully based upon the in the Ground Truth documentation.\"\"\"\n",
|
||||
" },\n",
|
||||
" # If you want the score to be saved on a scale from 0 to 1\n",
|
||||
" \"normalize_by\": 10,\n",
|
||||
" },\n",
|
||||
" prepare_data=lambda run, example: {\n",
|
||||
" \"prediction\": run.outputs[\"answer\"], \n",
|
||||
" \"reference\": run.outputs[\"contexts\"],\n",
|
||||
" \"input\": example.inputs[\"question\"],\n",
|
||||
" } \n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 19,
|
||||
"id": "6d5bf61b-3903-4cde-9ecf-67f0e0874521",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"View the evaluation results for experiment: 'rag-qa-oai-hallucination-fad2e13c' at:\n",
|
||||
"https://smith.langchain.com/o/1fa8b1f4-fcb9-4072-9aa9-983e35ad61b8/datasets/368734fb-7c14-4e1f-b91a-50d52cb58a07/compare?selectedSessions=9a1e9e7d-cf87-4b89-baf6-f5498a160627\n",
|
||||
"\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"application/vnd.jupyter.widget-view+json": {
|
||||
"model_id": "891904d8d44444e98c6a03faa43e147a",
|
||||
"version_major": 2,
|
||||
"version_minor": 0
|
||||
},
|
||||
"text/plain": [
|
||||
"0it [00:00, ?it/s]"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"dataset_name = \"RAG_test_LCEL\"\n",
|
||||
" \n",
|
||||
"experiment_results = evaluate(\n",
|
||||
" predict_rag_answer_with_context,\n",
|
||||
" data=dataset_name,\n",
|
||||
" evaluators=[answer_hallucination_evaluator],\n",
|
||||
" experiment_prefix=\"rag-qa-oai-hallucination\",\n",
|
||||
" # Any experiment metadata can be specified here\n",
|
||||
" metadata={\n",
|
||||
" \"variant\": \"LCEL context, gpt-3.5-turbo\",\n",
|
||||
" },\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "480a27cb-1a31-4194-b160-8cdcfbf24eea",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Type 3: Document Relevance to Question\n",
|
||||
"\n",
|
||||
"Finally, lets consider the case in which we want to compare our RAG chain document retrieval to the question.\n",
|
||||
"\n",
|
||||
"This is shown in green in the top figure.\n",
|
||||
"\n",
|
||||
"#### Eval flow\n",
|
||||
"\n",
|
||||
"We will use a `LangChainStringEvaluator`, as mentioned above.\n",
|
||||
"\n",
|
||||
"For comparing documents and answers, common built-in `LangChainStringEvaluator` options are `Criteria` [here](https://python.langchain.com/docs/guides/productionization/evaluation/string/criteria_eval_chain/#using-reference-labels) because we want to supply custom criteria.\n",
|
||||
"\n",
|
||||
"We will use `labeled_score_string` as an LLM-as-judge evaluator, which uses the eval prompt defined [here](https://smith.langchain.com/hub/wfh/labeled-score-string).\n",
|
||||
"\n",
|
||||
"Here, we only need to use two inputs of the `LangChainStringEvaluator` interface:\n",
|
||||
"\n",
|
||||
"1. `question` from LLM chain -> `reference` \n",
|
||||
"2. `contexts` from the LLM chain -> `prediction` \n",
|
||||
"\n",
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 22,
|
||||
"id": "df247034-14ed-40b1-b313-b0fef7286546",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langsmith.evaluation import LangChainStringEvaluator, evaluate\n",
|
||||
"\n",
|
||||
"docs_relevance_evaluator = LangChainStringEvaluator(\n",
|
||||
" \"labeled_score_string\", \n",
|
||||
" config={\n",
|
||||
" \"criteria\": { \n",
|
||||
" \"accuracy\": \"\"\"The Assistant's Answer is a set of documents retrieved from a vectorstore. The Ground Truth is a question\n",
|
||||
" used for retrieval. You will score whether the Assistant's Answer (retrieved docs) are relevant to the Ground Truth \n",
|
||||
" question. A score of 0 means that the Assistant answer contains documents that are not at all relevant to the \n",
|
||||
" Ground Truth question. A score of 5 means that the Assistant answer contains some documents are relevant to the Ground Truth \n",
|
||||
" question. A score of 10 means that all of the Assistant answer documents are all relevant to the Ground Truth question\"\"\"\n",
|
||||
" },\n",
|
||||
" # If you want the score to be saved on a scale from 0 to 1\n",
|
||||
" \"normalize_by\": 10,\n",
|
||||
" },\n",
|
||||
" prepare_data=lambda run, example: {\n",
|
||||
" \"prediction\": run.outputs[\"contexts\"], \n",
|
||||
" \"reference\": example.inputs[\"question\"],\n",
|
||||
" \"input\": example.inputs[\"question\"],\n",
|
||||
" } \n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 23,
|
||||
"id": "cfe988dc-2aaa-42f4-93ff-c3c9fe6b3124",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"View the evaluation results for experiment: 'rag-qa-oai-doc-relevance-82244196' at:\n",
|
||||
"https://smith.langchain.com/o/1fa8b1f4-fcb9-4072-9aa9-983e35ad61b8/datasets/368734fb-7c14-4e1f-b91a-50d52cb58a07/compare?selectedSessions=3bbf09c9-69de-47ba-9d3c-7bcedf5cd48f\n",
|
||||
"\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"application/vnd.jupyter.widget-view+json": {
|
||||
"model_id": "4e4091f1053b4d34871aa87428297e12",
|
||||
"version_major": 2,
|
||||
"version_minor": 0
|
||||
},
|
||||
"text/plain": [
|
||||
"0it [00:00, ?it/s]"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"experiment_results = evaluate(\n",
|
||||
" predict_rag_answer_with_context,\n",
|
||||
" data=dataset_name,\n",
|
||||
" evaluators=[docs_relevance_evaluator],\n",
|
||||
" experiment_prefix=\"rag-qa-oai-doc-relevance\",\n",
|
||||
" # Any experiment metadata can be specified here\n",
|
||||
" metadata={\n",
|
||||
" \"variant\": \"LCEL context, gpt-3.5-turbo\",\n",
|
||||
" },\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "c2f09b6e-667a-47fe-b3f9-8634783f7666",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.8"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -42,7 +42,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install --upgrade --quiet langchain langchain-openai deepeval"
|
||||
"%pip install --upgrade --quiet langchain langchain-openai deepeval langchain-chroma"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -215,8 +215,8 @@
|
||||
"source": [
|
||||
"import requests\n",
|
||||
"from langchain.chains import RetrievalQA\n",
|
||||
"from langchain_chroma import Chroma\n",
|
||||
"from langchain_community.document_loaders import TextLoader\n",
|
||||
"from langchain_community.vectorstores import Chroma\n",
|
||||
"from langchain_openai import OpenAI, OpenAIEmbeddings\n",
|
||||
"from langchain_text_splitters import CharacterTextSplitter\n",
|
||||
"\n",
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -51,7 +51,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
@@ -261,31 +261,46 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from pprint import pprint\n",
|
||||
"\n",
|
||||
"from langchain_core.messages import HumanMessage\n",
|
||||
"from langchain_google_vertexai import HarmBlockThreshold, HarmCategory"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"{'is_blocked': False,\n",
|
||||
" 'safety_ratings': [{'category': 'HARM_CATEGORY_HARASSMENT',\n",
|
||||
"{'citation_metadata': None,\n",
|
||||
" 'is_blocked': False,\n",
|
||||
" 'safety_ratings': [{'blocked': False,\n",
|
||||
" 'category': 'HARM_CATEGORY_HATE_SPEECH',\n",
|
||||
" 'probability_label': 'NEGLIGIBLE'},\n",
|
||||
" {'category': 'HARM_CATEGORY_HATE_SPEECH',\n",
|
||||
" {'blocked': False,\n",
|
||||
" 'category': 'HARM_CATEGORY_DANGEROUS_CONTENT',\n",
|
||||
" 'probability_label': 'NEGLIGIBLE'},\n",
|
||||
" {'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT',\n",
|
||||
" {'blocked': False,\n",
|
||||
" 'category': 'HARM_CATEGORY_HARASSMENT',\n",
|
||||
" 'probability_label': 'NEGLIGIBLE'},\n",
|
||||
" {'category': 'HARM_CATEGORY_DANGEROUS_CONTENT',\n",
|
||||
" 'probability_label': 'NEGLIGIBLE'}]}\n"
|
||||
" {'blocked': False,\n",
|
||||
" 'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT',\n",
|
||||
" 'probability_label': 'NEGLIGIBLE'}],\n",
|
||||
" 'usage_metadata': {'candidates_token_count': 6,\n",
|
||||
" 'prompt_token_count': 12,\n",
|
||||
" 'total_token_count': 18}}\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from pprint import pprint\n",
|
||||
"\n",
|
||||
"from langchain_core.messages import HumanMessage\n",
|
||||
"from langchain_google_vertexai import ChatVertexAI, HarmBlockThreshold, HarmCategory\n",
|
||||
"\n",
|
||||
"human = \"Translate this sentence from English to French. I love programming.\"\n",
|
||||
"messages = [HumanMessage(content=human)]\n",
|
||||
"\n",
|
||||
@@ -315,18 +330,21 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"{'is_blocked': False,\n",
|
||||
" 'safety_attributes': {'Derogatory': 0.1,\n",
|
||||
" 'Finance': 0.3,\n",
|
||||
" 'Insult': 0.1,\n",
|
||||
" 'Sexual': 0.1}}\n"
|
||||
"{'errors': (),\n",
|
||||
" 'grounding_metadata': {'citations': [], 'search_queries': []},\n",
|
||||
" 'is_blocked': False,\n",
|
||||
" 'safety_attributes': [{'Derogatory': 0.1, 'Insult': 0.1, 'Sexual': 0.2}],\n",
|
||||
" 'usage_metadata': {'candidates_billable_characters': 88.0,\n",
|
||||
" 'candidates_token_count': 24.0,\n",
|
||||
" 'prompt_billable_characters': 58.0,\n",
|
||||
" 'prompt_token_count': 12.0}}\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -341,40 +359,149 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Function Calling with Gemini\n",
|
||||
"## Tool calling (a.k.a. function calling) with Gemini\n",
|
||||
"\n",
|
||||
"We can call Gemini models with tools."
|
||||
"We can pass tool definitions to Gemini models to get the model to invoke those tools when appropriate. This is useful not only for LLM-powered tool use but also for getting structured outputs out of models more generally.\n",
|
||||
"\n",
|
||||
"With `ChatVertexAI.bind_tools()`, we can easily pass in Pydantic classes, dict schemas, LangChain tools, or even functions as tools to the model. Under the hood these are converted to a Gemini tool schema, which looks like:\n",
|
||||
"```python\n",
|
||||
"{\n",
|
||||
" \"name\": \"...\", # tool name\n",
|
||||
" \"description\": \"...\", # tool description\n",
|
||||
" \"parameters\": {...} # tool input schema as JSONSchema\n",
|
||||
"}\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"AIMessage(content='', additional_kwargs={'function_call': {'name': 'GetWeather', 'arguments': '{\"location\": \"San Francisco, CA\"}'}}, response_metadata={'is_blocked': False, 'safety_ratings': [{'category': 'HARM_CATEGORY_HATE_SPEECH', 'probability_label': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_DANGEROUS_CONTENT', 'probability_label': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HARASSMENT', 'probability_label': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT', 'probability_label': 'NEGLIGIBLE', 'blocked': False}], 'citation_metadata': None, 'usage_metadata': {'prompt_token_count': 41, 'candidates_token_count': 7, 'total_token_count': 48}}, id='run-05e760dc-0682-4286-88e1-5b23df69b083-0', tool_calls=[{'name': 'GetWeather', 'args': {'location': 'San Francisco, CA'}, 'id': 'cd2499c4-4513-4059-bfff-5321b6e922d0'}])"
|
||||
]
|
||||
},
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from langchain.pydantic_v1 import BaseModel, Field\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class GetWeather(BaseModel):\n",
|
||||
" \"\"\"Get the current weather in a given location\"\"\"\n",
|
||||
"\n",
|
||||
" location: str = Field(..., description=\"The city and state, e.g. San Francisco, CA\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"llm = ChatVertexAI(model_name=\"gemini-pro\", temperature=0)\n",
|
||||
"llm_with_tools = llm.bind_tools([GetWeather])\n",
|
||||
"ai_msg = llm_with_tools.invoke(\n",
|
||||
" \"what is the weather like in San Francisco\",\n",
|
||||
")\n",
|
||||
"ai_msg"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"The tool calls can be access via the `AIMessage.tool_calls` attribute, where they are extracted in a model-agnostic format:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[{'name': 'GetWeather',\n",
|
||||
" 'args': {'location': 'San Francisco, CA'},\n",
|
||||
" 'id': 'cd2499c4-4513-4059-bfff-5321b6e922d0'}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"ai_msg.tool_calls"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"For a complete guide on tool calling [head here](/docs/modules/model_io/chat/function_calling/)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Structured outputs\n",
|
||||
"\n",
|
||||
"Many applications require structured model outputs. Tool calling makes it much easier to do this reliably. The [with_structured_outputs](https://api.python.langchain.com/en/latest/chat_models/langchain_google_vertexai.chat_models.ChatVertexAI.html) constructor provides a simple interface built on top of tool calling for getting structured outputs out of a model. For a complete guide on structured outputs [head here](/docs/modules/model_io/chat/structured_output/).\n",
|
||||
"\n",
|
||||
"### ChatVertexAI.with_structured_outputs()\n",
|
||||
"\n",
|
||||
"To get structured outputs from our Gemini model all we need to do is to specify a desired schema, either as a Pydantic class or as a JSON schema, "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"Person(name='Stefan', age=13)"
|
||||
]
|
||||
},
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"class Person(BaseModel):\n",
|
||||
" \"\"\"Save information about a person.\"\"\"\n",
|
||||
"\n",
|
||||
" name: str = Field(..., description=\"The person's name.\")\n",
|
||||
" age: int = Field(..., description=\"The person's age.\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"structured_llm = llm.with_structured_output(Person)\n",
|
||||
"structured_llm.invoke(\"Stefan is already 13 years old\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### [Legacy] Using `create_structured_runnable()`\n",
|
||||
"\n",
|
||||
"The legacy wasy to get structured outputs is using the `create_structured_runnable` constructor:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"MyModel(name='Erick', age=27)"
|
||||
]
|
||||
},
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.pydantic_v1 import BaseModel\n",
|
||||
"from langchain_google_vertexai import create_structured_runnable\n",
|
||||
"\n",
|
||||
"llm = ChatVertexAI(model_name=\"gemini-pro\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class MyModel(BaseModel):\n",
|
||||
" name: str\n",
|
||||
" age: int\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"chain = create_structured_runnable(MyModel, llm)\n",
|
||||
"chain = create_structured_runnable(Person, llm)\n",
|
||||
"chain.invoke(\"My name is Erick and I'm 27 years old\")"
|
||||
]
|
||||
},
|
||||
@@ -484,11 +611,21 @@
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "",
|
||||
"name": ""
|
||||
"display_name": "poetry-venv-2",
|
||||
"language": "python",
|
||||
"name": "poetry-venv-2"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.9.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
193
docs/docs/integrations/document_loaders/firecrawl.ipynb
Normal file
193
docs/docs/integrations/document_loaders/firecrawl.ipynb
Normal file
File diff suppressed because one or more lines are too long
118
docs/docs/integrations/document_loaders/glue_catalog.ipynb
Normal file
118
docs/docs/integrations/document_loaders/glue_catalog.ipynb
Normal file
@@ -0,0 +1,118 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "MwTWzDxYgbrR"
|
||||
},
|
||||
"source": [
|
||||
"# Glue Catalog\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"The [AWS Glue Data Catalog](https://docs.aws.amazon.com/en_en/glue/latest/dg/catalog-and-crawler.html) is a centralized metadata repository that allows you to manage, access, and share metadata about your data stored in AWS. It acts as a metadata store for your data assets, enabling various AWS services and your applications to query and connect to the data they need efficiently.\n",
|
||||
"\n",
|
||||
"When you define data sources, transformations, and targets in AWS Glue, the metadata about these elements is stored in the Data Catalog. This includes information about data locations, schema definitions, runtime metrics, and more. It supports various data store types, such as Amazon S3, Amazon RDS, Amazon Redshift, and external databases compatible with JDBC. It is also directly integrated with Amazon Athena, Amazon Redshift Spectrum, and Amazon EMR, allowing these services to directly access and query the data.\n",
|
||||
"\n",
|
||||
"The Langchain GlueCatalogLoader will get the schema of all tables inside the given Glue database in the same format as Pandas dtype."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Setting up\n",
|
||||
"\n",
|
||||
"- Follow [instructions to set up an AWS accoung](https://docs.aws.amazon.com/athena/latest/ug/setting-up.html).\n",
|
||||
"- Install the boto3 library: `pip install boto3`\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Example"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "076NLjfngoWJ"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_community.document_loaders.glue_catalog import GlueCatalogLoader"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "XpMRQwU9gu44"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"database_name = \"my_database\"\n",
|
||||
"profile_name = \"my_profile\"\n",
|
||||
"\n",
|
||||
"loader = GlueCatalogLoader(\n",
|
||||
" database=database_name,\n",
|
||||
" profile_name=profile_name,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"schemas = loader.load()\n",
|
||||
"print(schemas)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Example with table filtering\n",
|
||||
"\n",
|
||||
"Table filtering allows you to selectively retrieve schema information for a specific subset of tables within a Glue database. Instead of loading the schemas for all tables, you can use the `table_filter` argument to specify exactly which tables you're interested in."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_community.document_loaders.glue_catalog import GlueCatalogLoader"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"database_name = \"my_database\"\n",
|
||||
"profile_name = \"my_profile\"\n",
|
||||
"table_filter = [\"table1\", \"table2\", \"table3\"]\n",
|
||||
"\n",
|
||||
"loader = GlueCatalogLoader(\n",
|
||||
" database=database_name, profile_name=profile_name, table_filter=table_filter\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"schemas = loader.load()\n",
|
||||
"print(schemas)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -39,7 +39,7 @@
|
||||
],
|
||||
"source": [
|
||||
"# Uncomment this to install psychicapi if you don't already have it installed\n",
|
||||
"!poetry run pip -q install psychicapi"
|
||||
"!poetry run pip -q install psychicapi langchain-chroma"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -78,7 +78,7 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.chains import RetrievalQAWithSourcesChain\n",
|
||||
"from langchain_community.vectorstores import Chroma\n",
|
||||
"from langchain_chroma import Chroma\n",
|
||||
"from langchain_openai import OpenAI, OpenAIEmbeddings\n",
|
||||
"from langchain_text_splitters import CharacterTextSplitter"
|
||||
]
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
"source": [
|
||||
"# WebBaseLoader\n",
|
||||
"\n",
|
||||
"This covers how to use `WebBaseLoader` to load all text from `HTML` webpages into a document format that we can use downstream. For more custom logic for loading webpages look at some child class examples such as `IMSDbLoader`, `AZLyricsLoader`, and `CollegeConfidentialLoader`"
|
||||
"This covers how to use `WebBaseLoader` to load all text from `HTML` webpages into a document format that we can use downstream. For more custom logic for loading webpages look at some child class examples such as `IMSDbLoader`, `AZLyricsLoader`, and `CollegeConfidentialLoader`. \n",
|
||||
"\n",
|
||||
"If you don't want to worry about website crawling, bypassing JS-blocking sites, and data cleaning, consider using `FireCrawlLoader`.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -277,4 +279,4 @@
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@
|
||||
"source": [
|
||||
"## Uploading Hugging Face model to SageMaker endpoint\n",
|
||||
"\n",
|
||||
"Refer to [this article](https://www.philschmid.de/custom-inference-huggingface-sagemaker) for general guideline. Here is a simple `inference.py` for creating an endpoint that works with `SagemakerEndpointCrossEncoder`.\n",
|
||||
"Here is a sample `inference.py` for creating an endpoint that works with `SagemakerEndpointCrossEncoder`. For more details with step-by-step guidance, refer to [this article](https://huggingface.co/blog/kchoe/deploy-any-huggingface-model-to-sagemaker). \n",
|
||||
"\n",
|
||||
"It downloads Hugging Face model on the fly, so you do not need to keep the model artifacts such as `pytorch_model.bin` in your `model.tar.gz`."
|
||||
]
|
||||
|
||||
@@ -347,7 +347,7 @@
|
||||
"from langchain_core.messages import HumanMessage\n",
|
||||
"from langchain_google_vertexai import ChatVertexAI\n",
|
||||
"\n",
|
||||
"llm = ChatVertexAI(model_name=\"gemini-ultra-vision\")\n",
|
||||
"llm = ChatVertexAI(model_name=\"gemini-pro-vision\")\n",
|
||||
"\n",
|
||||
"image_message = {\n",
|
||||
" \"type\": \"image_url\",\n",
|
||||
|
||||
@@ -124,7 +124,7 @@
|
||||
"In this example, we’ll use the `project_id` and Dallas url.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"You need to specify `model_id` that will be used for inferencing. All avaliable models you can find in [documentation](https://ibm.github.io/watsonx-ai-python-sdk/fm_model.html#ibm_watsonx_ai.foundation_models.utils.enums.ModelTypes)."
|
||||
"You need to specify `model_id` that will be used for inferencing. All available models you can find in [documentation](https://ibm.github.io/watsonx-ai-python-sdk/fm_model.html#ibm_watsonx_ai.foundation_models.utils.enums.ModelTypes)."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -63,7 +63,24 @@
|
||||
"source": [
|
||||
"from langchain_community.llms import Predibase\n",
|
||||
"\n",
|
||||
"# With an adapter, fine-tuned on the specified model\n",
|
||||
"# With a fine-tuned adapter hosted at Predibase (adapter_version can be specified; omitting it is equivalent to the most recent version).\n",
|
||||
"model = Predibase(\n",
|
||||
" model=\"mistral-7b\",\n",
|
||||
" adapter_id=\"e2e_nlg\",\n",
|
||||
" adapter_version=1,\n",
|
||||
" predibase_api_key=os.environ.get(\"PREDIBASE_API_TOKEN\"),\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_community.llms import Predibase\n",
|
||||
"\n",
|
||||
"# With a fine-tuned adapter hosted at HuggingFace (adapter_version does not apply and will be ignored).\n",
|
||||
"model = Predibase(\n",
|
||||
" model=\"mistral-7b\",\n",
|
||||
" adapter_id=\"predibase/e2e_nlg\",\n",
|
||||
@@ -109,13 +126,32 @@
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"vscode": {
|
||||
"languageId": "plaintext"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# With a fine-tuned adapter hosted at Predibase (adapter_version can be specified; omitting it is equivalent to the most recent version).\n",
|
||||
"model = Predibase(\n",
|
||||
" model=\"mistral-7b\",\n",
|
||||
" adapter_id=\"e2e_nlg\",\n",
|
||||
" adapter_version=1,\n",
|
||||
" predibase_api_key=os.environ.get(\"PREDIBASE_API_TOKEN\"),\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# With an adapter, fine-tuned on the specified model\n",
|
||||
"# With a fine-tuned adapter hosted at HuggingFace (adapter_version does not apply and will be ignored).\n",
|
||||
"llm = Predibase(\n",
|
||||
" model=\"mistral-7b\",\n",
|
||||
" adapter_id=\"predibase/e2e_nlg\",\n",
|
||||
@@ -211,7 +247,8 @@
|
||||
"\n",
|
||||
"model = Predibase(\n",
|
||||
" model=\"my-base-LLM\",\n",
|
||||
" adapter_id=\"my-finetuned-adapter-id\",\n",
|
||||
" adapter_id=\"my-finetuned-adapter-id\", # Supports both, Predibase-hosted and HuggingFace-hosted model repositories.\n",
|
||||
" # adapter_version=1, # optional (returns the latest, if omitted)\n",
|
||||
" predibase_api_key=os.environ.get(\n",
|
||||
" \"PREDIBASE_API_TOKEN\"\n",
|
||||
" ), # Adapter argument is optional.\n",
|
||||
|
||||
@@ -229,7 +229,7 @@ pip install langchain-google-cloud-sql-pg
|
||||
See [usage example](/docs/integrations/document_loaders/google_cloud_sql_pg).
|
||||
|
||||
```python
|
||||
from langchain_google_cloud_sql_pg import PostgreSQLEngine, PostgreSQLLoader
|
||||
from langchain_google_cloud_sql_pg import PostgresEngine, PostgresLoader
|
||||
```
|
||||
|
||||
### Cloud Storage
|
||||
@@ -486,6 +486,36 @@ See [usage example](/docs/integrations/vectorstores/google_spanner).
|
||||
from langchain_google_spanner import SpannerVectorStore
|
||||
```
|
||||
|
||||
### Firestore (Native Mode)
|
||||
|
||||
> [Google Cloud Firestore](https://cloud.google.com/firestore/docs/) is a NoSQL document database built for automatic scaling, high performance, and ease of application development.
|
||||
Install the python package:
|
||||
|
||||
```bash
|
||||
pip install langchain-google-firestore
|
||||
```
|
||||
|
||||
See [usage example](/docs/integrations/vectorstores/google_firestore).
|
||||
|
||||
```python
|
||||
from langchain_google_firestore import FirestoreVectorstore
|
||||
```
|
||||
|
||||
### Cloud SQL for MySQL
|
||||
|
||||
> [Google Cloud SQL for MySQL](https://cloud.google.com/sql) is a fully-managed database service that helps you set up, maintain, manage, and administer your MySQL relational databases on Google Cloud.
|
||||
Install the python package:
|
||||
|
||||
```bash
|
||||
pip install langchain-google-cloud-sql-mysql
|
||||
```
|
||||
|
||||
See [usage example](/docs/integrations/vectorstores/google_cloud_sql_mysql).
|
||||
|
||||
```python
|
||||
from langchain_google_cloud_sql_mysql import MySQLEngine, MySQLVectorStore
|
||||
```
|
||||
|
||||
### Cloud SQL for PostgreSQL
|
||||
|
||||
> [Google Cloud SQL for PostgreSQL](https://cloud.google.com/sql) is a fully-managed database service that helps you set up, maintain, manage, and administer your PostgreSQL relational databases on Google Cloud.
|
||||
@@ -498,7 +528,7 @@ pip install langchain-google-cloud-sql-pg
|
||||
See [usage example](/docs/integrations/vectorstores/google_cloud_sql_pg).
|
||||
|
||||
```python
|
||||
from langchain_google_cloud_sql_pg import PostgreSQLEngine, PostgresVectorStore
|
||||
from langchain_google_cloud_sql_pg import PostgresEngine, PostgresVectorStore
|
||||
```
|
||||
|
||||
### Vertex AI Vector Search
|
||||
@@ -783,7 +813,7 @@ See [usage example](/docs/integrations/memory/google_sql_pg).
|
||||
|
||||
|
||||
```python
|
||||
from langchain_google_cloud_sql_pg import PostgreSQLEngine, PostgreSQLChatMessageHistory
|
||||
from langchain_google_cloud_sql_pg import PostgresEngine, PostgresChatMessageHistory
|
||||
```
|
||||
|
||||
### Cloud SQL for MySQL
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
## Installation and Setup
|
||||
|
||||
```bash
|
||||
pip install chromadb
|
||||
pip install langchain-chroma
|
||||
```
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ There exists a wrapper around Chroma vector databases, allowing you to use it as
|
||||
whether for semantic search or example selection.
|
||||
|
||||
```python
|
||||
from langchain_community.vectorstores import Chroma
|
||||
from langchain_chroma import Chroma
|
||||
```
|
||||
|
||||
For a more detailed walkthrough of the Chroma wrapper, see [this notebook](/docs/integrations/vectorstores/chroma)
|
||||
|
||||
64
docs/docs/integrations/providers/dataherald.mdx
Normal file
64
docs/docs/integrations/providers/dataherald.mdx
Normal file
@@ -0,0 +1,64 @@
|
||||
# Dataherald
|
||||
|
||||
>[Dataherald](https://www.dataherald.com) is a natural language-to-SQL.
|
||||
|
||||
This page covers how to use the `Dataherald API` within LangChain.
|
||||
|
||||
## Installation and Setup
|
||||
- Install requirements with
|
||||
```bash
|
||||
pip install dataherald
|
||||
```
|
||||
- Go to dataherald and sign up [here](https://www.dataherald.com)
|
||||
- Create an app and get your `API KEY`
|
||||
- Set your `API KEY` as an environment variable `DATAHERALD_API_KEY`
|
||||
|
||||
|
||||
## Wrappers
|
||||
|
||||
### Utility
|
||||
|
||||
There exists a DataheraldAPIWrapper utility which wraps this API. To import this utility:
|
||||
|
||||
```python
|
||||
from langchain_community.utilities.dataherald import DataheraldAPIWrapper
|
||||
```
|
||||
|
||||
For a more detailed walkthrough of this wrapper, see [this notebook](/docs/integrations/tools/dataherald).
|
||||
|
||||
### Tool
|
||||
|
||||
You can use the tool in an agent like this:
|
||||
```python
|
||||
from langchain_community.utilities.dataherald import DataheraldAPIWrapper
|
||||
from langchain_community.tools.dataherald.tool import DataheraldTextToSQL
|
||||
from langchain_openai import ChatOpenAI
|
||||
from langchain import hub
|
||||
from langchain.agents import AgentExecutor, create_react_agent, load_tools
|
||||
|
||||
api_wrapper = DataheraldAPIWrapper(db_connection_id="<db_connection_id>")
|
||||
tool = DataheraldTextToSQL(api_wrapper=api_wrapper)
|
||||
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
|
||||
prompt = hub.pull("hwchase17/react")
|
||||
agent = create_react_agent(llm, tools, prompt)
|
||||
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
|
||||
agent_executor.invoke({"input":"Return the sql for this question: How many employees are in the company?"})
|
||||
```
|
||||
|
||||
Output
|
||||
```shell
|
||||
> Entering new AgentExecutor chain...
|
||||
I need to use a tool that can convert this question into SQL.
|
||||
Action: dataherald
|
||||
Action Input: How many employees are in the company?Answer: SELECT
|
||||
COUNT(*) FROM employeesI now know the final answer
|
||||
Final Answer: SELECT
|
||||
COUNT(*)
|
||||
FROM
|
||||
employees
|
||||
|
||||
> Finished chain.
|
||||
{'input': 'Return the sql for this question: How many employees are in the company?', 'output': "SELECT \n COUNT(*)\nFROM \n employees"}
|
||||
```
|
||||
|
||||
For more information on tools, see [this page](/docs/modules/tools/).
|
||||
@@ -1,113 +1,174 @@
|
||||
# Portkey
|
||||
|
||||
>[Portkey](https://docs.portkey.ai/overview/introduction) is a platform designed to streamline the deployment
|
||||
> and management of Generative AI applications.
|
||||
> It provides comprehensive features for monitoring, managing models,
|
||||
> and improving the performance of your AI applications.
|
||||
[Portkey](https://portkey.ai) is the Control Panel for AI apps. With it's popular AI Gateway and Observability Suite, hundreds of teams ship **reliable**, **cost-efficient**, and **fast** apps.
|
||||
|
||||
## LLMOps for Langchain
|
||||
|
||||
Portkey brings production readiness to Langchain. With Portkey, you can
|
||||
- [x] view detailed **metrics & logs** for all requests,
|
||||
- [x] enable **semantic cache** to reduce latency & costs,
|
||||
- [x] implement automatic **retries & fallbacks** for failed requests,
|
||||
- [x] add **custom tags** to requests for better tracking and analysis and [more](https://docs.portkey.ai).
|
||||
- [x] Connect to 150+ models through a unified API,
|
||||
- [x] View 42+ **metrics & logs** for all requests,
|
||||
- [x] Enable **semantic cache** to reduce latency & costs,
|
||||
- [x] Implement automatic **retries & fallbacks** for failed requests,
|
||||
- [x] Add **custom tags** to requests for better tracking and analysis and [more](https://portkey.ai/docs).
|
||||
|
||||
### Using Portkey with Langchain
|
||||
Using Portkey is as simple as just choosing which Portkey features you want, enabling them via `headers=Portkey.Config` and passing it in your LLM calls.
|
||||
|
||||
To start, get your Portkey API key by [signing up here](https://app.portkey.ai/login). (Click the profile icon on the top left, then click on "Copy API Key")
|
||||
## Quickstart - Portkey & Langchain
|
||||
Since Portkey is fully compatible with the OpenAI signature, you can connect to the Portkey AI Gateway through the `ChatOpenAI` interface.
|
||||
|
||||
For OpenAI, a simple integration with logging feature would look like this:
|
||||
- Set the `base_url` as `PORTKEY_GATEWAY_URL`
|
||||
- Add `default_headers` to consume the headers needed by Portkey using the `createHeaders` helper method.
|
||||
|
||||
To start, get your Portkey API key by [signing up here](https://app.portkey.ai/signup). (Click the profile icon on the bottom left, then click on "Copy API Key") or deploy the open source AI gateway in [your own environment](https://github.com/Portkey-AI/gateway/blob/main/docs/installation-deployments.md).
|
||||
|
||||
Next, install the Portkey SDK
|
||||
```python
|
||||
from langchain_openai import OpenAI
|
||||
from langchain_community.utilities import Portkey
|
||||
|
||||
# Add the Portkey API Key from your account
|
||||
headers = Portkey.Config(
|
||||
api_key = "<PORTKEY_API_KEY>"
|
||||
)
|
||||
|
||||
llm = OpenAI(temperature=0.9, headers=headers)
|
||||
llm.predict("What would be a good company name for a company that makes colorful socks?")
|
||||
pip install -U portkey_ai
|
||||
```
|
||||
Your logs will be captured on your [Portkey dashboard](https://app.portkey.ai).
|
||||
|
||||
A common Portkey X Langchain use case is to **trace a chain or an agent** and view all the LLM calls originating from that request.
|
||||
We can now connect to the Portkey AI Gateway by updating the `ChatOpenAI` model in Langchain
|
||||
```python
|
||||
from langchain_openai import ChatOpenAI
|
||||
from portkey_ai import createHeaders, PORTKEY_GATEWAY_URL
|
||||
|
||||
### **Tracing Chains & Agents**
|
||||
PORTKEY_API_KEY = "..." # Not needed when hosting your own gateway
|
||||
PROVIDER_API_KEY = "..." # Add the API key of the AI provider being used
|
||||
|
||||
portkey_headers = createHeaders(api_key=PORTKEY_API_KEY,provider="openai")
|
||||
|
||||
llm = ChatOpenAI(api_key=PROVIDER_API_KEY, base_url=PORTKEY_GATEWAY_URL, default_headers=portkey_headers)
|
||||
|
||||
llm.invoke("What is the meaning of life, universe and everything?")
|
||||
```
|
||||
|
||||
The request is routed through your Portkey AI Gateway to the specified `provider`. Portkey will also start logging all the requests in your account that makes debugging extremely simple.
|
||||
|
||||

|
||||
|
||||
## Using 150+ models through the AI Gateway
|
||||
The power of the AI gateway comes when you're able to use the above code snippet to connect with 150+ models across 20+ providers supported through the AI gateway.
|
||||
|
||||
Let's modify the code above to make a call to Anthropic's `claude-3-opus-20240229` model.
|
||||
|
||||
Portkey supports **[Virtual Keys](https://docs.portkey.ai/docs/product/ai-gateway-streamline-llm-integrations/virtual-keys)** which are an easy way to store and manage API keys in a secure vault. Lets try using a Virtual Key to make LLM calls. You can navigate to the Virtual Keys tab in Portkey and create a new key for Anthropic.
|
||||
|
||||
The `virtual_key` parameter sets the authentication and provider for the AI provider being used. In our case we're using the Anthropic Virtual key.
|
||||
|
||||
> Notice that the `api_key` can be left blank as that authentication won't be used.
|
||||
|
||||
```python
|
||||
from langchain.agents import AgentType, initialize_agent, load_tools
|
||||
from langchain_openai import OpenAI
|
||||
from langchain_community.utilities import Portkey
|
||||
from langchain_openai import ChatOpenAI
|
||||
from portkey_ai import createHeaders, PORTKEY_GATEWAY_URL
|
||||
|
||||
# Add the Portkey API Key from your account
|
||||
headers = Portkey.Config(
|
||||
api_key = "<PORTKEY_API_KEY>",
|
||||
trace_id = "fef659"
|
||||
PORTKEY_API_KEY = "..."
|
||||
VIRTUAL_KEY = "..." # Anthropic's virtual key we copied above
|
||||
|
||||
portkey_headers = createHeaders(api_key=PORTKEY_API_KEY,virtual_key=VIRTUAL_KEY)
|
||||
|
||||
llm = ChatOpenAI(api_key="X", base_url=PORTKEY_GATEWAY_URL, default_headers=portkey_headers, model="claude-3-opus-20240229")
|
||||
|
||||
llm.invoke("What is the meaning of life, universe and everything?")
|
||||
```
|
||||
|
||||
The Portkey AI gateway will authenticate the API request to Anthropic and get the response back in the OpenAI format for you to consume.
|
||||
|
||||
The AI gateway extends Langchain's `ChatOpenAI` class making it a single interface to call any provider and any model.
|
||||
|
||||
## Advanced Routing - Load Balancing, Fallbacks, Retries
|
||||
The Portkey AI Gateway brings capabilities like load-balancing, fallbacks, experimentation and canary testing to Langchain through a configuration-first approach.
|
||||
|
||||
Let's take an **example** where we might want to split traffic between `gpt-4` and `claude-opus` 50:50 to test the two large models. The gateway configuration for this would look like the following:
|
||||
|
||||
```python
|
||||
config = {
|
||||
"strategy": {
|
||||
"mode": "loadbalance"
|
||||
},
|
||||
"targets": [{
|
||||
"virtual_key": "openai-25654", # OpenAI's virtual key
|
||||
"override_params": {"model": "gpt4"},
|
||||
"weight": 0.5
|
||||
}, {
|
||||
"virtual_key": "anthropic-25654", # Anthropic's virtual key
|
||||
"override_params": {"model": "claude-3-opus-20240229"},
|
||||
"weight": 0.5
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
We can then use this config in our requests being made from langchain.
|
||||
|
||||
```python
|
||||
portkey_headers = createHeaders(
|
||||
api_key=PORTKEY_API_KEY,
|
||||
config=config
|
||||
)
|
||||
|
||||
llm = OpenAI(temperature=0, headers=headers)
|
||||
tools = load_tools(["serpapi", "llm-math"], llm=llm)
|
||||
agent = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True)
|
||||
llm = ChatOpenAI(api_key="X", base_url=PORTKEY_GATEWAY_URL, default_headers=portkey_headers)
|
||||
|
||||
llm.invoke("What is the meaning of life, universe and everything?")
|
||||
```
|
||||
|
||||
When the LLM is invoked, Portkey will distribute the requests to `gpt-4` and `claude-3-opus-20240229` in the ratio of the defined weights.
|
||||
|
||||
You can find more config examples [here](https://docs.portkey.ai/docs/api-reference/config-object#examples).
|
||||
|
||||
## **Tracing Chains & Agents**
|
||||
|
||||
Portkey's Langchain integration gives you full visibility into the running of an agent. Let's take an example of a [popular agentic workflow](https://python.langchain.com/docs/use_cases/tool_use/quickstart/#agents).
|
||||
|
||||
We only need to modify the `ChatOpenAI` class to use the AI Gateway as above.
|
||||
|
||||
```python
|
||||
from langchain import hub
|
||||
from langchain.agents import AgentExecutor, create_openai_tools_agent
|
||||
from langchain_openai import ChatOpenAI
|
||||
from langchain_core.tools import tool
|
||||
from portkey_ai import PORTKEY_GATEWAY_URL, createHeaders
|
||||
|
||||
prompt = hub.pull("hwchase17/openai-tools-agent")
|
||||
|
||||
portkey_headers = createHeaders(
|
||||
api_key=PORTKEY_API_KEY,
|
||||
virtual_key=OPENAI_VIRTUAL_KEY,
|
||||
trace_id="uuid-uuid-uuid-uuid"
|
||||
)
|
||||
|
||||
@tool
|
||||
def multiply(first_int: int, second_int: int) -> int:
|
||||
"""Multiply two integers together."""
|
||||
return first_int * second_int
|
||||
|
||||
# Let's test it out!
|
||||
agent.run("What was the high temperature in SF yesterday in Fahrenheit? What is that number raised to the .023 power?")
|
||||
|
||||
@tool
|
||||
def exponentiate(base: int, exponent: int) -> int:
|
||||
"Exponentiate the base to the exponent power."
|
||||
return base**exponent
|
||||
|
||||
|
||||
tools = [multiply, exponentiate]
|
||||
|
||||
model = ChatOpenAI(api_key="X", base_url=PORTKEY_GATEWAY_URL, default_headers=portkey_headers, temperature=0)
|
||||
|
||||
# Construct the OpenAI Tools agent
|
||||
agent = create_openai_tools_agent(model, tools, prompt)
|
||||
|
||||
# Create an agent executor by passing in the agent and tools
|
||||
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
|
||||
|
||||
agent_executor.invoke({
|
||||
"input": "Take 3 to the fifth power and multiply that by thirty six, then square the result"
|
||||
})
|
||||
```
|
||||
|
||||
**You can see the requests' logs along with the trace id on Portkey dashboard:**
|
||||
|
||||
<img src="/img/portkey-dashboard.gif" height="250"/>
|
||||
<img src="/img/portkey-tracing.png" height="250"/>
|
||||
|
||||
## Advanced Features
|
||||
|
||||
1. **Logging:** Log all your LLM requests automatically by sending them through Portkey. Each request log contains `timestamp`, `model name`, `total cost`, `request time`, `request json`, `response json`, and additional Portkey features.
|
||||
2. **Tracing:** Trace id can be passed along with each request and is visibe on the logs on Portkey dashboard. You can also set a **distinct trace id** for each request. You can [append user feedback](https://docs.portkey.ai/key-features/feedback-api) to a trace id as well.
|
||||
3. **Caching:** Respond to previously served customers queries from cache instead of sending them again to OpenAI. Match exact strings OR semantically similar strings. Cache can save costs and reduce latencies by 20x.
|
||||
4. **Retries:** Automatically reprocess any unsuccessful API requests **`upto 5`** times. Uses an **`exponential backoff`** strategy, which spaces out retry attempts to prevent network overload.
|
||||
5. **Tagging:** Track and audit each user interaction in high detail with predefined tags.
|
||||
|
||||
| Feature | Config Key | Value (Type) | Required/Optional |
|
||||
| -- | -- | -- | -- |
|
||||
| API Key | `api_key` | API Key (`string`) | ✅ Required |
|
||||
| [Tracing Requests](https://docs.portkey.ai/key-features/request-tracing) | `trace_id` | Custom `string` | ❔ Optional |
|
||||
| [Automatic Retries](https://docs.portkey.ai/key-features/automatic-retries) | `retry_count` | `integer` [1,2,3,4,5] | ❔ Optional |
|
||||
| [Enabling Cache](https://docs.portkey.ai/key-features/request-caching) | `cache` | `simple` OR `semantic` | ❔ Optional |
|
||||
| Cache Force Refresh | `cache_force_refresh` | `True` | ❔ Optional |
|
||||
| Set Cache Expiry | `cache_age` | `integer` (in seconds) | ❔ Optional |
|
||||
| [Add User](https://docs.portkey.ai/key-features/custom-metadata) | `user` | `string` | ❔ Optional |
|
||||
| [Add Organisation](https://docs.portkey.ai/key-features/custom-metadata) | `organisation` | `string` | ❔ Optional |
|
||||
| [Add Environment](https://docs.portkey.ai/key-features/custom-metadata) | `environment` | `string` | ❔ Optional |
|
||||
| [Add Prompt (version/id/string)](https://docs.portkey.ai/key-features/custom-metadata) | `prompt` | `string` | ❔ Optional |
|
||||

|
||||
|
||||
|
||||
## **Enabling all Portkey Features:**
|
||||
Additional Docs are available here:
|
||||
- Observability - https://portkey.ai/docs/product/observability-modern-monitoring-for-llms
|
||||
- AI Gateway - https://portkey.ai/docs/product/ai-gateway-streamline-llm-integrations
|
||||
- Prompt Library - https://portkey.ai/docs/product/prompt-library
|
||||
|
||||
```py
|
||||
headers = Portkey.Config(
|
||||
|
||||
# Mandatory
|
||||
api_key="<PORTKEY_API_KEY>",
|
||||
|
||||
# Cache Options
|
||||
cache="semantic",
|
||||
cache_force_refresh="True",
|
||||
cache_age=1729,
|
||||
You can check out our popular Open Source AI Gateway here - https://github.com/portkey-ai/gateway
|
||||
|
||||
# Advanced
|
||||
retry_count=5,
|
||||
trace_id="langchain_agent",
|
||||
|
||||
# Metadata
|
||||
environment="production",
|
||||
user="john",
|
||||
organisation="acme",
|
||||
prompt="Frost"
|
||||
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
For detailed information on each feature and how to use it, [please refer to the Portkey docs](https://docs.portkey.ai). If you have any questions or need further assistance, [reach out to us on Twitter.](https://twitter.com/portkeyai).
|
||||
For detailed information on each feature and how to use it, [please refer to the Portkey docs](https://portkey.ai/docs). If you have any questions or need further assistance, [reach out to us on Twitter.](https://twitter.com/portkeyai) or our [support email](mailto:hello@portkey.ai).
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"source": [
|
||||
"# Log, Trace, and Monitor\n",
|
||||
"\n",
|
||||
"When building apps or agents using Langchain, you end up making multiple API calls to fulfill a single user request. However, these requests are not chained when you want to analyse them. With [**Portkey**](/docs/integrations/providers/portkey), all the embeddings, completion, and other requests from a single user request will get logged and traced to a common ID, enabling you to gain full visibility of user interactions.\n",
|
||||
"When building apps or agents using Langchain, you end up making multiple API calls to fulfill a single user request. However, these requests are not chained when you want to analyse them. With [**Portkey**](/docs/integrations/providers/portkey/), all the embeddings, completions, and other requests from a single user request will get logged and traced to a common ID, enabling you to gain full visibility of user interactions.\n",
|
||||
"\n",
|
||||
"This notebook serves as a step-by-step guide on how to log, trace, and monitor Langchain LLM calls using `Portkey` in your Langchain app."
|
||||
]
|
||||
@@ -20,15 +20,15 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"from langchain.agents import AgentType, initialize_agent, load_tools\n",
|
||||
"from langchain_community.utilities import Portkey\n",
|
||||
"from langchain_openai import OpenAI"
|
||||
"from langchain.agents import AgentExecutor, create_openai_tools_agent\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"from portkey_ai import PORTKEY_GATEWAY_URL, createHeaders"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -40,11 +40,11 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"os.environ[\"OPENAI_API_KEY\"] = \"<OPENAI_API_KEY>\""
|
||||
"os.environ[\"OPENAI_API_KEY\"] = \"...\""
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -52,18 +52,18 @@
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Get Portkey API Key\n",
|
||||
"1. Sign up for [Portkey here](https://app.portkey.ai/login)\n",
|
||||
"2. On your [dashboard](https://app.portkey.ai/), click on the profile icon on the top left, then click on \"Copy API Key\"\n",
|
||||
"1. Sign up for [Portkey here](https://app.portkey.ai/signup)\n",
|
||||
"2. On your [dashboard](https://app.portkey.ai/), click on the profile icon on the bottom left, then click on \"Copy API Key\"\n",
|
||||
"3. Paste it below"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"PORTKEY_API_KEY = \"<PORTKEY_API_KEY>\" # Paste your Portkey API Key here"
|
||||
"PORTKEY_API_KEY = \"...\" # Paste your Portkey API Key here"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -77,11 +77,11 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"TRACE_ID = \"portkey_langchain_demo\" # Set trace id here"
|
||||
"TRACE_ID = \"uuid-trace-id\" # Set trace id here"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -93,16 +93,49 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"headers = Portkey.Config(\n",
|
||||
" api_key=PORTKEY_API_KEY,\n",
|
||||
" trace_id=TRACE_ID,\n",
|
||||
"portkey_headers = createHeaders(\n",
|
||||
" api_key=PORTKEY_API_KEY, provider=\"openai\", trace_id=TRACE_ID\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Define the prompts and the tools to use"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain import hub\n",
|
||||
"from langchain_core.tools import tool\n",
|
||||
"\n",
|
||||
"prompt = hub.pull(\"hwchase17/openai-tools-agent\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"@tool\n",
|
||||
"def multiply(first_int: int, second_int: int) -> int:\n",
|
||||
" \"\"\"Multiply two integers together.\"\"\"\n",
|
||||
" return first_int * second_int\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"@tool\n",
|
||||
"def exponentiate(base: int, exponent: int) -> int:\n",
|
||||
" \"Exponentiate the base to the exponent power.\"\n",
|
||||
" return base**exponent\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"tools = [multiply, exponentiate]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
@@ -112,19 +145,60 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"\n",
|
||||
"\n",
|
||||
"\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
|
||||
"\u001b[32;1m\u001b[1;3m\n",
|
||||
"Invoking: `exponentiate` with `{'base': 3, 'exponent': 5}`\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\u001b[0m\u001b[33;1m\u001b[1;3m243\u001b[0m\u001b[32;1m\u001b[1;3m\n",
|
||||
"Invoking: `multiply` with `{'first_int': 243, 'second_int': 36}`\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\u001b[0m\u001b[36;1m\u001b[1;3m8748\u001b[0m\u001b[32;1m\u001b[1;3m\n",
|
||||
"Invoking: `exponentiate` with `{'base': 8748, 'exponent': 2}`\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\u001b[0m\u001b[33;1m\u001b[1;3m76527504\u001b[0m\u001b[32;1m\u001b[1;3mThe result of taking 3 to the fifth power, multiplying it by 36, and then squaring the result is 76,527,504.\u001b[0m\n",
|
||||
"\n",
|
||||
"\u001b[1m> Finished chain.\u001b[0m\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'input': 'Take 3 to the fifth power and multiply that by thirty six, then square the result',\n",
|
||||
" 'output': 'The result of taking 3 to the fifth power, multiplying it by 36, and then squaring the result is 76,527,504.'}"
|
||||
]
|
||||
},
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"llm = OpenAI(temperature=0, headers=headers)\n",
|
||||
"tools = load_tools([\"serpapi\", \"llm-math\"], llm=llm)\n",
|
||||
"agent = initialize_agent(\n",
|
||||
" tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True\n",
|
||||
"model = ChatOpenAI(\n",
|
||||
" base_url=PORTKEY_GATEWAY_URL, default_headers=portkey_headers, temperature=0\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Let's test it out!\n",
|
||||
"agent.run(\n",
|
||||
" \"What was the high temperature in SF yesterday in Fahrenheit? What is that number raised to the .023 power?\"\n",
|
||||
"# Construct the OpenAI Tools agent\n",
|
||||
"agent = create_openai_tools_agent(model, tools, prompt)\n",
|
||||
"\n",
|
||||
"# Create an agent executor by passing in the agent and tools\n",
|
||||
"agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)\n",
|
||||
"\n",
|
||||
"agent_executor.invoke(\n",
|
||||
" {\n",
|
||||
" \"input\": \"Take 3 to the fifth power and multiply that by thirty six, then square the result\"\n",
|
||||
" }\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
@@ -138,10 +212,13 @@
|
||||
"- Sending your request through Portkey ensures that all of the requests are logged by default\n",
|
||||
"- Each request log contains `timestamp`, `model name`, `total cost`, `request time`, `request json`, `response json`, and additional Portkey features\n",
|
||||
"\n",
|
||||
"**Tracing**\n",
|
||||
"- Trace id is passed along with each request and is visibe on the logs on Portkey dashboard\n",
|
||||
"**[Tracing](https://portkey.ai/docs/product/observability-modern-monitoring-for-llms/traces)**\n",
|
||||
"- Trace id is passed along with each request and is visible on the logs on Portkey dashboard\n",
|
||||
"- You can also set a **distinct trace id** for each request if you want\n",
|
||||
"- You can append user feedback to a trace id as well. [More info on this here](https://docs.portkey.ai/key-features/feedback-api)"
|
||||
"- You can append user feedback to a trace id as well. [More info on this here](https://portkey.ai/docs/product/observability-modern-monitoring-for-llms/feedback)\n",
|
||||
"\n",
|
||||
"For the above request, you will be able to view the entire log trace like this\n",
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -154,62 +231,15 @@
|
||||
"\n",
|
||||
"**Caching**\n",
|
||||
"\n",
|
||||
"Respond to previously served customers queries from cache instead of sending them again to OpenAI. Match exact strings OR semantically similar strings. Cache can save costs and reduce latencies by 20x.\n",
|
||||
"Respond to previously served customers queries from cache instead of sending them again to OpenAI. Match exact strings OR semantically similar strings. Cache can save costs and reduce latencies by 20x. [Docs](https://portkey.ai/docs/product/ai-gateway-streamline-llm-integrations/cache-simple-and-semantic)\n",
|
||||
"\n",
|
||||
"**Retries**\n",
|
||||
"\n",
|
||||
"Automatically reprocess any unsuccessful API requests **`upto 5`** times. Uses an **`exponential backoff`** strategy, which spaces out retry attempts to prevent network overload.\n",
|
||||
"\n",
|
||||
"| Feature | Config Key | Value (Type) |\n",
|
||||
"| -- | -- | -- |\n",
|
||||
"| [🔁 Automatic Retries](https://docs.portkey.ai/key-features/automatic-retries) | `retry_count` | `integer` [1,2,3,4,5] |\n",
|
||||
"| [🧠 Enabling Cache](https://docs.portkey.ai/key-features/request-caching) | `cache` | `simple` OR `semantic` |\n",
|
||||
"Automatically reprocess any unsuccessful API requests **`upto 5`** times. Uses an **`exponential backoff`** strategy, which spaces out retry attempts to prevent network overload.[Docs](https://portkey.ai/docs/product/ai-gateway-streamline-llm-integrations)\n",
|
||||
"\n",
|
||||
"**Tagging**\n",
|
||||
"\n",
|
||||
"Track and audit ach user interaction in high detail with predefined tags.\n",
|
||||
"\n",
|
||||
"| Tag | Config Key | Value (Type) |\n",
|
||||
"| -- | -- | -- |\n",
|
||||
"| User Tag | `user` | `string` |\n",
|
||||
"| Organisation Tag | `organisation` | `string` |\n",
|
||||
"| Environment Tag | `environment` | `string` |\n",
|
||||
"| Prompt Tag (version/id/string) | `prompt` | `string` |"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Code Example With All Features"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"headers = Portkey.Config(\n",
|
||||
" # Mandatory\n",
|
||||
" api_key=\"<PORTKEY_API_KEY>\",\n",
|
||||
" # Cache Options\n",
|
||||
" cache=\"semantic\",\n",
|
||||
" cache_force_refresh=\"True\",\n",
|
||||
" cache_age=1729,\n",
|
||||
" # Advanced\n",
|
||||
" retry_count=5,\n",
|
||||
" trace_id=\"langchain_agent\",\n",
|
||||
" # Metadata\n",
|
||||
" environment=\"production\",\n",
|
||||
" user=\"john\",\n",
|
||||
" organisation=\"acme\",\n",
|
||||
" prompt=\"Frost\",\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"llm = OpenAI(temperature=0.9, headers=headers)\n",
|
||||
"\n",
|
||||
"print(llm(\"Two roads diverged in the yellow woods\"))"
|
||||
"Track and audit each user interaction in high detail with predefined tags. [Docs](https://portkey.ai/docs/product/observability-modern-monitoring-for-llms/metadata)"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -229,7 +259,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.1"
|
||||
"version": "3.9.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
@@ -23,6 +23,21 @@ response = model("Can you recommend me a nice dry wine?")
|
||||
print(response)
|
||||
```
|
||||
|
||||
Predibase also supports Predibase-hosted and HuggingFace-hosted adapters that are fine-tuned on the base model given by the `model` argument:
|
||||
|
||||
```python
|
||||
import os
|
||||
os.environ["PREDIBASE_API_TOKEN"] = "{PREDIBASE_API_TOKEN}"
|
||||
|
||||
from langchain_community.llms import Predibase
|
||||
|
||||
# The fine-tuned adapter is hosted at Predibase (adapter_version can be specified; omitting it is equivalent to the most recent version).
|
||||
model = Predibase(model="mistral-7b"", adapter_id="e2e_nlg", adapter_version=1, predibase_api_key=os.environ.get("PREDIBASE_API_TOKEN"))
|
||||
|
||||
response = model("Can you recommend me a nice dry wine?")
|
||||
print(response)
|
||||
```
|
||||
|
||||
Predibase also supports adapters that are fine-tuned on the base model given by the `model` argument:
|
||||
|
||||
```python
|
||||
@@ -31,6 +46,7 @@ os.environ["PREDIBASE_API_TOKEN"] = "{PREDIBASE_API_TOKEN}"
|
||||
|
||||
from langchain_community.llms import Predibase
|
||||
|
||||
# The fine-tuned adapter is hosted at HuggingFace (adapter_version does not apply and will be ignored).
|
||||
model = Predibase(model="mistral-7b"", adapter_id="predibase/e2e_nlg", predibase_api_key=os.environ.get("PREDIBASE_API_TOKEN"))
|
||||
|
||||
response = model("Can you recommend me a nice dry wine?")
|
||||
|
||||
@@ -28,12 +28,12 @@
|
||||
" DocumentCompressorPipeline,\n",
|
||||
" MergerRetriever,\n",
|
||||
")\n",
|
||||
"from langchain_chroma import Chroma\n",
|
||||
"from langchain_community.document_transformers import (\n",
|
||||
" EmbeddingsClusteringFilter,\n",
|
||||
" EmbeddingsRedundantFilter,\n",
|
||||
")\n",
|
||||
"from langchain_community.embeddings import HuggingFaceEmbeddings\n",
|
||||
"from langchain_community.vectorstores import Chroma\n",
|
||||
"from langchain_openai import OpenAIEmbeddings\n",
|
||||
"\n",
|
||||
"# Get 3 diff embeddings.\n",
|
||||
|
||||
@@ -28,8 +28,8 @@
|
||||
"import logging\n",
|
||||
"\n",
|
||||
"from langchain.retrievers import RePhraseQueryRetriever\n",
|
||||
"from langchain_chroma import Chroma\n",
|
||||
"from langchain_community.document_loaders import WebBaseLoader\n",
|
||||
"from langchain_community.vectorstores import Chroma\n",
|
||||
"from langchain_openai import ChatOpenAI, OpenAIEmbeddings\n",
|
||||
"from langchain_text_splitters import RecursiveCharacterTextSplitter"
|
||||
]
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"## Creating a Chroma vector store\n",
|
||||
"First we'll want to create a Chroma vector store and seed it with some data. We've created a small demo set of documents that contain summaries of movies.\n",
|
||||
"\n",
|
||||
"**Note:** The self-query retriever requires you to have `lark` installed (`pip install lark`). We also need the `chromadb` package."
|
||||
"**Note:** The self-query retriever requires you to have `lark` installed (`pip install lark`). We also need the `langchain-chroma` package."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -44,7 +44,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install --upgrade --quiet chromadb"
|
||||
"%pip install --upgrade --quiet langchain-chroma"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -87,7 +87,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_community.vectorstores import Chroma\n",
|
||||
"from langchain_chroma import Chroma\n",
|
||||
"from langchain_core.documents import Document\n",
|
||||
"from langchain_openai import OpenAIEmbeddings\n",
|
||||
"\n",
|
||||
|
||||
117
docs/docs/integrations/tools/dataherald.ipynb
Normal file
117
docs/docs/integrations/tools/dataherald.ipynb
Normal file
@@ -0,0 +1,117 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "245a954a",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Dataherald\n",
|
||||
"\n",
|
||||
"This notebook goes over how to use the dataherald component.\n",
|
||||
"\n",
|
||||
"First, you need to set up your Dataherald account and get your API KEY:\n",
|
||||
"\n",
|
||||
"1. Go to dataherald and sign up [here](https://www.dataherald.com/)\n",
|
||||
"2. Once you are logged in your Admin Console, create an API KEY\n",
|
||||
"3. pip install dataherald\n",
|
||||
"\n",
|
||||
"Then we will need to set some environment variables:\n",
|
||||
"1. Save your API KEY into DATAHERALD_API_KEY env variable"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "961b3689",
|
||||
"metadata": {
|
||||
"vscode": {
|
||||
"languageId": "shellscript"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"pip install dataherald"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "34bb5968",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"os.environ[\"DATAHERALD_API_KEY\"] = \"\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"id": "ac4910f8",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_community.utilities.dataherald import DataheraldAPIWrapper"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"id": "84b8f773",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"dataherald = DataheraldAPIWrapper(db_connection_id=\"65fb766367dd22c99ce1a12d\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"id": "068991a6",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'select COUNT(*) from employees'"
|
||||
]
|
||||
},
|
||||
"execution_count": 11,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"dataherald.run(\"How many employees are in the company?\")"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": ".venv",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.9.7"
|
||||
},
|
||||
"vscode": {
|
||||
"interpreter": {
|
||||
"hash": "53f3bc57609c7a84333bb558594977aa5b4026b1d6070b93987956689e367341"
|
||||
}
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
"Install Chroma with:\n",
|
||||
"\n",
|
||||
"```sh\n",
|
||||
"pip install chromadb\n",
|
||||
"pip install langchain-chroma\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"Chroma runs in various modes. See below for examples of each integrated with LangChain.\n",
|
||||
@@ -65,11 +65,11 @@
|
||||
],
|
||||
"source": [
|
||||
"# import\n",
|
||||
"from langchain_chroma import Chroma\n",
|
||||
"from langchain_community.document_loaders import TextLoader\n",
|
||||
"from langchain_community.embeddings.sentence_transformer import (\n",
|
||||
" SentenceTransformerEmbeddings,\n",
|
||||
")\n",
|
||||
"from langchain_community.vectorstores import Chroma\n",
|
||||
"from langchain_text_splitters import CharacterTextSplitter\n",
|
||||
"\n",
|
||||
"# load the document and split it into chunks\n",
|
||||
|
||||
585
docs/docs/integrations/vectorstores/google_cloud_sql_mysql.ipynb
Normal file
585
docs/docs/integrations/vectorstores/google_cloud_sql_mysql.ipynb
Normal file
@@ -0,0 +1,585 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Google Cloud SQL for MySQL\n",
|
||||
"\n",
|
||||
"> [Cloud SQL](https://cloud.google.com/sql) is a fully managed relational database service that offers high performance, seamless integration, and impressive scalability. It offers PostgreSQL, MySQL, and SQL Server database engines. Extend your database application to build AI-powered experiences leveraging Cloud SQL's LangChain integrations.\n",
|
||||
"\n",
|
||||
"This notebook goes over how to use `Cloud SQL for MySQL` to store vector embeddings with the `MySQLVectorStore` class.\n",
|
||||
"\n",
|
||||
"Learn more about the package on [GitHub](https://github.com/googleapis/langchain-google-cloud-sql-mysql-python/).\n",
|
||||
"\n",
|
||||
"[](https://colab.research.google.com/github/googleapis/langchain-google-cloud-sql-mysql-python/blob/main/docs/vector_store.ipynb)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Before you begin\n",
|
||||
"\n",
|
||||
"To run this notebook, you will need to do the following:\n",
|
||||
"\n",
|
||||
" * [Create a Google Cloud Project](https://developers.google.com/workspace/guides/create-project)\n",
|
||||
" * [Enable the Cloud SQL Admin API.](https://console.cloud.google.com/flows/enableapi?apiid=sqladmin.googleapis.com)\n",
|
||||
" * [Create a Cloud SQL instance.](https://cloud.google.com/sql/docs/mysql/connect-instance-auth-proxy#create-instance) (version must be >= **8.0.36** with **cloudsql_vector** database flag configured to \"On\")\n",
|
||||
" * [Create a Cloud SQL database.](https://cloud.google.com/sql/docs/mysql/create-manage-databases)\n",
|
||||
" * [Add a User to the database.](https://cloud.google.com/sql/docs/mysql/create-manage-users)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### 🦜🔗 Library Installation\n",
|
||||
"Install the integration library, `langchain-google-cloud-sql-mysql`, and the library for the embedding service, `langchain-google-vertexai`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "0ZITIDE160OD"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install --upgrade --quiet langchain-google-cloud-sql-mysql langchain-google-vertexai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "v40bB_GMcr9f"
|
||||
},
|
||||
"source": [
|
||||
"**Colab only:** Uncomment the following cell to restart the kernel or use the button to restart the kernel. For Vertex AI Workbench you can restart the terminal using the button on top."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "v6jBDnYnNM08"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# # Automatically restart kernel after installs so that your environment can access the new packages\n",
|
||||
"# import IPython\n",
|
||||
"\n",
|
||||
"# app = IPython.Application.instance()\n",
|
||||
"# app.kernel.do_shutdown(True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "yygMe6rPWxHS"
|
||||
},
|
||||
"source": [
|
||||
"### 🔐 Authentication\n",
|
||||
"Authenticate to Google Cloud as the IAM user logged into this notebook in order to access your Google Cloud Project.\n",
|
||||
"\n",
|
||||
"* If you are using Colab to run this notebook, use the cell below and continue.\n",
|
||||
"* If you are using Vertex AI Workbench, check out the setup instructions [here](https://github.com/GoogleCloudPlatform/generative-ai/tree/main/setup-env)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "PTXN1_DSXj2b"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.colab import auth\n",
|
||||
"\n",
|
||||
"auth.authenticate_user()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "NEvB9BoLEulY"
|
||||
},
|
||||
"source": [
|
||||
"### ☁ Set Your Google Cloud Project\n",
|
||||
"Set your Google Cloud project so that you can leverage Google Cloud resources within this notebook.\n",
|
||||
"\n",
|
||||
"If you don't know your project ID, try the following:\n",
|
||||
"\n",
|
||||
"* Run `gcloud config list`.\n",
|
||||
"* Run `gcloud projects list`.\n",
|
||||
"* See the support page: [Locate the project ID](https://support.google.com/googleapi/answer/7014113)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"cellView": "form",
|
||||
"id": "gfkS3yVRE4_W"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# @markdown Please fill in the value below with your Google Cloud project ID and then run the cell.\n",
|
||||
"\n",
|
||||
"PROJECT_ID = \"my-project-id\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# Set the project id\n",
|
||||
"!gcloud config set project {PROJECT_ID}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "f8f2830ee9ca1e01"
|
||||
},
|
||||
"source": [
|
||||
"## Basic Usage"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "OMvzMWRrR6n7"
|
||||
},
|
||||
"source": [
|
||||
"### Set Cloud SQL database values\n",
|
||||
"Find your database values, in the [Cloud SQL Instances page](https://console.cloud.google.com/sql?_ga=2.223735448.2062268965.1707700487-2088871159.1707257687).\n",
|
||||
"\n",
|
||||
"**Note:** MySQL vector support is only available on MySQL instances with version **>= 8.0.36**.\n",
|
||||
"\n",
|
||||
"For existing instances, you may need to perform a [self-service maintenance update](https://cloud.google.com/sql/docs/mysql/self-service-maintenance) to update your maintenance version to **MYSQL_8_0_36.R20240401.03_00** or greater. Once updated, [configure your database flags](https://cloud.google.com/sql/docs/mysql/flags) to have the new **cloudsql_vector** flag to \"On\"."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# @title Set Your Values Here { display-mode: \"form\" }\n",
|
||||
"REGION = \"us-central1\" # @param {type: \"string\"}\n",
|
||||
"INSTANCE = \"my-mysql-instance\" # @param {type: \"string\"}\n",
|
||||
"DATABASE = \"my-database\" # @param {type: \"string\"}\n",
|
||||
"TABLE_NAME = \"vector_store\" # @param {type: \"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "QuQigs4UoFQ2"
|
||||
},
|
||||
"source": [
|
||||
"### MySQLEngine Connection Pool\n",
|
||||
"\n",
|
||||
"One of the requirements and arguments to establish Cloud SQL as a vector store is a `MySQLEngine` object. The `MySQLEngine` configures a connection pool to your Cloud SQL database, enabling successful connections from your application and following industry best practices.\n",
|
||||
"\n",
|
||||
"To create a `MySQLEngine` using `MySQLEngine.from_instance()` you need to provide only 4 things:\n",
|
||||
"\n",
|
||||
"1. `project_id` : Project ID of the Google Cloud Project where the Cloud SQL instance is located.\n",
|
||||
"1. `region` : Region where the Cloud SQL instance is located.\n",
|
||||
"1. `instance` : The name of the Cloud SQL instance.\n",
|
||||
"1. `database` : The name of the database to connect to on the Cloud SQL instance.\n",
|
||||
"\n",
|
||||
"By default, [IAM database authentication](https://cloud.google.com/sql/docs/mysql/iam-authentication#iam-db-auth) will be used as the method of database authentication. This library uses the IAM principal belonging to the [Application Default Credentials (ADC)](https://cloud.google.com/docs/authentication/application-default-credentials) sourced from the envionment.\n",
|
||||
"\n",
|
||||
"For more informatin on IAM database authentication please see:\n",
|
||||
"\n",
|
||||
"* [Configure an instance for IAM database authentication](https://cloud.google.com/sql/docs/mysql/create-edit-iam-instances)\n",
|
||||
"* [Manage users with IAM database authentication](https://cloud.google.com/sql/docs/mysql/add-manage-iam-users)\n",
|
||||
"\n",
|
||||
"Optionally, [built-in database authentication](https://cloud.google.com/sql/docs/mysql/built-in-authentication) using a username and password to access the Cloud SQL database can also be used. Just provide the optional `user` and `password` arguments to `MySQLEngine.from_instance()`:\n",
|
||||
"\n",
|
||||
"* `user` : Database user to use for built-in database authentication and login\n",
|
||||
"* `password` : Database password to use for built-in database authentication and login.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "guVURf1QLL53"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_google_cloud_sql_mysql import MySQLEngine\n",
|
||||
"\n",
|
||||
"engine = MySQLEngine.from_instance(\n",
|
||||
" project_id=PROJECT_ID, region=REGION, instance=INSTANCE, database=DATABASE\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Initialize a table\n",
|
||||
"The `MySQLVectorStore` class requires a database table. The `MySQLEngine` class has a helper method `init_vectorstore_table()` that can be used to create a table with the proper schema for you."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "avlyHEMn6gzU"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"engine.init_vectorstore_table(\n",
|
||||
" table_name=TABLE_NAME,\n",
|
||||
" vector_size=768, # Vector size for VertexAI model(textembedding-gecko@latest)\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Create an embedding class instance\n",
|
||||
"\n",
|
||||
"You can use any [LangChain embeddings model](/docs/integrations/text_embedding/).\n",
|
||||
"You may need to enable the Vertex AI API to use `VertexAIEmbeddings`.\n",
|
||||
"\n",
|
||||
"We recommend pinning the embedding model's version for production, learn more about the [Text embeddings models](https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/text-embeddings)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "5utKIdq7KYi5"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# enable Vertex AI API\n",
|
||||
"!gcloud services enable aiplatform.googleapis.com"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "Vb2RJocV9_LQ"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_google_vertexai import VertexAIEmbeddings\n",
|
||||
"\n",
|
||||
"embedding = VertexAIEmbeddings(\n",
|
||||
" model_name=\"textembedding-gecko@latest\", project=PROJECT_ID\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "e1tl0aNx7SWy"
|
||||
},
|
||||
"source": [
|
||||
"### Initialize a default MySQLVectorStore\n",
|
||||
"\n",
|
||||
"To initialize a `MySQLVectorStore` class you need to provide only 3 things:\n",
|
||||
"\n",
|
||||
"1. `engine` - An instance of a `MySQLEngine` engine.\n",
|
||||
"1. `embedding_service` - An instance of a LangChain embedding model.\n",
|
||||
"1. `table_name` : The name of the table within the Cloud SQL database to use as the vector store."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "z-AZyzAQ7bsf"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_google_cloud_sql_mysql import MySQLVectorStore\n",
|
||||
"\n",
|
||||
"store = MySQLVectorStore(\n",
|
||||
" engine=engine,\n",
|
||||
" embedding_service=embedding,\n",
|
||||
" table_name=TABLE_NAME,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Add texts"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "nrDvGWIOLL54"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import uuid\n",
|
||||
"\n",
|
||||
"all_texts = [\"Apples and oranges\", \"Cars and airplanes\", \"Pineapple\", \"Train\", \"Banana\"]\n",
|
||||
"metadatas = [{\"len\": len(t)} for t in all_texts]\n",
|
||||
"ids = [str(uuid.uuid4()) for _ in all_texts]\n",
|
||||
"\n",
|
||||
"store.add_texts(all_texts, metadatas=metadatas, ids=ids)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Delete texts\n",
|
||||
"\n",
|
||||
"Delete vectors from the vector store by ID."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"store.delete([ids[1]])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Search for documents"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 17,
|
||||
"metadata": {
|
||||
"id": "fpqeZgUeLL54",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "f674a3af-452c-4d58-bb62-cbf514a9e1e3"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"Pineapple\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"query = \"I'd like a fruit.\"\n",
|
||||
"docs = store.similarity_search(query)\n",
|
||||
"print(docs[0].page_content)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Search for documents by vector\n",
|
||||
"\n",
|
||||
"It is also possible to do a search for documents similar to a given embedding vector using `similarity_search_by_vector` which accepts an embedding vector as a parameter instead of a string."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "N-NC5jgGLL55",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "69a1f9de-a830-450d-8a5e-118b36815a46"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"[Document(page_content='Pineapple', metadata={'len': 9}), Document(page_content='Banana', metadata={'len': 6})]\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"query_vector = embedding.embed_query(query)\n",
|
||||
"docs = store.similarity_search_by_vector(query_vector, k=2)\n",
|
||||
"print(docs)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Add an index\n",
|
||||
"Speed up vector search queries by applying a vector index. Learn more about [MySQL vector indexes](https://github.com/googleapis/langchain-google-cloud-sql-mysql-python/blob/main/src/langchain_google_cloud_sql_mysql/indexes.py).\n",
|
||||
"\n",
|
||||
"**Note:** For IAM database authentication (default usage), the IAM database user will need to be granted the following permissions by a privileged database user for full control of vector indexes.\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"GRANT EXECUTE ON PROCEDURE mysql.create_vector_index TO '<IAM_DB_USER>'@'%';\n",
|
||||
"GRANT EXECUTE ON PROCEDURE mysql.alter_vector_index TO '<IAM_DB_USER>'@'%';\n",
|
||||
"GRANT EXECUTE ON PROCEDURE mysql.drop_vector_index TO '<IAM_DB_USER>'@'%';\n",
|
||||
"GRANT SELECT ON mysql.vector_indexes TO '<IAM_DB_USER>'@'%';\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_google_cloud_sql_mysql import VectorIndex\n",
|
||||
"\n",
|
||||
"store.apply_vector_index(VectorIndex())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Remove an index"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"store.drop_vector_index()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"## Advanced Usage"
|
||||
],
|
||||
"metadata": {}
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Create a MySQLVectorStore with custom metadata\n",
|
||||
"\n",
|
||||
"A vector store can take advantage of relational data to filter similarity searches.\n",
|
||||
"\n",
|
||||
"Create a table and `MySQLVectorStore` instance with custom metadata columns."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "eANG7_8qLL55"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_google_cloud_sql_mysql import Column\n",
|
||||
"\n",
|
||||
"# set table name\n",
|
||||
"CUSTOM_TABLE_NAME = \"vector_store_custom\"\n",
|
||||
"\n",
|
||||
"engine.init_vectorstore_table(\n",
|
||||
" table_name=CUSTOM_TABLE_NAME,\n",
|
||||
" vector_size=768, # VertexAI model: textembedding-gecko@latest\n",
|
||||
" metadata_columns=[Column(\"len\", \"INTEGER\")],\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# initialize MySQLVectorStore with custom metadata columns\n",
|
||||
"custom_store = MySQLVectorStore(\n",
|
||||
" engine=engine,\n",
|
||||
" embedding_service=embedding,\n",
|
||||
" table_name=CUSTOM_TABLE_NAME,\n",
|
||||
" metadata_columns=[\"len\"],\n",
|
||||
" # connect to an existing VectorStore by customizing the table schema:\n",
|
||||
" # id_column=\"uuid\",\n",
|
||||
" # content_column=\"documents\",\n",
|
||||
" # embedding_column=\"vectors\",\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bj2d-c2sLL5-"
|
||||
},
|
||||
"source": [
|
||||
"### Search for documents with metadata filter\n",
|
||||
"\n",
|
||||
"It can be helpful to narrow down the documents before working with them.\n",
|
||||
"\n",
|
||||
"For example, documents can be filtered on metadata using the `filter` argument."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "Sqfgk6EOLL5-",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "a10c74e2-fe48-4cf9-ba2f-85aecb2490d0"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"[Document(page_content='Pineapple', metadata={'len': 9}), Document(page_content='Banana', metadata={'len': 6}), Document(page_content='Apples and oranges', metadata={'len': 18}), Document(page_content='Cars and airplanes', metadata={'len': 18})]\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import uuid\n",
|
||||
"\n",
|
||||
"# add texts to the vector store\n",
|
||||
"all_texts = [\"Apples and oranges\", \"Cars and airplanes\", \"Pineapple\", \"Train\", \"Banana\"]\n",
|
||||
"metadatas = [{\"len\": len(t)} for t in all_texts]\n",
|
||||
"ids = [str(uuid.uuid4()) for _ in all_texts]\n",
|
||||
"custom_store.add_texts(all_texts, metadatas=metadatas, ids=ids)\n",
|
||||
"\n",
|
||||
"# use filter on search\n",
|
||||
"query_vector = embedding.embed_query(\"I'd like a fruit.\")\n",
|
||||
"docs = custom_store.similarity_search_by_vector(query_vector, filter=\"len >= 6\")\n",
|
||||
"\n",
|
||||
"print(docs)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": [],
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"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.11.5"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
399
docs/docs/integrations/vectorstores/google_firestore.ipynb
Normal file
399
docs/docs/integrations/vectorstores/google_firestore.ipynb
Normal file
@@ -0,0 +1,399 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "raw",
|
||||
"id": "1957f5cb",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"---\n",
|
||||
"sidebar_label: Firestore\n",
|
||||
"---"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "ef1f0986",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Google Firestore (Native Mode)\n",
|
||||
"\n",
|
||||
"> [Firestore](https://cloud.google.com/firestore) is a serverless document-oriented database that scales to meet any demand. Extend your database application to build AI-powered experiences leveraging Firestore's Langchain integrations.\n",
|
||||
"\n",
|
||||
"This notebook goes over how to use [Firestore](https://cloud.google.com/firestore) to to store vectors and query them using the `FirestoreVectorStore` class.\n",
|
||||
"\n",
|
||||
"[](https://colab.research.google.com/github/googleapis/langchain-google-firestore-python/blob/main/docs/vectorstores.ipynb)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "36fdc060",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Before You Begin\n",
|
||||
"\n",
|
||||
"To run this notebook, you will need to do the following:\n",
|
||||
"\n",
|
||||
"* [Create a Google Cloud Project](https://developers.google.com/workspace/guides/create-project)\n",
|
||||
"* [Enable the Firestore API](https://console.cloud.google.com/flows/enableapi?apiid=firestore.googleapis.com)\n",
|
||||
"* [Create a Firestore database](https://cloud.google.com/firestore/docs/manage-databases)\n",
|
||||
"\n",
|
||||
"After confirmed access to database in the runtime environment of this notebook, filling the following values and run the cell before running example scripts."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "22e53b34",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# @markdown Please specify a source for demo purpose.\n",
|
||||
"COLLECTION_NAME = \"test\" # @param {type:\"CollectionReference\"|\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "e5d3d8e4",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### 🦜🔗 Library Installation\n",
|
||||
"\n",
|
||||
"The integration lives in its own `langchain-google-firestore` package, so we need to install it. For this notebook, we will also install `langchain-google-genai` to use Google Generative AI embeddings."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "75510ef7",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install -upgrade --quiet langchain-google-firestore langchain-google-vertexai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "2664ca45",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Colab only**: Uncomment the following cell to restart the kernel or use the button to restart the kernel. For Vertex AI Workbench you can restart the terminal using the button on top."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "ddfcd6b7",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# # Automatically restart kernel after installs so that your environment can access the new packages\n",
|
||||
"# import IPython\n",
|
||||
"\n",
|
||||
"# app = IPython.Application.instance()\n",
|
||||
"# app.kernel.do_shutdown(True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "4ab63daa",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### ☁ Set Your Google Cloud Project\n",
|
||||
"Set your Google Cloud project so that you can leverage Google Cloud resources within this notebook.\n",
|
||||
"\n",
|
||||
"If you don't know your project ID, try the following:\n",
|
||||
"\n",
|
||||
"* Run `gcloud config list`.\n",
|
||||
"* Run `gcloud projects list`.\n",
|
||||
"* See the support page: [Locate the project ID](https://support.google.com/googleapi/answer/7014113)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "129f1f8d",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# @markdown Please fill in the value below with your Google Cloud project ID and then run the cell.\n",
|
||||
"\n",
|
||||
"PROJECT_ID = \"extensions-testing\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# Set the project id\n",
|
||||
"!gcloud config set project {PROJECT_ID}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "ccd32ce5",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### 🔐 Authentication\n",
|
||||
"\n",
|
||||
"Authenticate to Google Cloud as the IAM user logged into this notebook in order to access your Google Cloud Project.\n",
|
||||
"\n",
|
||||
"- If you are using Colab to run this notebook, use the cell below and continue.\n",
|
||||
"- If you are using Vertex AI Workbench, check out the setup instructions [here](https://github.com/GoogleCloudPlatform/generative-ai/tree/main/setup-env)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "4b5793e7",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.colab import auth\n",
|
||||
"\n",
|
||||
"auth.authenticate_user()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "2cade39f",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Basic Usage"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "580e6f96",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Initialize FirestoreVectorStore\n",
|
||||
"\n",
|
||||
"`FirestoreVectorStore` allows you to store new vectors in a Firestore database. You can use it to store embeddings from any model, including those from Google Generative AI."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "dc37144c-208d-4ab3-9f3a-0407a69fe052",
|
||||
"metadata": {
|
||||
"tags": []
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_google_firestore import FirestoreVectorStore\n",
|
||||
"from langchain_google_vertexai import VertexAIEmbeddings\n",
|
||||
"\n",
|
||||
"embedding = VertexAIEmbeddings(\n",
|
||||
" model_name=\"textembedding-gecko@latest\",\n",
|
||||
" project=PROJECT_ID,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Sample data\n",
|
||||
"ids = [\"apple\", \"banana\", \"orange\"]\n",
|
||||
"fruits_texts = ['{\"name\": \"apple\"}', '{\"name\": \"banana\"}', '{\"name\": \"orange\"}']\n",
|
||||
"\n",
|
||||
"# Create a vector store\n",
|
||||
"vector_store = FirestoreVectorStore(\n",
|
||||
" collection=\"fruits\",\n",
|
||||
" embedding=embedding,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Add the fruits to the vector store\n",
|
||||
"vector_store.add_texts(fruits_texts, ids=ids)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "f8a4d7f7",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"As a shorthand, you can initilize and add vectors in a single step using the `from_texts` and `from_documents` method."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "0bb6745e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"vector_store = FirestoreVectorStore.from_texts(\n",
|
||||
" collection=\"fruits\",\n",
|
||||
" texts=fruits_texts,\n",
|
||||
" embedding=embedding,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "f86024b9",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_core.documents import Document\n",
|
||||
"\n",
|
||||
"fruits_docs = [Document(page_content=fruit) for fruit in fruits_texts]\n",
|
||||
"\n",
|
||||
"vector_store = FirestoreVectorStore.from_documents(\n",
|
||||
" collection=\"fruits\",\n",
|
||||
" documents=fruits_docs,\n",
|
||||
" embedding=embedding,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "942911a8",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Delete Vectors"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "ee1d8090",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"You can delete documents with vectors from the database using the `delete` method. You'll need to provide the document ID of the vector you want to delete. This will remove the whole document from the database, including any other fields it may have."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "901f2ae7",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"vector_store.delete(ids)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "bc8e555f",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Update Vectors"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "af734e8f",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Updating vectors is similar to adding them. You can use the `add` method to update the vector of a document by providing the document ID and the new vector."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "cb2aadb7",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"fruit_to_update = ['{\"name\": \"apple\",\"price\": 12}']\n",
|
||||
"apple_id = \"apple\"\n",
|
||||
"\n",
|
||||
"vector_store.add_texts(fruit_to_update, ids=[apple_id])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "16342b7a",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Similarity Search\n",
|
||||
"\n",
|
||||
"You can use the `FirestoreVectorStore` to perform similarity searches on the vectors you have stored. This is useful for finding similar documents or text."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "44d1b94e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"vector_store.similarity_search(\"I like fuji apples\", k=3)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "acb2f640",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"vector_store.max_marginal_relevance_search(\"fuji\", 5)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "4ac1d391",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"You can add a pre-filter to the search by using the `filters` parameter. This is useful for filtering by a specific field or value."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "cd864d4f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud.firestore_v1.base_query import FieldFilter\n",
|
||||
"\n",
|
||||
"vector_store.max_marginal_relevance_search(\n",
|
||||
" \"fuji\", 5, filters=FieldFilter(\"content\", \"==\", \"apple\")\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "9988c71d",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Customize Connection & Authentication"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "6b9dfc65",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.api_core.client_options import ClientOptions\n",
|
||||
"from google.cloud import firestore\n",
|
||||
"from langchain_google_firestore import FirestoreVectorStore\n",
|
||||
"\n",
|
||||
"client_options = ClientOptions()\n",
|
||||
"client = firestore.Client(client_options=client_options)\n",
|
||||
"\n",
|
||||
"# Create a vector store\n",
|
||||
"vector_store = FirestoreVectorStore(\n",
|
||||
" collection=\"fruits\",\n",
|
||||
" embedding=embedding,\n",
|
||||
" client=client,\n",
|
||||
")"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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.11.7"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -2,597 +2,442 @@
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "7679dd7b-7ed4-4755-a499-824deadba708",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# PGVector\n",
|
||||
"\n",
|
||||
">[PGVector](https://github.com/pgvector/pgvector) is an open-source vector similarity search for `Postgres`\n",
|
||||
"> An implementation of LangChain vectorstore abstraction using `postgres` as the backend and utilizing the `pgvector` extension.\n",
|
||||
"\n",
|
||||
"It supports:\n",
|
||||
"- exact and approximate nearest neighbor search\n",
|
||||
"- L2 distance, inner product, and cosine distance\n",
|
||||
"The code lives in an integration package called: [langchain_postgres](https://github.com/langchain-ai/langchain-postgres/).\n",
|
||||
"\n",
|
||||
"This notebook shows how to use the Postgres vector database (`PGVector`)."
|
||||
"You can run the following command to spin up a a postgres container with the `pgvector` extension:\n",
|
||||
"\n",
|
||||
"```shell\n",
|
||||
"docker run --name pgvector-container -e POSTGRES_USER=langchain -e POSTGRES_PASSWORD=langchain -e POSTGRES_DB=langchain -p 6024:5432 -d pgvector/pgvector:pg16\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"## Status\n",
|
||||
"\n",
|
||||
"This code has been ported over from `langchain_community` into a dedicated package called `langchain-postgres`. The following changes have been made:\n",
|
||||
"\n",
|
||||
"* langchain_postgres works only with psycopg3. Please update your connnecion strings from `postgresql+psycopg2://...` to `postgresql+psycopg://langchain:langchain@...` (yes, it's the driver name is `psycopg` not `psycopg3`, but it'll use `psycopg3`.\n",
|
||||
"* The schema of the embedding store and collection have been changed to make add_documents work correctly with user specified ids.\n",
|
||||
"* One has to pass an explicit connection object now.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"Currently, there is **no mechanism** that supports easy data migration on schema changes. So any schema changes in the vectorstore will require the user to recreate the tables and re-add the documents.\n",
|
||||
"If this is a concern, please use a different vectorstore. If not, this implementation should be fine for your use case."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "342cd5e9-f349-42b4-9713-12e63779835b",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"See the [installation instruction](https://github.com/pgvector/pgvector)."
|
||||
"## Install dependencies\n",
|
||||
"\n",
|
||||
"Here, we're using `langchain_cohere` for embeddings, but you can use other embeddings providers."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 1,
|
||||
"id": "42d42297-11b8-44e3-bf21-7c3d1bce8277",
|
||||
"metadata": {
|
||||
"tags": []
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Pip install necessary package\n",
|
||||
"%pip install --upgrade --quiet pgvector\n",
|
||||
"%pip install --upgrade --quiet langchain-openai\n",
|
||||
"%pip install --upgrade --quiet psycopg2-binary\n",
|
||||
"%pip install --upgrade --quiet tiktoken"
|
||||
"!pip install --quiet -U langchain_cohere\n",
|
||||
"!pip install --quiet -U langchain_postgres"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "eee31ce1-2c28-484d-82be-d22d9f9a31fd",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"We want to use `OpenAIEmbeddings` so we have to get the OpenAI API Key."
|
||||
"## Initialize the vectorstore"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "979a65bd-742f-4b0d-be1e-c0baae245ec6",
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2023-09-09T08:02:16.802456Z",
|
||||
"start_time": "2023-09-09T08:02:07.065604Z"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import getpass\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"os.environ[\"OPENAI_API_KEY\"] = getpass.getpass(\"OpenAI API Key:\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2023-09-09T08:02:19.742896Z",
|
||||
"start_time": "2023-09-09T08:02:19.732527Z"
|
||||
},
|
||||
"tags": []
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": "False"
|
||||
},
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"## Loading Environment Variables\n",
|
||||
"from dotenv import load_dotenv\n",
|
||||
"\n",
|
||||
"load_dotenv()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2023-09-09T08:02:23.144824Z",
|
||||
"start_time": "2023-09-09T08:02:22.047801Z"
|
||||
},
|
||||
"tags": []
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.docstore.document import Document\n",
|
||||
"from langchain_community.document_loaders import TextLoader\n",
|
||||
"from langchain_community.vectorstores.pgvector import PGVector\n",
|
||||
"from langchain_openai import OpenAIEmbeddings\n",
|
||||
"from langchain_text_splitters import CharacterTextSplitter"
|
||||
"from langchain_cohere import CohereEmbeddings\n",
|
||||
"from langchain_core.documents import Document\n",
|
||||
"from langchain_postgres import PGVector\n",
|
||||
"from langchain_postgres.vectorstores import PGVector\n",
|
||||
"\n",
|
||||
"# See docker command above to launch a postgres instance with pgvector enabled.\n",
|
||||
"connection = \"postgresql+psycopg://langchain:langchain@localhost:6024/langchain\" # Uses psycopg3!\n",
|
||||
"collection_name = \"my_docs\"\n",
|
||||
"embeddings = CohereEmbeddings()\n",
|
||||
"\n",
|
||||
"vectorstore = PGVector(\n",
|
||||
" embeddings=embeddings,\n",
|
||||
" collection_name=collection_name,\n",
|
||||
" connection=connection,\n",
|
||||
" use_jsonb=True,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "0fc32168-5a82-4629-a78d-158fe2615086",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Drop tables\n",
|
||||
"\n",
|
||||
"If you need to drop tables (e.g., updating the embedding to a different dimension or just updating the embedding provider): "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "5de5ef98-7dbb-4892-853f-47c7dc87b70e",
|
||||
"metadata": {
|
||||
"tags": []
|
||||
},
|
||||
"source": [
|
||||
"```python\n",
|
||||
"vectorstore.drop_tables()\n",
|
||||
"````"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "61a224a1-d70b-4daf-86ba-ab6e43c08b50",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Add documents\n",
|
||||
"\n",
|
||||
"Add documents to the vectorstore"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "88a288cc-ffd4-4800-b011-750c72b9fd10",
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2023-09-09T08:02:25.452472Z",
|
||||
"start_time": "2023-09-09T08:02:25.441563Z"
|
||||
}
|
||||
"tags": []
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"loader = TextLoader(\"../../modules/state_of_the_union.txt\")\n",
|
||||
"documents = loader.load()\n",
|
||||
"text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)\n",
|
||||
"docs = text_splitter.split_documents(documents)\n",
|
||||
"\n",
|
||||
"embeddings = OpenAIEmbeddings()"
|
||||
"docs = [\n",
|
||||
" Document(\n",
|
||||
" page_content=\"there are cats in the pond\",\n",
|
||||
" metadata={\"id\": 1, \"location\": \"pond\", \"topic\": \"animals\"},\n",
|
||||
" ),\n",
|
||||
" Document(\n",
|
||||
" page_content=\"ducks are also found in the pond\",\n",
|
||||
" metadata={\"id\": 2, \"location\": \"pond\", \"topic\": \"animals\"},\n",
|
||||
" ),\n",
|
||||
" Document(\n",
|
||||
" page_content=\"fresh apples are available at the market\",\n",
|
||||
" metadata={\"id\": 3, \"location\": \"market\", \"topic\": \"food\"},\n",
|
||||
" ),\n",
|
||||
" Document(\n",
|
||||
" page_content=\"the market also sells fresh oranges\",\n",
|
||||
" metadata={\"id\": 4, \"location\": \"market\", \"topic\": \"food\"},\n",
|
||||
" ),\n",
|
||||
" Document(\n",
|
||||
" page_content=\"the new art exhibit is fascinating\",\n",
|
||||
" metadata={\"id\": 5, \"location\": \"museum\", \"topic\": \"art\"},\n",
|
||||
" ),\n",
|
||||
" Document(\n",
|
||||
" page_content=\"a sculpture exhibit is also at the museum\",\n",
|
||||
" metadata={\"id\": 6, \"location\": \"museum\", \"topic\": \"art\"},\n",
|
||||
" ),\n",
|
||||
" Document(\n",
|
||||
" page_content=\"a new coffee shop opened on Main Street\",\n",
|
||||
" metadata={\"id\": 7, \"location\": \"Main Street\", \"topic\": \"food\"},\n",
|
||||
" ),\n",
|
||||
" Document(\n",
|
||||
" page_content=\"the book club meets at the library\",\n",
|
||||
" metadata={\"id\": 8, \"location\": \"library\", \"topic\": \"reading\"},\n",
|
||||
" ),\n",
|
||||
" Document(\n",
|
||||
" page_content=\"the library hosts a weekly story time for kids\",\n",
|
||||
" metadata={\"id\": 9, \"location\": \"library\", \"topic\": \"reading\"},\n",
|
||||
" ),\n",
|
||||
" Document(\n",
|
||||
" page_content=\"a cooking class for beginners is offered at the community center\",\n",
|
||||
" metadata={\"id\": 10, \"location\": \"community center\", \"topic\": \"classes\"},\n",
|
||||
" ),\n",
|
||||
"]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "73aa9124-9d49-4e10-8ed3-82255e7a4106",
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2023-09-09T08:02:28.174088Z",
|
||||
"start_time": "2023-09-09T08:02:28.162698Z"
|
||||
"tags": []
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"
|
||||
]
|
||||
},
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
],
|
||||
"source": [
|
||||
"# PGVector needs the connection string to the database.\n",
|
||||
"CONNECTION_STRING = \"postgresql+psycopg2://harrisonchase@localhost:5432/test3\"\n",
|
||||
"\n",
|
||||
"# # Alternatively, you can create it from environment variables.\n",
|
||||
"# import os\n",
|
||||
"\n",
|
||||
"# CONNECTION_STRING = PGVector.connection_string_from_db_params(\n",
|
||||
"# driver=os.environ.get(\"PGVECTOR_DRIVER\", \"psycopg2\"),\n",
|
||||
"# host=os.environ.get(\"PGVECTOR_HOST\", \"localhost\"),\n",
|
||||
"# port=int(os.environ.get(\"PGVECTOR_PORT\", \"5432\")),\n",
|
||||
"# database=os.environ.get(\"PGVECTOR_DATABASE\", \"postgres\"),\n",
|
||||
"# user=os.environ.get(\"PGVECTOR_USER\", \"postgres\"),\n",
|
||||
"# password=os.environ.get(\"PGVECTOR_PASSWORD\", \"postgres\"),\n",
|
||||
"# )"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"source": [
|
||||
"## Similarity Search with Euclidean Distance (Default)"
|
||||
"vectorstore.add_documents(docs, ids=[doc.metadata[\"id\"] for doc in docs])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "a5b2b71f-49eb-407d-b03a-dea4c0a517d6",
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2023-09-09T08:04:16.696625Z",
|
||||
"start_time": "2023-09-09T08:02:31.817790Z"
|
||||
}
|
||||
"tags": []
|
||||
},
|
||||
"outputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[Document(page_content='there are cats in the pond', metadata={'id': 1, 'topic': 'animals', 'location': 'pond'}),\n",
|
||||
" Document(page_content='the book club meets at the library', metadata={'id': 8, 'topic': 'reading', 'location': 'library'}),\n",
|
||||
" Document(page_content='the library hosts a weekly story time for kids', metadata={'id': 9, 'topic': 'reading', 'location': 'library'}),\n",
|
||||
" Document(page_content='the new art exhibit is fascinating', metadata={'id': 5, 'topic': 'art', 'location': 'museum'}),\n",
|
||||
" Document(page_content='ducks are also found in the pond', metadata={'id': 2, 'topic': 'animals', 'location': 'pond'}),\n",
|
||||
" Document(page_content='the market also sells fresh oranges', metadata={'id': 4, 'topic': 'food', 'location': 'market'}),\n",
|
||||
" Document(page_content='a cooking class for beginners is offered at the community center', metadata={'id': 10, 'topic': 'classes', 'location': 'community center'}),\n",
|
||||
" Document(page_content='fresh apples are available at the market', metadata={'id': 3, 'topic': 'food', 'location': 'market'}),\n",
|
||||
" Document(page_content='a sculpture exhibit is also at the museum', metadata={'id': 6, 'topic': 'art', 'location': 'museum'}),\n",
|
||||
" Document(page_content='a new coffee shop opened on Main Street', metadata={'id': 7, 'topic': 'food', 'location': 'Main Street'})]"
|
||||
]
|
||||
},
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# The PGVector Module will try to create a table with the name of the collection.\n",
|
||||
"# So, make sure that the collection name is unique and the user has the permission to create a table.\n",
|
||||
"\n",
|
||||
"COLLECTION_NAME = \"state_of_the_union_test\"\n",
|
||||
"\n",
|
||||
"db = PGVector.from_documents(\n",
|
||||
" embedding=embeddings,\n",
|
||||
" documents=docs,\n",
|
||||
" collection_name=COLLECTION_NAME,\n",
|
||||
" connection_string=CONNECTION_STRING,\n",
|
||||
")"
|
||||
"vectorstore.similarity_search(\"kitty\", k=10)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "1d87a413-015a-4b46-a64e-332f30806524",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Adding documents by ID will over-write any existing documents that match that ID."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "13c69357-aaee-4de0-bcc2-7ab4419c920e",
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2023-09-09T08:05:11.104135Z",
|
||||
"start_time": "2023-09-09T08:05:10.548998Z"
|
||||
}
|
||||
"tags": []
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"query = \"What did the president say about Ketanji Brown Jackson\"\n",
|
||||
"docs_with_score = db.similarity_search_with_score(query)"
|
||||
"docs = [\n",
|
||||
" Document(\n",
|
||||
" page_content=\"there are cats in the pond\",\n",
|
||||
" metadata={\"id\": 1, \"location\": \"pond\", \"topic\": \"animals\"},\n",
|
||||
" ),\n",
|
||||
" Document(\n",
|
||||
" page_content=\"ducks are also found in the pond\",\n",
|
||||
" metadata={\"id\": 2, \"location\": \"pond\", \"topic\": \"animals\"},\n",
|
||||
" ),\n",
|
||||
" Document(\n",
|
||||
" page_content=\"fresh apples are available at the market\",\n",
|
||||
" metadata={\"id\": 3, \"location\": \"market\", \"topic\": \"food\"},\n",
|
||||
" ),\n",
|
||||
" Document(\n",
|
||||
" page_content=\"the market also sells fresh oranges\",\n",
|
||||
" metadata={\"id\": 4, \"location\": \"market\", \"topic\": \"food\"},\n",
|
||||
" ),\n",
|
||||
" Document(\n",
|
||||
" page_content=\"the new art exhibit is fascinating\",\n",
|
||||
" metadata={\"id\": 5, \"location\": \"museum\", \"topic\": \"art\"},\n",
|
||||
" ),\n",
|
||||
" Document(\n",
|
||||
" page_content=\"a sculpture exhibit is also at the museum\",\n",
|
||||
" metadata={\"id\": 6, \"location\": \"museum\", \"topic\": \"art\"},\n",
|
||||
" ),\n",
|
||||
" Document(\n",
|
||||
" page_content=\"a new coffee shop opened on Main Street\",\n",
|
||||
" metadata={\"id\": 7, \"location\": \"Main Street\", \"topic\": \"food\"},\n",
|
||||
" ),\n",
|
||||
" Document(\n",
|
||||
" page_content=\"the book club meets at the library\",\n",
|
||||
" metadata={\"id\": 8, \"location\": \"library\", \"topic\": \"reading\"},\n",
|
||||
" ),\n",
|
||||
" Document(\n",
|
||||
" page_content=\"the library hosts a weekly story time for kids\",\n",
|
||||
" metadata={\"id\": 9, \"location\": \"library\", \"topic\": \"reading\"},\n",
|
||||
" ),\n",
|
||||
" Document(\n",
|
||||
" page_content=\"a cooking class for beginners is offered at the community center\",\n",
|
||||
" metadata={\"id\": 10, \"location\": \"community center\", \"topic\": \"classes\"},\n",
|
||||
" ),\n",
|
||||
"]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "59f82250-7903-4279-8300-062542c83416",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Filtering Support\n",
|
||||
"\n",
|
||||
"The vectorstore supports a set of filters that can be applied against the metadata fields of the documents.\n",
|
||||
"\n",
|
||||
"| Operator | Meaning/Category |\n",
|
||||
"|----------|-------------------------|\n",
|
||||
"| \\$eq | Equality (==) |\n",
|
||||
"| \\$ne | Inequality (!=) |\n",
|
||||
"| \\$lt | Less than (<) |\n",
|
||||
"| \\$lte | Less than or equal (<=) |\n",
|
||||
"| \\$gt | Greater than (>) |\n",
|
||||
"| \\$gte | Greater than or equal (>=) |\n",
|
||||
"| \\$in | Special Cased (in) |\n",
|
||||
"| \\$nin | Special Cased (not in) |\n",
|
||||
"| \\$between | Special Cased (between) |\n",
|
||||
"| \\$like | Text (like) |\n",
|
||||
"| \\$ilike | Text (case-insensitive like) |\n",
|
||||
"| \\$and | Logical (and) |\n",
|
||||
"| \\$or | Logical (or) |"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"id": "f15a2359-6dc3-4099-8214-785f167a9ca4",
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2023-09-09T08:05:13.532334Z",
|
||||
"start_time": "2023-09-09T08:05:13.523191Z"
|
||||
}
|
||||
"tags": []
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"Score: 0.18456886638850434\n",
|
||||
"Tonight. I call on the Senate to: Pass the Freedom to Vote Act. Pass the John Lewis Voting Rights Act. And while you’re at it, pass the Disclose Act so Americans can know who is funding our elections. \n",
|
||||
"\n",
|
||||
"Tonight, I’d like to honor someone who has dedicated his life to serve this country: Justice Stephen Breyer—an Army veteran, Constitutional scholar, and retiring Justice of the United States Supreme Court. Justice Breyer, thank you for your service. \n",
|
||||
"\n",
|
||||
"One of the most serious constitutional responsibilities a President has is nominating someone to serve on the United States Supreme Court. \n",
|
||||
"\n",
|
||||
"And I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson. One of our nation’s top legal minds, who will continue Justice Breyer’s legacy of excellence.\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"Score: 0.21742627672631343\n",
|
||||
"A former top litigator in private practice. A former federal public defender. And from a family of public school educators and police officers. A consensus builder. Since she’s been nominated, she’s received a broad range of support—from the Fraternal Order of Police to former judges appointed by Democrats and Republicans. \n",
|
||||
"\n",
|
||||
"And if we are to advance liberty and justice, we need to secure the Border and fix the immigration system. \n",
|
||||
"\n",
|
||||
"We can do both. At our border, we’ve installed new technology like cutting-edge scanners to better detect drug smuggling. \n",
|
||||
"\n",
|
||||
"We’ve set up joint patrols with Mexico and Guatemala to catch more human traffickers. \n",
|
||||
"\n",
|
||||
"We’re putting in place dedicated immigration judges so families fleeing persecution and violence can have their cases heard faster. \n",
|
||||
"\n",
|
||||
"We’re securing commitments and supporting partners in South and Central America to host more refugees and secure their own borders.\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"Score: 0.22641793174529334\n",
|
||||
"And for our LGBTQ+ Americans, let’s finally get the bipartisan Equality Act to my desk. The onslaught of state laws targeting transgender Americans and their families is wrong. \n",
|
||||
"\n",
|
||||
"As I said last year, especially to our younger transgender Americans, I will always have your back as your President, so you can be yourself and reach your God-given potential. \n",
|
||||
"\n",
|
||||
"While it often appears that we never agree, that isn’t true. I signed 80 bipartisan bills into law last year. From preventing government shutdowns to protecting Asian-Americans from still-too-common hate crimes to reforming military justice. \n",
|
||||
"\n",
|
||||
"And soon, we’ll strengthen the Violence Against Women Act that I first wrote three decades ago. It is important for us to show the nation that we can come together and do big things. \n",
|
||||
"\n",
|
||||
"So tonight I’m offering a Unity Agenda for the Nation. Four big things we can do together. \n",
|
||||
"\n",
|
||||
"First, beat the opioid epidemic.\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"Score: 0.22670040608054465\n",
|
||||
"Tonight, I’m announcing a crackdown on these companies overcharging American businesses and consumers. \n",
|
||||
"\n",
|
||||
"And as Wall Street firms take over more nursing homes, quality in those homes has gone down and costs have gone up. \n",
|
||||
"\n",
|
||||
"That ends on my watch. \n",
|
||||
"\n",
|
||||
"Medicare is going to set higher standards for nursing homes and make sure your loved ones get the care they deserve and expect. \n",
|
||||
"\n",
|
||||
"We’ll also cut costs and keep the economy going strong by giving workers a fair shot, provide more training and apprenticeships, hire them based on their skills not degrees. \n",
|
||||
"\n",
|
||||
"Let’s pass the Paycheck Fairness Act and paid leave. \n",
|
||||
"\n",
|
||||
"Raise the minimum wage to $15 an hour and extend the Child Tax Credit, so no one has to raise a family in poverty. \n",
|
||||
"\n",
|
||||
"Let’s increase Pell Grants and increase our historic support of HBCUs, and invest in what Jill—our First Lady who teaches full-time—calls America’s best-kept secret: community colleges.\n",
|
||||
"--------------------------------------------------------------------------------\n"
|
||||
]
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[Document(page_content='there are cats in the pond', metadata={'id': 1, 'topic': 'animals', 'location': 'pond'}),\n",
|
||||
" Document(page_content='the library hosts a weekly story time for kids', metadata={'id': 9, 'topic': 'reading', 'location': 'library'}),\n",
|
||||
" Document(page_content='the new art exhibit is fascinating', metadata={'id': 5, 'topic': 'art', 'location': 'museum'}),\n",
|
||||
" Document(page_content='ducks are also found in the pond', metadata={'id': 2, 'topic': 'animals', 'location': 'pond'})]"
|
||||
]
|
||||
},
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"for doc, score in docs_with_score:\n",
|
||||
" print(\"-\" * 80)\n",
|
||||
" print(\"Score: \", score)\n",
|
||||
" print(doc.page_content)\n",
|
||||
" print(\"-\" * 80)"
|
||||
"vectorstore.similarity_search(\"kitty\", k=10, filter={\"id\": {\"$in\": [1, 5, 2, 9]}})"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"id": "d92ea049-1b1f-4ae9-9525-35750fe2e52e",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Maximal Marginal Relevance Search (MMR)\n",
|
||||
"Maximal marginal relevance optimizes for similarity to query AND diversity among selected documents."
|
||||
"If you provide a dict with multiple fields, but no operators, the top level will be interpreted as a logical **AND** filter"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"id": "88f919e4-e4b0-4b5f-99b3-24c675c26d33",
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2023-09-09T08:05:23.276819Z",
|
||||
"start_time": "2023-09-09T08:05:21.972256Z"
|
||||
},
|
||||
"collapsed": false
|
||||
"tags": []
|
||||
},
|
||||
"outputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[Document(page_content='ducks are also found in the pond', metadata={'id': 2, 'topic': 'animals', 'location': 'pond'}),\n",
|
||||
" Document(page_content='there are cats in the pond', metadata={'id': 1, 'topic': 'animals', 'location': 'pond'})]"
|
||||
]
|
||||
},
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"docs_with_score = db.max_marginal_relevance_search_with_score(query)"
|
||||
"vectorstore.similarity_search(\n",
|
||||
" \"ducks\",\n",
|
||||
" k=10,\n",
|
||||
" filter={\"id\": {\"$in\": [1, 5, 2, 9]}, \"location\": {\"$in\": [\"pond\", \"market\"]}},\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"id": "88f423a4-6575-4fb8-9be2-a3da01106591",
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2023-09-09T08:05:27.478580Z",
|
||||
"start_time": "2023-09-09T08:05:27.470138Z"
|
||||
},
|
||||
"collapsed": false
|
||||
"tags": []
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"Score: 0.18453882564037527\n",
|
||||
"Tonight. I call on the Senate to: Pass the Freedom to Vote Act. Pass the John Lewis Voting Rights Act. And while you’re at it, pass the Disclose Act so Americans can know who is funding our elections. \n",
|
||||
"\n",
|
||||
"Tonight, I’d like to honor someone who has dedicated his life to serve this country: Justice Stephen Breyer—an Army veteran, Constitutional scholar, and retiring Justice of the United States Supreme Court. Justice Breyer, thank you for your service. \n",
|
||||
"\n",
|
||||
"One of the most serious constitutional responsibilities a President has is nominating someone to serve on the United States Supreme Court. \n",
|
||||
"\n",
|
||||
"And I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson. One of our nation’s top legal minds, who will continue Justice Breyer’s legacy of excellence.\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"Score: 0.23523731441720075\n",
|
||||
"We can’t change how divided we’ve been. But we can change how we move forward—on COVID-19 and other issues we must face together. \n",
|
||||
"\n",
|
||||
"I recently visited the New York City Police Department days after the funerals of Officer Wilbert Mora and his partner, Officer Jason Rivera. \n",
|
||||
"\n",
|
||||
"They were responding to a 9-1-1 call when a man shot and killed them with a stolen gun. \n",
|
||||
"\n",
|
||||
"Officer Mora was 27 years old. \n",
|
||||
"\n",
|
||||
"Officer Rivera was 22. \n",
|
||||
"\n",
|
||||
"Both Dominican Americans who’d grown up on the same streets they later chose to patrol as police officers. \n",
|
||||
"\n",
|
||||
"I spoke with their families and told them that we are forever in debt for their sacrifice, and we will carry on their mission to restore the trust and safety every community deserves. \n",
|
||||
"\n",
|
||||
"I’ve worked on these issues a long time. \n",
|
||||
"\n",
|
||||
"I know what works: Investing in crime prevention and community police officers who’ll walk the beat, who’ll know the neighborhood, and who can restore trust and safety.\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"Score: 0.2448441215698569\n",
|
||||
"One was stationed at bases and breathing in toxic smoke from “burn pits” that incinerated wastes of war—medical and hazard material, jet fuel, and more. \n",
|
||||
"\n",
|
||||
"When they came home, many of the world’s fittest and best trained warriors were never the same. \n",
|
||||
"\n",
|
||||
"Headaches. Numbness. Dizziness. \n",
|
||||
"\n",
|
||||
"A cancer that would put them in a flag-draped coffin. \n",
|
||||
"\n",
|
||||
"I know. \n",
|
||||
"\n",
|
||||
"One of those soldiers was my son Major Beau Biden. \n",
|
||||
"\n",
|
||||
"We don’t know for sure if a burn pit was the cause of his brain cancer, or the diseases of so many of our troops. \n",
|
||||
"\n",
|
||||
"But I’m committed to finding out everything we can. \n",
|
||||
"\n",
|
||||
"Committed to military families like Danielle Robinson from Ohio. \n",
|
||||
"\n",
|
||||
"The widow of Sergeant First Class Heath Robinson. \n",
|
||||
"\n",
|
||||
"He was born a soldier. Army National Guard. Combat medic in Kosovo and Iraq. \n",
|
||||
"\n",
|
||||
"Stationed near Baghdad, just yards from burn pits the size of football fields. \n",
|
||||
"\n",
|
||||
"Heath’s widow Danielle is here with us tonight. They loved going to Ohio State football games. He loved building Legos with their daughter.\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"Score: 0.2513994424701056\n",
|
||||
"And I’m taking robust action to make sure the pain of our sanctions is targeted at Russia’s economy. And I will use every tool at our disposal to protect American businesses and consumers. \n",
|
||||
"\n",
|
||||
"Tonight, I can announce that the United States has worked with 30 other countries to release 60 Million barrels of oil from reserves around the world. \n",
|
||||
"\n",
|
||||
"America will lead that effort, releasing 30 Million barrels from our own Strategic Petroleum Reserve. And we stand ready to do more if necessary, unified with our allies. \n",
|
||||
"\n",
|
||||
"These steps will help blunt gas prices here at home. And I know the news about what’s happening can seem alarming. \n",
|
||||
"\n",
|
||||
"But I want you to know that we are going to be okay. \n",
|
||||
"\n",
|
||||
"When the history of this era is written Putin’s war on Ukraine will have left Russia weaker and the rest of the world stronger. \n",
|
||||
"\n",
|
||||
"While it shouldn’t have taken something so terrible for people around the world to see what’s at stake now everyone sees it clearly.\n",
|
||||
"--------------------------------------------------------------------------------\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"for doc, score in docs_with_score:\n",
|
||||
" print(\"-\" * 80)\n",
|
||||
" print(\"Score: \", score)\n",
|
||||
" print(doc.page_content)\n",
|
||||
" print(\"-\" * 80)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Working with vectorstore\n",
|
||||
"\n",
|
||||
"Above, we created a vectorstore from scratch. However, often times we want to work with an existing vectorstore.\n",
|
||||
"In order to do that, we can initialize it directly."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"store = PGVector(\n",
|
||||
" collection_name=COLLECTION_NAME,\n",
|
||||
" connection_string=CONNECTION_STRING,\n",
|
||||
" embedding_function=embeddings,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Add documents\n",
|
||||
"We can add documents to the existing vectorstore."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 19,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"['048c2e14-1cf3-11ee-8777-e65801318980']"
|
||||
"[Document(page_content='ducks are also found in the pond', metadata={'id': 2, 'topic': 'animals', 'location': 'pond'}),\n",
|
||||
" Document(page_content='there are cats in the pond', metadata={'id': 1, 'topic': 'animals', 'location': 'pond'})]"
|
||||
]
|
||||
},
|
||||
"execution_count": 19,
|
||||
"execution_count": 11,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"store.add_documents([Document(page_content=\"foo\")])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 20,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"docs_with_score = db.similarity_search_with_score(\"foo\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 21,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"(Document(page_content='foo', metadata={}), 3.3203430005457335e-09)"
|
||||
]
|
||||
},
|
||||
"execution_count": 21,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"docs_with_score[0]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 22,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"(Document(page_content='A former top litigator in private practice. A former federal public defender. And from a family of public school educators and police officers. A consensus builder. Since she’s been nominated, she’s received a broad range of support—from the Fraternal Order of Police to former judges appointed by Democrats and Republicans. \\n\\nAnd if we are to advance liberty and justice, we need to secure the Border and fix the immigration system. \\n\\nWe can do both. At our border, we’ve installed new technology like cutting-edge scanners to better detect drug smuggling. \\n\\nWe’ve set up joint patrols with Mexico and Guatemala to catch more human traffickers. \\n\\nWe’re putting in place dedicated immigration judges so families fleeing persecution and violence can have their cases heard faster. \\n\\nWe’re securing commitments and supporting partners in South and Central America to host more refugees and secure their own borders.', metadata={'source': '../../../state_of_the_union.txt'}),\n",
|
||||
" 0.2404395365581814)"
|
||||
]
|
||||
},
|
||||
"execution_count": 22,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"docs_with_score[1]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Overriding a vectorstore\n",
|
||||
"\n",
|
||||
"If you have an existing collection, you override it by doing `from_documents` and setting `pre_delete_collection` = True"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 23,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"db = PGVector.from_documents(\n",
|
||||
" documents=docs,\n",
|
||||
" embedding=embeddings,\n",
|
||||
" collection_name=COLLECTION_NAME,\n",
|
||||
" connection_string=CONNECTION_STRING,\n",
|
||||
" pre_delete_collection=True,\n",
|
||||
"vectorstore.similarity_search(\n",
|
||||
" \"ducks\",\n",
|
||||
" k=10,\n",
|
||||
" filter={\n",
|
||||
" \"$and\": [\n",
|
||||
" {\"id\": {\"$in\": [1, 5, 2, 9]}},\n",
|
||||
" {\"location\": {\"$in\": [\"pond\", \"market\"]}},\n",
|
||||
" ]\n",
|
||||
" },\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 24,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"docs_with_score = db.similarity_search_with_score(\"foo\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 25,
|
||||
"metadata": {},
|
||||
"execution_count": 12,
|
||||
"id": "65133340-2acd-4957-849e-029b6b5d60f0",
|
||||
"metadata": {
|
||||
"tags": []
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"(Document(page_content='A former top litigator in private practice. A former federal public defender. And from a family of public school educators and police officers. A consensus builder. Since she’s been nominated, she’s received a broad range of support—from the Fraternal Order of Police to former judges appointed by Democrats and Republicans. \\n\\nAnd if we are to advance liberty and justice, we need to secure the Border and fix the immigration system. \\n\\nWe can do both. At our border, we’ve installed new technology like cutting-edge scanners to better detect drug smuggling. \\n\\nWe’ve set up joint patrols with Mexico and Guatemala to catch more human traffickers. \\n\\nWe’re putting in place dedicated immigration judges so families fleeing persecution and violence can have their cases heard faster. \\n\\nWe’re securing commitments and supporting partners in South and Central America to host more refugees and secure their own borders.', metadata={'source': '../../../state_of_the_union.txt'}),\n",
|
||||
" 0.2404115088144465)"
|
||||
"[Document(page_content='the book club meets at the library', metadata={'id': 8, 'topic': 'reading', 'location': 'library'}),\n",
|
||||
" Document(page_content='the new art exhibit is fascinating', metadata={'id': 5, 'topic': 'art', 'location': 'museum'}),\n",
|
||||
" Document(page_content='the library hosts a weekly story time for kids', metadata={'id': 9, 'topic': 'reading', 'location': 'library'}),\n",
|
||||
" Document(page_content='a sculpture exhibit is also at the museum', metadata={'id': 6, 'topic': 'art', 'location': 'museum'}),\n",
|
||||
" Document(page_content='the market also sells fresh oranges', metadata={'id': 4, 'topic': 'food', 'location': 'market'}),\n",
|
||||
" Document(page_content='a cooking class for beginners is offered at the community center', metadata={'id': 10, 'topic': 'classes', 'location': 'community center'}),\n",
|
||||
" Document(page_content='a new coffee shop opened on Main Street', metadata={'id': 7, 'topic': 'food', 'location': 'Main Street'}),\n",
|
||||
" Document(page_content='fresh apples are available at the market', metadata={'id': 3, 'topic': 'food', 'location': 'market'})]"
|
||||
]
|
||||
},
|
||||
"execution_count": 25,
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"docs_with_score[0]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Using a VectorStore as a Retriever"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 26,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"retriever = store.as_retriever()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 27,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"tags=None metadata=None vectorstore=<langchain_community.vectorstores.pgvector.PGVector object at 0x29f94f880> search_type='similarity' search_kwargs={}\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(retriever)"
|
||||
"vectorstore.similarity_search(\"bird\", k=10, filter={\"location\": {\"$ne\": \"pond\"}})"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -612,9 +457,9 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.9.1"
|
||||
"version": "3.11.4"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ Our commentary on when you should consider using this agent type.
|
||||
|
||||
| Agent Type | Intended Model Type | Supports Chat History | Supports Multi-Input Tools | Supports Parallel Function Calling | Required Model Params | When to Use | API |
|
||||
|--------------------------------------------|---------------------|-----------------------|----------------------------|-------------------------------------|----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------|
|
||||
| [Tool Calling](/docs/modules/agents/agent_types/tool_calling) | Chat | ✅ | ✅ | ✅ | `tools` | If you are using a tool-calling model | TODO: Ref |
|
||||
| [Tool Calling](/docs/modules/agents/agent_types/tool_calling) | Chat | ✅ | ✅ | ✅ | `tools` | If you are using a tool-calling model | [Ref](https://api.python.langchain.com/en/latest/agents/langchain.agents.tool_calling_agent.base.create_tool_calling_agent.html) |
|
||||
| [OpenAI Tools](./openai_tools) | Chat | ✅ | ✅ | ✅ | `tools` | [Legacy] If you are using a recent OpenAI model (`1106` onwards). Generic Tool Calling agent recommended instead. | [Ref](https://api.python.langchain.com/en/latest/agents/langchain.agents.openai_tools.base.create_openai_tools_agent.html) |
|
||||
| [OpenAI Functions](./openai_functions_agent)| Chat | ✅ | ✅ | | `functions` | [Legacy] If you are using an OpenAI model, or an open-source model that has been finetuned for function calling and exposes the same `functions` parameters as OpenAI. Generic Tool Calling agent recommended instead | [Ref](https://api.python.langchain.com/en/latest/agents/langchain.agents.openai_functions_agent.base.create_openai_functions_agent.html) |
|
||||
| [XML](./xml_agent) | LLM | ✅ | | | | If you are using Anthropic models, or other models good at XML | [Ref](https://api.python.langchain.com/en/latest/agents/langchain.agents.xml.base.create_xml_agent.html) |
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
"\n",
|
||||
"## Setup\n",
|
||||
"\n",
|
||||
"Any models that support tool calling can be used in this agent. [TODO ADD WHICH]\n",
|
||||
"Any models that support tool calling can be used in this agent. You can see which models support tool calling [here](/docs/integrations/chat/)\n",
|
||||
"\n",
|
||||
"This demo uses [Tavily](https://app.tavily.com), but you can also swap in any other [built-in tool](/docs/integrations/tools) or add [custom tools](/docs/modules/tools/custom_tools/).\n",
|
||||
"You'll need to sign up for an API key and set it as `process.env.TAVILY_API_KEY`.\n",
|
||||
@@ -36,7 +36,10 @@
|
||||
"```{=mdx}\n",
|
||||
"import ChatModelTabs from \"@theme/ChatModelTabs\";\n",
|
||||
"\n",
|
||||
"<ChatModelTabs customVarName=\"llm\" />\n",
|
||||
"<ChatModelTabs\n",
|
||||
" customVarName=\"llm\"\n",
|
||||
" hideCohere\n",
|
||||
"/>\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
@@ -46,6 +49,9 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# | output: false\n",
|
||||
"# | echo: false\n",
|
||||
"\n",
|
||||
"from langchain_anthropic import ChatAnthropic\n",
|
||||
"\n",
|
||||
"llm = ChatAnthropic(model=\"claude-3-sonnet-20240229\", temperature=0)"
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install -qU chromadb langchain langchain-community langchain-openai"
|
||||
"%pip install -qU langchain langchain-community langchain-openai langchain-chroma"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -53,8 +53,8 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_chroma import Chroma\n",
|
||||
"from langchain_community.document_loaders import TextLoader\n",
|
||||
"from langchain_community.vectorstores import Chroma\n",
|
||||
"from langchain_openai import OpenAIEmbeddings\n",
|
||||
"from langchain_text_splitters import RecursiveCharacterTextSplitter"
|
||||
]
|
||||
|
||||
@@ -55,6 +55,32 @@ data
|
||||
|
||||
</CodeOutputBlock>
|
||||
|
||||
## Loading HTML with FireCrawlLoader
|
||||
|
||||
[FireCrawl](https://firecrawl.dev/?ref=langchain) crawls and convert any website into markdown. It crawls all accessible subpages and give you clean markdown and metadata for each.
|
||||
|
||||
FireCrawl handles complex tasks such as reverse proxies, caching, rate limits, and content blocked by JavaScript.
|
||||
|
||||
### Prerequisite
|
||||
|
||||
You need to have a FireCrawl API key to use this loader. You can get one by signing up at [FireCrawl](https://firecrawl.dev/?ref=langchainpy).
|
||||
|
||||
```python
|
||||
%pip install --upgrade --quiet langchain langchain-community firecrawl-py
|
||||
|
||||
from langchain_community.document_loaders import FireCrawlLoader
|
||||
|
||||
|
||||
loader = FireCrawlLoader(
|
||||
api_key="YOUR_API_KEY", url="https://firecrawl.dev", mode="crawl"
|
||||
)
|
||||
|
||||
data = loader.load()
|
||||
```
|
||||
|
||||
For more information on how to use FireCrawl, visit [FireCrawl](https://firecrawl.dev/?ref=langchainpy).
|
||||
|
||||
|
||||
## Loading HTML with AzureAIDocumentIntelligenceLoader
|
||||
|
||||
[Azure AI Document Intelligence](https://aka.ms/doc-intelligence) (formerly known as `Azure Form Recognizer`) is machine-learning
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Build a sample vectorDB\n",
|
||||
"from langchain_chroma import Chroma\n",
|
||||
"from langchain_community.document_loaders import WebBaseLoader\n",
|
||||
"from langchain_community.vectorstores import Chroma\n",
|
||||
"from langchain_openai import OpenAIEmbeddings\n",
|
||||
"from langchain_text_splitters import RecursiveCharacterTextSplitter\n",
|
||||
"\n",
|
||||
|
||||
@@ -0,0 +1,309 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "raw",
|
||||
"id": "b5fc1fc7-c4c5-418f-99da-006c604a7ea6",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"---\n",
|
||||
"title: Custom Retriever\n",
|
||||
"---"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "ff6f3c79-0848-4956-9115-54f6b2134587",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Custom Retriever\n",
|
||||
"\n",
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"Many LLM applications involve retrieving information from external data sources using a `Retriever`. \n",
|
||||
"\n",
|
||||
"A retriever is responsible for retrieving a list of relevant `Documents` to a given user `query`.\n",
|
||||
"\n",
|
||||
"The retrieved documents are often formatted into prompts that are fed into an LLM, allowing the LLM to use the information in the to generate an appropriate response (e.g., answering a user question based on a knowledge base).\n",
|
||||
"\n",
|
||||
"## Interface\n",
|
||||
"\n",
|
||||
"To create your own retriever, you need to extend the `BaseRetriever` class and implement the following methods:\n",
|
||||
"\n",
|
||||
"| Method | Description | Required/Optional |\n",
|
||||
"|--------------------------------|--------------------------------------------------|-------------------|\n",
|
||||
"| `_get_relevant_documents` | Get documents relevant to a query. | Required |\n",
|
||||
"| `_aget_relevant_documents` | Implement to provide async native support. | Optional |\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"The logic inside of `_get_relevant_documents` can involve arbitrary calls to a database or to the web using requests.\n",
|
||||
"\n",
|
||||
":::{.callout-tip}\n",
|
||||
"By inherting from `BaseRetriever`, your retriever automatically becomes a LangChain [Runnable](/docs/expression_language/interface) and will gain the standard `Runnable` functionality out of the box!\n",
|
||||
":::\n",
|
||||
"\n",
|
||||
"\n",
|
||||
":::{.callout-info}\n",
|
||||
"You can use a `RunnableLambda` or `RunnableGenerator` to implement a retriever.\n",
|
||||
"\n",
|
||||
"The main benefit of implementing a retriever as a `BaseRetriever` vs. a `RunnableLambda` (a custom [runnable function](/docs/expression_language/primitives/functions)) is that a `BaseRetriever` is a well\n",
|
||||
"known LangChain entity so some tooling for monitoring may implement specialized behavior for retrievers. Another difference\n",
|
||||
"is that a `BaseRetriever` will behave slightly differently from `RunnableLambda` in some APIs; e.g., the `start` event\n",
|
||||
"in `astream_events` API will be `on_retriever_start` instead of `on_chain_start`.\n",
|
||||
":::\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "2be9fe82-0757-41d1-a647-15bed11fd3bf",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Example\n",
|
||||
"\n",
|
||||
"Let's implement a toy retriever that returns all documents whose text contains the text in the user query."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 26,
|
||||
"id": "bdf61902-2984-493b-a002-d4fced6df590",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from typing import List\n",
|
||||
"\n",
|
||||
"from langchain_core.callbacks import CallbackManagerForRetrieverRun\n",
|
||||
"from langchain_core.documents import Document\n",
|
||||
"from langchain_core.retrievers import BaseRetriever\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class ToyRetriever(BaseRetriever):\n",
|
||||
" \"\"\"A toy retriever that contains the top k documents that contain the user query.\n",
|
||||
"\n",
|
||||
" This retriever only implements the sync method _get_relevant_documents.\n",
|
||||
"\n",
|
||||
" If the retriever were to involve file access or network access, it could benefit\n",
|
||||
" from a native async implementation of `_aget_relevant_documents`.\n",
|
||||
"\n",
|
||||
" As usual, with Runnables, there's a default async implementation that's provided\n",
|
||||
" that delegates to the sync implementation running on another thread.\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" documents: List[Document]\n",
|
||||
" \"\"\"List of documents to retrieve from.\"\"\"\n",
|
||||
" k: int\n",
|
||||
" \"\"\"Number of top results to return\"\"\"\n",
|
||||
"\n",
|
||||
" def _get_relevant_documents(\n",
|
||||
" self, query: str, *, run_manager: CallbackManagerForRetrieverRun\n",
|
||||
" ) -> List[Document]:\n",
|
||||
" \"\"\"Sync implementations for retriever.\"\"\"\n",
|
||||
" matching_documents = []\n",
|
||||
" for document in documents:\n",
|
||||
" if len(matching_documents) > self.k:\n",
|
||||
" return matching_documents\n",
|
||||
"\n",
|
||||
" if query.lower() in document.page_content.lower():\n",
|
||||
" matching_documents.append(document)\n",
|
||||
" return matching_documents\n",
|
||||
"\n",
|
||||
" # Optional: Provide a more efficient native implementation by overriding\n",
|
||||
" # _aget_relevant_documents\n",
|
||||
" # async def _aget_relevant_documents(\n",
|
||||
" # self, query: str, *, run_manager: AsyncCallbackManagerForRetrieverRun\n",
|
||||
" # ) -> List[Document]:\n",
|
||||
" # \"\"\"Asynchronously get documents relevant to a query.\n",
|
||||
"\n",
|
||||
" # Args:\n",
|
||||
" # query: String to find relevant documents for\n",
|
||||
" # run_manager: The callbacks handler to use\n",
|
||||
"\n",
|
||||
" # Returns:\n",
|
||||
" # List of relevant documents\n",
|
||||
" # \"\"\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "2eac1f28-29c1-4888-b3aa-b4fa70c73b4c",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Test it 🧪"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 21,
|
||||
"id": "ea868db5-48cc-4ec2-9b0a-1ab94c32b302",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"documents = [\n",
|
||||
" Document(\n",
|
||||
" page_content=\"Dogs are great companions, known for their loyalty and friendliness.\",\n",
|
||||
" metadata={\"type\": \"dog\", \"trait\": \"loyalty\"},\n",
|
||||
" ),\n",
|
||||
" Document(\n",
|
||||
" page_content=\"Cats are independent pets that often enjoy their own space.\",\n",
|
||||
" metadata={\"type\": \"cat\", \"trait\": \"independence\"},\n",
|
||||
" ),\n",
|
||||
" Document(\n",
|
||||
" page_content=\"Goldfish are popular pets for beginners, requiring relatively simple care.\",\n",
|
||||
" metadata={\"type\": \"fish\", \"trait\": \"low maintenance\"},\n",
|
||||
" ),\n",
|
||||
" Document(\n",
|
||||
" page_content=\"Parrots are intelligent birds capable of mimicking human speech.\",\n",
|
||||
" metadata={\"type\": \"bird\", \"trait\": \"intelligence\"},\n",
|
||||
" ),\n",
|
||||
" Document(\n",
|
||||
" page_content=\"Rabbits are social animals that need plenty of space to hop around.\",\n",
|
||||
" metadata={\"type\": \"rabbit\", \"trait\": \"social\"},\n",
|
||||
" ),\n",
|
||||
"]\n",
|
||||
"retriever = ToyRetriever(documents=documents, k=3)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 22,
|
||||
"id": "18be85e9-6ef0-4ee0-ae5d-a0810c38b254",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[Document(page_content='Cats are independent pets that often enjoy their own space.', metadata={'type': 'cat', 'trait': 'independence'}),\n",
|
||||
" Document(page_content='Rabbits are social animals that need plenty of space to hop around.', metadata={'type': 'rabbit', 'trait': 'social'})]"
|
||||
]
|
||||
},
|
||||
"execution_count": 22,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"retriever.invoke(\"that\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "13f76f6e-cf2b-4f67-859b-0ef8be98abbe",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"It's a **runnable** so it'll benefit from the standard Runnable Interface! 🤩"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 23,
|
||||
"id": "3672e9fe-4365-4628-9d25-31924cfaf784",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[Document(page_content='Cats are independent pets that often enjoy their own space.', metadata={'type': 'cat', 'trait': 'independence'}),\n",
|
||||
" Document(page_content='Rabbits are social animals that need plenty of space to hop around.', metadata={'type': 'rabbit', 'trait': 'social'})]"
|
||||
]
|
||||
},
|
||||
"execution_count": 23,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"await retriever.ainvoke(\"that\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 24,
|
||||
"id": "e2c96eed-6813-421c-acf2-6554839840ee",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[[Document(page_content='Dogs are great companions, known for their loyalty and friendliness.', metadata={'type': 'dog', 'trait': 'loyalty'})],\n",
|
||||
" [Document(page_content='Cats are independent pets that often enjoy their own space.', metadata={'type': 'cat', 'trait': 'independence'})]]"
|
||||
]
|
||||
},
|
||||
"execution_count": 24,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"retriever.batch([\"dog\", \"cat\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 25,
|
||||
"id": "978b6636-bf36-42c2-969c-207718f084cf",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"{'event': 'on_retriever_start', 'run_id': 'f96f268d-8383-4921-b175-ca583924d9ff', 'name': 'ToyRetriever', 'tags': [], 'metadata': {}, 'data': {'input': 'bar'}}\n",
|
||||
"{'event': 'on_retriever_stream', 'run_id': 'f96f268d-8383-4921-b175-ca583924d9ff', 'tags': [], 'metadata': {}, 'name': 'ToyRetriever', 'data': {'chunk': []}}\n",
|
||||
"{'event': 'on_retriever_end', 'name': 'ToyRetriever', 'run_id': 'f96f268d-8383-4921-b175-ca583924d9ff', 'tags': [], 'metadata': {}, 'data': {'output': []}}\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"async for event in retriever.astream_events(\"bar\", version=\"v1\"):\n",
|
||||
" print(event)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "7b45c404-37bf-4370-bb7c-26556777ff46",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Contributing\n",
|
||||
"\n",
|
||||
"We appreciate contributions of interesting retrievers!\n",
|
||||
"\n",
|
||||
"Here's a checklist to help make sure your contribution gets added to LangChain:\n",
|
||||
"\n",
|
||||
"Documentation:\n",
|
||||
"\n",
|
||||
"* The retriever contains doc-strings for all initialization arguments, as these will be surfaced in the [API Reference](https://api.python.langchain.com/en/stable/langchain_api_reference.html).\n",
|
||||
"* The class doc-string for the model contains a link to any relevant APIs used for the retriever (e.g., if the retriever is retrieving from wikipedia, it'll be good to link to the wikipedia API!)\n",
|
||||
"\n",
|
||||
"Tests:\n",
|
||||
"\n",
|
||||
"* [ ] Add unit or integration tests to verify that `invoke` and `ainvoke` work.\n",
|
||||
"\n",
|
||||
"Optimizations:\n",
|
||||
"\n",
|
||||
"If the retriever is connecting to external data sources (e.g., an API or a file), it'll almost certainly benefit from an async native optimization!\n",
|
||||
" \n",
|
||||
"* [ ] Provide a native async implementation of `_aget_relevant_documents` (used by `ainvoke`)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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.11.4"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -80,23 +80,4 @@ chain.invoke("What did the president say about technology?")
|
||||
|
||||
## Custom Retriever
|
||||
|
||||
Since the retriever interface is so simple, it's pretty easy to write a custom one.
|
||||
|
||||
```python
|
||||
from langchain_core.retrievers import BaseRetriever
|
||||
from langchain_core.callbacks import CallbackManagerForRetrieverRun
|
||||
from langchain_core.documents import Document
|
||||
from typing import List
|
||||
|
||||
|
||||
class CustomRetriever(BaseRetriever):
|
||||
|
||||
def _get_relevant_documents(
|
||||
self, query: str, *, run_manager: CallbackManagerForRetrieverRun
|
||||
) -> List[Document]:
|
||||
return [Document(page_content=query)]
|
||||
|
||||
retriever = CustomRetriever()
|
||||
|
||||
retriever.get_relevant_documents("bar")
|
||||
```
|
||||
See the [documentation here](/docs/modules/data_connection/retrievers/custom_retriever) to implement a custom retriever.
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install --upgrade --quiet sentence-transformers > /dev/null"
|
||||
"%pip install --upgrade --quiet sentence-transformers langchain-chroma langchain langchain-openai > /dev/null"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -53,11 +53,11 @@
|
||||
"source": [
|
||||
"from langchain.chains import LLMChain, StuffDocumentsChain\n",
|
||||
"from langchain.prompts import PromptTemplate\n",
|
||||
"from langchain_chroma import Chroma\n",
|
||||
"from langchain_community.document_transformers import (\n",
|
||||
" LongContextReorder,\n",
|
||||
")\n",
|
||||
"from langchain_community.embeddings import HuggingFaceEmbeddings\n",
|
||||
"from langchain_community.vectorstores import Chroma\n",
|
||||
"from langchain_openai import OpenAI\n",
|
||||
"\n",
|
||||
"# Get embeddings.\n",
|
||||
|
||||
@@ -37,8 +37,8 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.storage import InMemoryByteStore\n",
|
||||
"from langchain_chroma import Chroma\n",
|
||||
"from langchain_community.document_loaders import TextLoader\n",
|
||||
"from langchain_community.vectorstores import Chroma\n",
|
||||
"from langchain_openai import OpenAIEmbeddings\n",
|
||||
"from langchain_text_splitters import RecursiveCharacterTextSplitter"
|
||||
]
|
||||
|
||||
@@ -43,8 +43,8 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.storage import InMemoryStore\n",
|
||||
"from langchain_chroma import Chroma\n",
|
||||
"from langchain_community.document_loaders import TextLoader\n",
|
||||
"from langchain_community.vectorstores import Chroma\n",
|
||||
"from langchain_openai import OpenAIEmbeddings\n",
|
||||
"from langchain_text_splitters import RecursiveCharacterTextSplitter"
|
||||
]
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install --upgrade --quiet lark chromadb"
|
||||
"%pip install --upgrade --quiet lark langchain-chroma"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -40,7 +40,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_community.vectorstores import Chroma\n",
|
||||
"from langchain_chroma import Chroma\n",
|
||||
"from langchain_core.documents import Document\n",
|
||||
"from langchain_openai import OpenAIEmbeddings\n",
|
||||
"\n",
|
||||
|
||||
@@ -30,7 +30,7 @@ There are many great vector store options, here are a few that are free, open-so
|
||||
This walkthrough uses the `chroma` vector database, which runs on your local machine as a library.
|
||||
|
||||
```bash
|
||||
pip install chromadb
|
||||
pip install langchain-chroma
|
||||
```
|
||||
|
||||
We want to use OpenAIEmbeddings so we have to get the OpenAI API Key.
|
||||
@@ -47,7 +47,7 @@ os.environ['OPENAI_API_KEY'] = getpass.getpass('OpenAI API Key:')
|
||||
from langchain_community.document_loaders import TextLoader
|
||||
from langchain_openai import OpenAIEmbeddings
|
||||
from langchain_text_splitters import CharacterTextSplitter
|
||||
from langchain_community.vectorstores import Chroma
|
||||
from langchain_chroma import Chroma
|
||||
|
||||
# Load the document, split it into chunks, embed each chunk and load it into the vector store.
|
||||
raw_documents = TextLoader('../../../state_of_the_union.txt').load()
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_community.vectorstores import Chroma\n",
|
||||
"from langchain_chroma import Chroma\n",
|
||||
"from langchain_openai import OpenAIEmbeddings\n",
|
||||
"from langchain_text_splitters import CharacterTextSplitter"
|
||||
]
|
||||
|
||||
707
docs/docs/modules/model_io/chat/function_calling.ipynb
Normal file
707
docs/docs/modules/model_io/chat/function_calling.ipynb
Normal file
@@ -0,0 +1,707 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "raw",
|
||||
"id": "a413ade7-48f0-4d43-a1f3-d87f550a8018",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"---\n",
|
||||
"sidebar_position: 2\n",
|
||||
"title: Tool/function calling\n",
|
||||
"---"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "50d59b14-c434-4359-be8e-4a21378e762f",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Tool calling\n",
|
||||
"\n",
|
||||
"```{=mdx}\n",
|
||||
":::info\n",
|
||||
"We use the term tool calling interchangeably with function calling. Although\n",
|
||||
"function calling is sometimes meant to refer to invocations of a single function,\n",
|
||||
"we treat all models as though they can return multiple tool or function calls in \n",
|
||||
"each message.\n",
|
||||
":::\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"Tool calling allows a model to respond to a given prompt by generating output that \n",
|
||||
"matches a user-defined schema. While the name implies that the model is performing \n",
|
||||
"some action, this is actually not the case! The model is coming up with the \n",
|
||||
"arguments to a tool, and actually running the tool (or not) is up to the user - \n",
|
||||
"for example, if you want to [extract output matching some schema](/docs/use_cases/extraction/) \n",
|
||||
"from unstructured text, you could give the model an \"extraction\" tool that takes \n",
|
||||
"parameters matching the desired schema, then treat the generated output as your final \n",
|
||||
"result.\n",
|
||||
"\n",
|
||||
"A tool call includes a name, arguments dict, and an optional identifier. The \n",
|
||||
"arguments dict is structured `{argument_name: argument_value}`.\n",
|
||||
"\n",
|
||||
"Many LLM providers, including [Anthropic](https://www.anthropic.com/), \n",
|
||||
"[Cohere](https://cohere.com/), [Google](https://cloud.google.com/vertex-ai), \n",
|
||||
"[Mistral](https://mistral.ai/), [OpenAI](https://openai.com/), and others, \n",
|
||||
"support variants of a tool calling feature. These features typically allow requests \n",
|
||||
"to the LLM to include available tools and their schemas, and for responses to include \n",
|
||||
"calls to these tools. For instance, given a search engine tool, an LLM might handle a \n",
|
||||
"query by first issuing a call to the search engine. The system calling the LLM can \n",
|
||||
"receive the tool call, execute it, and return the output to the LLM to inform its \n",
|
||||
"response. LangChain includes a suite of [built-in tools](/docs/integrations/tools/) \n",
|
||||
"and supports several methods for defining your own [custom tools](/docs/modules/tools/custom_tools). \n",
|
||||
"Tool-calling is extremely useful for building [tool-using chains and agents](/docs/use_cases/tool_use), \n",
|
||||
"and for getting structured outputs from models more generally.\n",
|
||||
"\n",
|
||||
"Providers adopt different conventions for formatting tool schemas and tool calls. \n",
|
||||
"For instance, Anthropic returns tool calls as parsed structures within a larger content block:\n",
|
||||
"```python\n",
|
||||
"[\n",
|
||||
" {\n",
|
||||
" \"text\": \"<thinking>\\nI should use a tool.\\n</thinking>\",\n",
|
||||
" \"type\": \"text\"\n",
|
||||
" },\n",
|
||||
" {\n",
|
||||
" \"id\": \"id_value\",\n",
|
||||
" \"input\": {\"arg_name\": \"arg_value\"},\n",
|
||||
" \"name\": \"tool_name\",\n",
|
||||
" \"type\": \"tool_use\"\n",
|
||||
" }\n",
|
||||
"]\n",
|
||||
"```\n",
|
||||
"whereas OpenAI separates tool calls into a distinct parameter, with arguments as JSON strings:\n",
|
||||
"```python\n",
|
||||
"{\n",
|
||||
" \"tool_calls\": [\n",
|
||||
" {\n",
|
||||
" \"id\": \"id_value\",\n",
|
||||
" \"function\": {\n",
|
||||
" \"arguments\": '{\"arg_name\": \"arg_value\"}',\n",
|
||||
" \"name\": \"tool_name\"\n",
|
||||
" },\n",
|
||||
" \"type\": \"function\"\n",
|
||||
" }\n",
|
||||
" ]\n",
|
||||
"}\n",
|
||||
"```\n",
|
||||
"LangChain implements standard interfaces for defining tools, passing them to LLMs, \n",
|
||||
"and representing tool calls.\n",
|
||||
"\n",
|
||||
"## Passing tools to LLMs\n",
|
||||
"\n",
|
||||
"Chat models supporting tool calling features implement a `.bind_tools` method, which \n",
|
||||
"receives a list of LangChain [tool objects](https://api.python.langchain.com/en/latest/tools/langchain_core.tools.BaseTool.html#langchain_core.tools.BaseTool) \n",
|
||||
"and binds them to the chat model in its expected format. Subsequent invocations of the \n",
|
||||
"chat model will include tool schemas in its calls to the LLM.\n",
|
||||
"\n",
|
||||
"For example, we can define the schema for custom tools using the `@tool` decorator \n",
|
||||
"on Python functions:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 22,
|
||||
"id": "841dca72-1b57-4a42-8e22-da4835c4cfe0",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_core.tools import tool\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"@tool\n",
|
||||
"def add(a: int, b: int) -> int:\n",
|
||||
" \"\"\"Adds a and b.\"\"\"\n",
|
||||
" return a + b\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"@tool\n",
|
||||
"def multiply(a: int, b: int) -> int:\n",
|
||||
" \"\"\"Multiplies a and b.\"\"\"\n",
|
||||
" return a * b\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"tools = [add, multiply]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "48058b7d-048d-48e6-a272-3931ad7ad146",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Or below, we define the schema using Pydantic:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 23,
|
||||
"id": "fca56328-85e4-4839-97b7-b5dc55920602",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_core.pydantic_v1 import BaseModel, Field\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Note that the docstrings here are crucial, as they will be passed along\n",
|
||||
"# to the model along with the class name.\n",
|
||||
"class Add(BaseModel):\n",
|
||||
" \"\"\"Add two integers together.\"\"\"\n",
|
||||
"\n",
|
||||
" a: int = Field(..., description=\"First integer\")\n",
|
||||
" b: int = Field(..., description=\"Second integer\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class Multiply(BaseModel):\n",
|
||||
" \"\"\"Multiply two integers together.\"\"\"\n",
|
||||
"\n",
|
||||
" a: int = Field(..., description=\"First integer\")\n",
|
||||
" b: int = Field(..., description=\"Second integer\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"tools = [Add, Multiply]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "ead9068d-11f6-42f3-a508-3c1830189947",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"We can bind them to chat models as follows:\n",
|
||||
"\n",
|
||||
"```{=mdx}\n",
|
||||
"import ChatModelTabs from \"@theme/ChatModelTabs\";\n",
|
||||
"\n",
|
||||
"<ChatModelTabs\n",
|
||||
" customVarName=\"llm\"\n",
|
||||
" fireworksParams={`model=\"accounts/fireworks/models/firefunction-v1\", temperature=0`}\n",
|
||||
"/>\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"We can use the `bind_tools()` method to handle converting\n",
|
||||
"`Multiply` to a \"tool\" and binding it to the model (i.e.,\n",
|
||||
"passing it in each time the model is invoked)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 67,
|
||||
"id": "44eb8327-a03d-4c7c-945e-30f13f455346",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# | echo: false\n",
|
||||
"# | output: false\n",
|
||||
"\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"\n",
|
||||
"llm = ChatOpenAI(model=\"gpt-3.5-turbo-0125\", temperature=0)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 68,
|
||||
"id": "af2a83ac-e43f-43ce-b107-9ed8376bfb75",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"llm_with_tools = llm.bind_tools(tools)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "16208230-f64f-4935-9aa1-280a91f34ba3",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Tool calls\n",
|
||||
"\n",
|
||||
"If tool calls are included in a LLM response, they are attached to the corresponding \n",
|
||||
"[message](https://api.python.langchain.com/en/latest/messages/langchain_core.messages.ai.AIMessage.html#langchain_core.messages.ai.AIMessage) \n",
|
||||
"or [message chunk](https://api.python.langchain.com/en/latest/messages/langchain_core.messages.ai.AIMessageChunk.html#langchain_core.messages.ai.AIMessageChunk) \n",
|
||||
"as a list of [tool call](https://api.python.langchain.com/en/latest/messages/langchain_core.messages.tool.ToolCall.html#langchain_core.messages.tool.ToolCall) \n",
|
||||
"objects in the `.tool_calls` attribute. A `ToolCall` is a typed dict that includes a \n",
|
||||
"tool name, dict of argument values, and (optionally) an identifier. Messages with no \n",
|
||||
"tool calls default to an empty list for this attribute.\n",
|
||||
"\n",
|
||||
"Example:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 15,
|
||||
"id": "1640a4b4-c201-4b23-b257-738d854fb9fd",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[{'name': 'Multiply',\n",
|
||||
" 'args': {'a': 3, 'b': 12},\n",
|
||||
" 'id': 'call_1Tdp5wUXbYQzpkBoagGXqUTo'},\n",
|
||||
" {'name': 'Add',\n",
|
||||
" 'args': {'a': 11, 'b': 49},\n",
|
||||
" 'id': 'call_k9v09vYioS3X0Qg35zESuUKI'}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 15,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"query = \"What is 3 * 12? Also, what is 11 + 49?\"\n",
|
||||
"\n",
|
||||
"llm_with_tools.invoke(query).tool_calls"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "ac3ff0fe-5119-46b8-a578-530245bff23f",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"The `.tool_calls` attribute should contain valid tool calls. Note that on occasion, \n",
|
||||
"model providers may output malformed tool calls (e.g., arguments that are not \n",
|
||||
"valid JSON). When parsing fails in these cases, instances \n",
|
||||
"of [InvalidToolCall](https://api.python.langchain.com/en/latest/messages/langchain_core.messages.tool.InvalidToolCall.html#langchain_core.messages.tool.InvalidToolCall) \n",
|
||||
"are populated in the `.invalid_tool_calls` attribute. An `InvalidToolCall` can have \n",
|
||||
"a name, string arguments, identifier, and error message.\n",
|
||||
"\n",
|
||||
"If desired, [output parsers](/docs/modules/model_io/output_parsers) can further \n",
|
||||
"process the output. For example, we can convert back to the original Pydantic class:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 16,
|
||||
"id": "ca15fcad-74fe-4109-a1b1-346c3eefe238",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[Multiply(a=3, b=12), Add(a=11, b=49)]"
|
||||
]
|
||||
},
|
||||
"execution_count": 16,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from langchain_core.output_parsers.openai_tools import PydanticToolsParser\n",
|
||||
"\n",
|
||||
"chain = llm_with_tools | PydanticToolsParser(tools=[Multiply, Add])\n",
|
||||
"chain.invoke(query)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "0ba3505d-f405-43ba-93c4-7fbd84f6464b",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Streaming\n",
|
||||
"\n",
|
||||
"When tools are called in a streaming context, \n",
|
||||
"[message chunks](https://api.python.langchain.com/en/latest/messages/langchain_core.messages.ai.AIMessageChunk.html#langchain_core.messages.ai.AIMessageChunk) \n",
|
||||
"will be populated with [tool call chunk](https://api.python.langchain.com/en/latest/messages/langchain_core.messages.tool.ToolCallChunk.html#langchain_core.messages.tool.ToolCallChunk) \n",
|
||||
"objects in a list via the `.tool_call_chunks` attribute. A `ToolCallChunk` includes \n",
|
||||
"optional string fields for the tool `name`, `args`, and `id`, and includes an optional \n",
|
||||
"integer field `index` that can be used to join chunks together. Fields are optional \n",
|
||||
"because portions of a tool call may be streamed across different chunks (e.g., a chunk \n",
|
||||
"that includes a substring of the arguments may have null values for the tool name and id).\n",
|
||||
"\n",
|
||||
"Because message chunks inherit from their parent message class, an \n",
|
||||
"[AIMessageChunk](https://api.python.langchain.com/en/latest/messages/langchain_core.messages.ai.AIMessageChunk.html#langchain_core.messages.ai.AIMessageChunk) \n",
|
||||
"with tool call chunks will also include `.tool_calls` and `.invalid_tool_calls` fields. \n",
|
||||
"These fields are parsed best-effort from the message's tool call chunks.\n",
|
||||
"\n",
|
||||
"Note that not all providers currently support streaming for tool calls.\n",
|
||||
"\n",
|
||||
"Example:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 17,
|
||||
"id": "4f54a0de-74c7-4f2d-86c5-660aed23840d",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"[]\n",
|
||||
"[{'name': 'Multiply', 'args': '', 'id': 'call_d39MsxKM5cmeGJOoYKdGBgzc', 'index': 0}]\n",
|
||||
"[{'name': None, 'args': '{\"a\"', 'id': None, 'index': 0}]\n",
|
||||
"[{'name': None, 'args': ': 3, ', 'id': None, 'index': 0}]\n",
|
||||
"[{'name': None, 'args': '\"b\": 1', 'id': None, 'index': 0}]\n",
|
||||
"[{'name': None, 'args': '2}', 'id': None, 'index': 0}]\n",
|
||||
"[{'name': 'Add', 'args': '', 'id': 'call_QJpdxD9AehKbdXzMHxgDMMhs', 'index': 1}]\n",
|
||||
"[{'name': None, 'args': '{\"a\"', 'id': None, 'index': 1}]\n",
|
||||
"[{'name': None, 'args': ': 11,', 'id': None, 'index': 1}]\n",
|
||||
"[{'name': None, 'args': ' \"b\": ', 'id': None, 'index': 1}]\n",
|
||||
"[{'name': None, 'args': '49}', 'id': None, 'index': 1}]\n",
|
||||
"[]\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"async for chunk in llm_with_tools.astream(query):\n",
|
||||
" print(chunk.tool_call_chunks)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "55046320-3466-4ec1-a1f8-336234ba9019",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Note that adding message chunks will merge their corresponding tool call chunks. This is the principle by which LangChain's various [tool output parsers](/docs/modules/model_io/output_parsers/types/openai_tools/) support streaming.\n",
|
||||
"\n",
|
||||
"For example, below we accumulate tool call chunks:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 18,
|
||||
"id": "0a944af0-eedd-43c8-8ff3-f4301f129d9b",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"[]\n",
|
||||
"[{'name': 'Multiply', 'args': '', 'id': 'call_erKtz8z3e681cmxYKbRof0NS', 'index': 0}]\n",
|
||||
"[{'name': 'Multiply', 'args': '{\"a\"', 'id': 'call_erKtz8z3e681cmxYKbRof0NS', 'index': 0}]\n",
|
||||
"[{'name': 'Multiply', 'args': '{\"a\": 3, ', 'id': 'call_erKtz8z3e681cmxYKbRof0NS', 'index': 0}]\n",
|
||||
"[{'name': 'Multiply', 'args': '{\"a\": 3, \"b\": 1', 'id': 'call_erKtz8z3e681cmxYKbRof0NS', 'index': 0}]\n",
|
||||
"[{'name': 'Multiply', 'args': '{\"a\": 3, \"b\": 12}', 'id': 'call_erKtz8z3e681cmxYKbRof0NS', 'index': 0}]\n",
|
||||
"[{'name': 'Multiply', 'args': '{\"a\": 3, \"b\": 12}', 'id': 'call_erKtz8z3e681cmxYKbRof0NS', 'index': 0}, {'name': 'Add', 'args': '', 'id': 'call_tYHYdEV2YBvzDcSCiFCExNvw', 'index': 1}]\n",
|
||||
"[{'name': 'Multiply', 'args': '{\"a\": 3, \"b\": 12}', 'id': 'call_erKtz8z3e681cmxYKbRof0NS', 'index': 0}, {'name': 'Add', 'args': '{\"a\"', 'id': 'call_tYHYdEV2YBvzDcSCiFCExNvw', 'index': 1}]\n",
|
||||
"[{'name': 'Multiply', 'args': '{\"a\": 3, \"b\": 12}', 'id': 'call_erKtz8z3e681cmxYKbRof0NS', 'index': 0}, {'name': 'Add', 'args': '{\"a\": 11,', 'id': 'call_tYHYdEV2YBvzDcSCiFCExNvw', 'index': 1}]\n",
|
||||
"[{'name': 'Multiply', 'args': '{\"a\": 3, \"b\": 12}', 'id': 'call_erKtz8z3e681cmxYKbRof0NS', 'index': 0}, {'name': 'Add', 'args': '{\"a\": 11, \"b\": ', 'id': 'call_tYHYdEV2YBvzDcSCiFCExNvw', 'index': 1}]\n",
|
||||
"[{'name': 'Multiply', 'args': '{\"a\": 3, \"b\": 12}', 'id': 'call_erKtz8z3e681cmxYKbRof0NS', 'index': 0}, {'name': 'Add', 'args': '{\"a\": 11, \"b\": 49}', 'id': 'call_tYHYdEV2YBvzDcSCiFCExNvw', 'index': 1}]\n",
|
||||
"[{'name': 'Multiply', 'args': '{\"a\": 3, \"b\": 12}', 'id': 'call_erKtz8z3e681cmxYKbRof0NS', 'index': 0}, {'name': 'Add', 'args': '{\"a\": 11, \"b\": 49}', 'id': 'call_tYHYdEV2YBvzDcSCiFCExNvw', 'index': 1}]\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"first = True\n",
|
||||
"async for chunk in llm_with_tools.astream(query):\n",
|
||||
" if first:\n",
|
||||
" gathered = chunk\n",
|
||||
" first = False\n",
|
||||
" else:\n",
|
||||
" gathered = gathered + chunk\n",
|
||||
"\n",
|
||||
" print(gathered.tool_call_chunks)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 19,
|
||||
"id": "db4e3e3a-3553-44dc-bd31-149c0981a06a",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"<class 'str'>\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(type(gathered.tool_call_chunks[0][\"args\"]))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "95e92826-6e55-4684-9498-556f357f73ac",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"And below we accumulate tool calls to demonstrate partial parsing:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 20,
|
||||
"id": "e9402bde-d4b5-4564-a99e-f88c9b46b28a",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"[]\n",
|
||||
"[]\n",
|
||||
"[{'name': 'Multiply', 'args': {}, 'id': 'call_BXqUtt6jYCwR1DguqpS2ehP0'}]\n",
|
||||
"[{'name': 'Multiply', 'args': {'a': 3}, 'id': 'call_BXqUtt6jYCwR1DguqpS2ehP0'}]\n",
|
||||
"[{'name': 'Multiply', 'args': {'a': 3, 'b': 1}, 'id': 'call_BXqUtt6jYCwR1DguqpS2ehP0'}]\n",
|
||||
"[{'name': 'Multiply', 'args': {'a': 3, 'b': 12}, 'id': 'call_BXqUtt6jYCwR1DguqpS2ehP0'}]\n",
|
||||
"[{'name': 'Multiply', 'args': {'a': 3, 'b': 12}, 'id': 'call_BXqUtt6jYCwR1DguqpS2ehP0'}]\n",
|
||||
"[{'name': 'Multiply', 'args': {'a': 3, 'b': 12}, 'id': 'call_BXqUtt6jYCwR1DguqpS2ehP0'}, {'name': 'Add', 'args': {}, 'id': 'call_UjSHJKROSAw2BDc8cp9cSv4i'}]\n",
|
||||
"[{'name': 'Multiply', 'args': {'a': 3, 'b': 12}, 'id': 'call_BXqUtt6jYCwR1DguqpS2ehP0'}, {'name': 'Add', 'args': {'a': 11}, 'id': 'call_UjSHJKROSAw2BDc8cp9cSv4i'}]\n",
|
||||
"[{'name': 'Multiply', 'args': {'a': 3, 'b': 12}, 'id': 'call_BXqUtt6jYCwR1DguqpS2ehP0'}, {'name': 'Add', 'args': {'a': 11}, 'id': 'call_UjSHJKROSAw2BDc8cp9cSv4i'}]\n",
|
||||
"[{'name': 'Multiply', 'args': {'a': 3, 'b': 12}, 'id': 'call_BXqUtt6jYCwR1DguqpS2ehP0'}, {'name': 'Add', 'args': {'a': 11, 'b': 49}, 'id': 'call_UjSHJKROSAw2BDc8cp9cSv4i'}]\n",
|
||||
"[{'name': 'Multiply', 'args': {'a': 3, 'b': 12}, 'id': 'call_BXqUtt6jYCwR1DguqpS2ehP0'}, {'name': 'Add', 'args': {'a': 11, 'b': 49}, 'id': 'call_UjSHJKROSAw2BDc8cp9cSv4i'}]\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"first = True\n",
|
||||
"async for chunk in llm_with_tools.astream(query):\n",
|
||||
" if first:\n",
|
||||
" gathered = chunk\n",
|
||||
" first = False\n",
|
||||
" else:\n",
|
||||
" gathered = gathered + chunk\n",
|
||||
"\n",
|
||||
" print(gathered.tool_calls)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 21,
|
||||
"id": "8c2f21cc-0c6d-416a-871f-e854621c96e2",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"<class 'dict'>\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(type(gathered.tool_calls[0][\"args\"]))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "97a0c977-0c3c-4011-b49b-db98c609d0ce",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Passing tool outputs to model\n",
|
||||
"\n",
|
||||
"If we're using the model-generated tool invocations to actually call tools and want to pass the tool results back to the model, we can do so using `ToolMessage`s."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 117,
|
||||
"id": "48049192-be28-42ab-9a44-d897924e67cd",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[HumanMessage(content='What is 3 * 12? Also, what is 11 + 49?'),\n",
|
||||
" AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_K5DsWEmgt6D08EI9AFu9NaL1', 'function': {'arguments': '{\"a\": 3, \"b\": 12}', 'name': 'Multiply'}, 'type': 'function'}, {'id': 'call_qywVrsplg0ZMv7LHYYMjyG81', 'function': {'arguments': '{\"a\": 11, \"b\": 49}', 'name': 'Add'}, 'type': 'function'}]}, response_metadata={'token_usage': {'completion_tokens': 50, 'prompt_tokens': 105, 'total_tokens': 155}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_b28b39ffa8', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-1a0b8cdd-9221-4d94-b2ed-5701f67ce9fe-0', tool_calls=[{'name': 'Multiply', 'args': {'a': 3, 'b': 12}, 'id': 'call_K5DsWEmgt6D08EI9AFu9NaL1'}, {'name': 'Add', 'args': {'a': 11, 'b': 49}, 'id': 'call_qywVrsplg0ZMv7LHYYMjyG81'}]),\n",
|
||||
" ToolMessage(content='36', tool_call_id='call_K5DsWEmgt6D08EI9AFu9NaL1'),\n",
|
||||
" ToolMessage(content='60', tool_call_id='call_qywVrsplg0ZMv7LHYYMjyG81')]"
|
||||
]
|
||||
},
|
||||
"execution_count": 117,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from langchain_core.messages import HumanMessage, ToolMessage\n",
|
||||
"\n",
|
||||
"messages = [HumanMessage(query)]\n",
|
||||
"ai_msg = llm_with_tools.invoke(messages)\n",
|
||||
"messages.append(ai_msg)\n",
|
||||
"for tool_call in ai_msg.tool_calls:\n",
|
||||
" selected_tool = {\"add\": add, \"multiply\": multiply}[tool_call[\"name\"].lower()]\n",
|
||||
" tool_output = selected_tool.invoke(tool_call[\"args\"])\n",
|
||||
" messages.append(ToolMessage(tool_output, tool_call_id=tool_call[\"id\"]))\n",
|
||||
"messages"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 118,
|
||||
"id": "611e0f36-d736-48d1-bca1-1cec51d223f3",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"AIMessage(content='3 * 12 is 36 and 11 + 49 is 60.', response_metadata={'token_usage': {'completion_tokens': 18, 'prompt_tokens': 171, 'total_tokens': 189}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_b28b39ffa8', 'finish_reason': 'stop', 'logprobs': None}, id='run-a6c8093c-b16a-4c92-8308-7c9ac998118c-0')"
|
||||
]
|
||||
},
|
||||
"execution_count": 118,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"llm_with_tools.invoke(messages)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "a5937498-d6fe-400a-b192-ef35c314168e",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Few-shot prompting\n",
|
||||
"\n",
|
||||
"For more complex tool use it's very useful to add few-shot examples to the prompt. We can do this by adding `AIMessage`s with `ToolCall`s and corresponding `ToolMessage`s to our prompt.\n",
|
||||
"\n",
|
||||
"For example, even with some special instructions our model can get tripped up by order of operations:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 112,
|
||||
"id": "5ef2e7c3-0925-49da-ab8f-e42c4fa40f29",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[{'name': 'Multiply',\n",
|
||||
" 'args': {'a': 119, 'b': 8},\n",
|
||||
" 'id': 'call_Dl3FXRVkQCFW4sUNYOe4rFr7'},\n",
|
||||
" {'name': 'Add',\n",
|
||||
" 'args': {'a': 952, 'b': -20},\n",
|
||||
" 'id': 'call_n03l4hmka7VZTCiP387Wud2C'}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 112,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"llm_with_tools.invoke(\n",
|
||||
" \"Whats 119 times 8 minus 20. Don't do any math yourself, only use tools for math. Respect order of operations\"\n",
|
||||
").tool_calls"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "a5249069-b5f8-40ac-ae74-30d67c4e9168",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"The model shouldn't be trying to add anything yet, since it technically can't know the results of 119 * 8 yet.\n",
|
||||
"\n",
|
||||
"By adding a prompt with some examples we can correct this behavior:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 107,
|
||||
"id": "7b2e8b19-270f-4e1a-8be7-7aad704c1cf4",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[{'name': 'Multiply',\n",
|
||||
" 'args': {'a': 119, 'b': 8},\n",
|
||||
" 'id': 'call_MoSgwzIhPxhclfygkYaKIsGZ'}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 107,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from langchain_core.messages import AIMessage\n",
|
||||
"from langchain_core.prompts import ChatPromptTemplate\n",
|
||||
"from langchain_core.runnables import RunnablePassthrough\n",
|
||||
"\n",
|
||||
"examples = [\n",
|
||||
" HumanMessage(\n",
|
||||
" \"What's the product of 317253 and 128472 plus four\", name=\"example_user\"\n",
|
||||
" ),\n",
|
||||
" AIMessage(\n",
|
||||
" \"\",\n",
|
||||
" name=\"example_assistant\",\n",
|
||||
" tool_calls=[\n",
|
||||
" {\"name\": \"Multiply\", \"args\": {\"x\": 317253, \"y\": 128472}, \"id\": \"1\"}\n",
|
||||
" ],\n",
|
||||
" ),\n",
|
||||
" ToolMessage(\"16505054784\", tool_call_id=\"1\"),\n",
|
||||
" AIMessage(\n",
|
||||
" \"\",\n",
|
||||
" name=\"example_assistant\",\n",
|
||||
" tool_calls=[{\"name\": \"Add\", \"args\": {\"x\": 16505054784, \"y\": 4}, \"id\": \"2\"}],\n",
|
||||
" ),\n",
|
||||
" ToolMessage(\"16505054788\", tool_call_id=\"2\"),\n",
|
||||
" AIMessage(\n",
|
||||
" \"The product of 317253 and 128472 plus four is 16505054788\",\n",
|
||||
" name=\"example_assistant\",\n",
|
||||
" ),\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"system = \"\"\"You are bad at math but are an expert at using a calculator. \n",
|
||||
"\n",
|
||||
"Use past tool usage as an example of how to correctly use the tools.\"\"\"\n",
|
||||
"few_shot_prompt = ChatPromptTemplate.from_messages(\n",
|
||||
" [\n",
|
||||
" (\"system\", system),\n",
|
||||
" *examples,\n",
|
||||
" (\"human\", \"{query}\"),\n",
|
||||
" ]\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"chain = {\"query\": RunnablePassthrough()} | few_shot_prompt | llm_with_tools\n",
|
||||
"chain.invoke(\"Whats 119 times 8 minus 20\").tool_calls"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "19160e3e-3eb5-4e9a-ae56-74a2dce0af32",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Seems like we get the correct output this time.\n",
|
||||
"\n",
|
||||
"Here's what the [LangSmith trace](https://smith.langchain.com/public/f70550a1-585f-4c9d-a643-13148ab1616f/r) looks like."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "020cfd3b-0838-49d0-96bb-7cd919921833",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Next steps\n",
|
||||
"\n",
|
||||
"- **Output parsing**: See [OpenAI Tools output\n",
|
||||
" parsers](/docs/modules/model_io/output_parsers/types/openai_tools/)\n",
|
||||
" and [OpenAI Functions output\n",
|
||||
" parsers](/docs/modules/model_io/output_parsers/types/openai_functions/)\n",
|
||||
" to learn about extracting the function calling API responses into\n",
|
||||
" various formats.\n",
|
||||
"- **Structured output chains**: [Some models have constructors](/docs/modules/model_io/chat/structured_output/) that\n",
|
||||
" handle creating a structured output chain for you.\n",
|
||||
"- **Tool use**: See how to construct chains and agents that\n",
|
||||
" call the invoked tools in [these\n",
|
||||
" guides](/docs/use_cases/tool_use/)."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "poetry-venv-2",
|
||||
"language": "python",
|
||||
"name": "poetry-venv-2"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.9.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -1,326 +0,0 @@
|
||||
---
|
||||
sidebar_position: 2
|
||||
title: Tool/function calling
|
||||
---
|
||||
|
||||
# Tool calling
|
||||
|
||||
:::info
|
||||
We use the term tool calling interchangeably with function calling. Although
|
||||
function calling is sometimes meant to refer to invocations of a single function,
|
||||
we treat all models as though they can return multiple tool or function calls in
|
||||
each message.
|
||||
:::
|
||||
|
||||
# Calling Tools
|
||||
|
||||
Tool calling allows a model to respond to a given prompt by generating output that
|
||||
matches a user-defined schema. While the name implies that the model is performing
|
||||
some action, this is actually not the case! The model is coming up with the
|
||||
arguments to a tool, and actually running the tool (or not) is up to the user -
|
||||
for example, if you want to [extract output matching some schema](/docs/use_cases/extraction/)
|
||||
from unstructured text, you could give the model an "extraction" tool that takes
|
||||
parameters matching the desired schema, then treat the generated output as your final
|
||||
result.
|
||||
|
||||
A tool call includes a name, arguments dict, and an optional identifier. The
|
||||
arguments dict is structured `{argument_name: argument_value}`.
|
||||
|
||||
Many LLM providers, including [Anthropic](https://www.anthropic.com/),
|
||||
[Cohere](https://cohere.com/), [Google](https://cloud.google.com/vertex-ai),
|
||||
[Mistral](https://mistral.ai/), [OpenAI](https://openai.com/), and others,
|
||||
support variants of a tool calling feature. These features typically allow requests
|
||||
to the LLM to include available tools and their schemas, and for responses to include
|
||||
calls to these tools. For instance, given a search engine tool, an LLM might handle a
|
||||
query by first issuing a call to the search engine. The system calling the LLM can
|
||||
receive the tool call, execute it, and return the output to the LLM to inform its
|
||||
response. LangChain includes a suite of [built-in tools](/docs/integrations/tools/)
|
||||
and supports several methods for defining your own [custom tools](/docs/modules/tools/custom_tools).
|
||||
Tool-calling is extremely useful for building [tool-using chains and agents](/docs/use_cases/tool_use),
|
||||
and for getting structured outputs from models more generally.
|
||||
|
||||
Providers adopt different conventions for formatting tool schemas and tool calls.
|
||||
For instance, Anthropic returns tool calls as parsed structures within a larger content block:
|
||||
```
|
||||
[
|
||||
{
|
||||
"text": "<thinking>\nI should use a tool.\n</thinking>",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"id": "id_value",
|
||||
"input": {"arg_name": "arg_value"},
|
||||
"name": "tool_name",
|
||||
"type": "tool_use"
|
||||
}
|
||||
]
|
||||
```
|
||||
whereas OpenAI separates tool calls into a distinct parameter, with arguments as JSON strings:
|
||||
```
|
||||
{
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "id_value",
|
||||
"function": {
|
||||
"arguments": '{"arg_name": "arg_value"}',
|
||||
"name": "tool_name"
|
||||
},
|
||||
"type": "function"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
LangChain implements standard interfaces for defining tools, passing them to LLMs,
|
||||
and representing tool calls.
|
||||
|
||||
## Passing tools to LLMs
|
||||
|
||||
Chat models supporting tool calling features implement a `.bind_tools` method, which
|
||||
receives a list of LangChain [tool objects](https://api.python.langchain.com/en/latest/tools/langchain_core.tools.BaseTool.html#langchain_core.tools.BaseTool)
|
||||
and binds them to the chat model in its expected format. Subsequent invocations of the
|
||||
chat model will include tool schemas in its calls to the LLM.
|
||||
|
||||
For example, we can define the schema for custom tools using the `@tool` decorator
|
||||
on Python functions:
|
||||
|
||||
```python
|
||||
from langchain.tools import tool
|
||||
|
||||
|
||||
@tool
|
||||
def add(a: int, b: int) -> int:
|
||||
"""Adds a and b."""
|
||||
return a + b
|
||||
|
||||
|
||||
@tool
|
||||
def multiply(a: int, b: int) -> int:
|
||||
"""Multiplies a and b."""
|
||||
return a * b
|
||||
|
||||
|
||||
tools = [add, multiply]
|
||||
```
|
||||
|
||||
Or below, we define the schema using Pydantic:
|
||||
```python
|
||||
from langchain_core.pydantic_v1 import BaseModel, Field
|
||||
|
||||
|
||||
# Note that the docstrings here are crucial, as they will be passed along
|
||||
# to the model along with the class name.
|
||||
class Add(BaseModel):
|
||||
"""Add two integers together."""
|
||||
|
||||
a: int = Field(..., description="First integer")
|
||||
b: int = Field(..., description="Second integer")
|
||||
|
||||
|
||||
class Multiply(BaseModel):
|
||||
"""Multiply two integers together."""
|
||||
|
||||
a: int = Field(..., description="First integer")
|
||||
b: int = Field(..., description="Second integer")
|
||||
|
||||
|
||||
tools = [Add, Multiply]
|
||||
```
|
||||
|
||||
We can bind them to chat models as follows:
|
||||
|
||||
import Tabs from "@theme/Tabs";
|
||||
import TabItem from "@theme/TabItem";
|
||||
|
||||
import ChatModelTabs from "@theme/ChatModelTabs";
|
||||
|
||||
<ChatModelTabs
|
||||
customVarName="llm"
|
||||
fireworksParams={`model="accounts/fireworks/models/firefunction-v1", temperature=0`}
|
||||
hideGoogle={true}
|
||||
hideAnthropic={false}
|
||||
/>
|
||||
|
||||
We can use the `bind_tools()` method to handle converting
|
||||
`Multiply` to a "tool" and binding it to the model (i.e.,
|
||||
passing it in each time the model is invoked).
|
||||
|
||||
```python
|
||||
llm_with_tools = llm.bind_tools(tools)
|
||||
```
|
||||
|
||||
## Tool calls
|
||||
|
||||
If tool calls are included in a LLM response, they are attached to the corresponding
|
||||
[message](https://api.python.langchain.com/en/latest/messages/langchain_core.messages.ai.AIMessage.html#langchain_core.messages.ai.AIMessage)
|
||||
or [message chunk](https://api.python.langchain.com/en/latest/messages/langchain_core.messages.ai.AIMessageChunk.html#langchain_core.messages.ai.AIMessageChunk)
|
||||
as a list of [tool call](https://api.python.langchain.com/en/latest/messages/langchain_core.messages.tool.ToolCall.html#langchain_core.messages.tool.ToolCall)
|
||||
objects in the `.tool_calls` attribute. A `ToolCall` is a typed dict that includes a
|
||||
tool name, dict of argument values, and (optionally) an identifier. Messages with no
|
||||
tool calls default to an empty list for this attribute.
|
||||
|
||||
Example:
|
||||
|
||||
```python
|
||||
query = "What is 3 * 12? Also, what is 11 + 49?"
|
||||
|
||||
llm_with_tools.invoke(query).tool_calls
|
||||
```
|
||||
```text
|
||||
[{'name': 'Multiply',
|
||||
'args': {'a': 3, 'b': 12},
|
||||
'id': 'call_viACG45wBz9jYzljHIwHamXw'},
|
||||
{'name': 'Add',
|
||||
'args': {'a': 11, 'b': 49},
|
||||
'id': 'call_JMFUqoi5L27rGeMuII4MJMWo'}]
|
||||
```
|
||||
|
||||
The `.tool_calls` attribute should contain valid tool calls. Note that on occasion,
|
||||
model providers may output malformed tool calls (e.g., arguments that are not
|
||||
valid JSON). When parsing fails in these cases, instances
|
||||
of [InvalidToolCall](https://api.python.langchain.com/en/latest/messages/langchain_core.messages.tool.InvalidToolCall.html#langchain_core.messages.tool.InvalidToolCall)
|
||||
are populated in the `.invalid_tool_calls` attribute. An `InvalidToolCall` can have
|
||||
a name, string arguments, identifier, and error message.
|
||||
|
||||
If desired, [output parsers](/docs/modules/model_io/output_parsers) can further
|
||||
process the output. For example, we can convert back to the original Pydantic class:
|
||||
|
||||
```python
|
||||
from langchain_core.output_parsers.openai_tools import PydanticToolsParser
|
||||
|
||||
chain = llm_with_tools | PydanticToolsParser(tools=[Multiply, Add])
|
||||
chain.invoke(query)
|
||||
```
|
||||
```text
|
||||
[Multiply(a=3, b=12), Add(a=11, b=49)]
|
||||
```
|
||||
|
||||
### Streaming
|
||||
|
||||
When tools are called in a streaming context,
|
||||
[message chunks](https://api.python.langchain.com/en/latest/messages/langchain_core.messages.ai.AIMessageChunk.html#langchain_core.messages.ai.AIMessageChunk)
|
||||
will be populated with [tool call chunk](https://api.python.langchain.com/en/latest/messages/langchain_core.messages.tool.ToolCallChunk.html#langchain_core.messages.tool.ToolCallChunk)
|
||||
objects in a list via the `.tool_call_chunks` attribute. A `ToolCallChunk` includes
|
||||
optional string fields for the tool `name`, `args`, and `id`, and includes an optional
|
||||
integer field `index` that can be used to join chunks together. Fields are optional
|
||||
because portions of a tool call may be streamed across different chunks (e.g., a chunk
|
||||
that includes a substring of the arguments may have null values for the tool name and id).
|
||||
|
||||
Because message chunks inherit from their parent message class, an
|
||||
[AIMessageChunk](https://api.python.langchain.com/en/latest/messages/langchain_core.messages.ai.AIMessageChunk.html#langchain_core.messages.ai.AIMessageChunk)
|
||||
with tool call chunks will also include `.tool_calls` and `.invalid_tool_calls` fields.
|
||||
These fields are parsed best-effort from the message's tool call chunks.
|
||||
|
||||
Note that not all providers currently support streaming for tool calls.
|
||||
|
||||
Example:
|
||||
|
||||
```python
|
||||
async for chunk in llm_with_tools.astream(query):
|
||||
print(chunk.tool_call_chunks)
|
||||
```
|
||||
|
||||
```text
|
||||
[]
|
||||
[{'name': 'Multiply', 'args': '', 'id': 'call_Al2xpR4uFPXQUDzGTSawMOah', 'index': 0}]
|
||||
[{'name': None, 'args': '{"a"', 'id': None, 'index': 0}]
|
||||
[{'name': None, 'args': ': 3, ', 'id': None, 'index': 0}]
|
||||
[{'name': None, 'args': '"b": 1', 'id': None, 'index': 0}]
|
||||
[{'name': None, 'args': '2}', 'id': None, 'index': 0}]
|
||||
[{'name': 'Add', 'args': '', 'id': 'call_VV6ck8JSQ6joKtk2xGtNKgXf', 'index': 1}]
|
||||
[{'name': None, 'args': '{"a"', 'id': None, 'index': 1}]
|
||||
[{'name': None, 'args': ': 11,', 'id': None, 'index': 1}]
|
||||
[{'name': None, 'args': ' "b": ', 'id': None, 'index': 1}]
|
||||
[{'name': None, 'args': '49}', 'id': None, 'index': 1}]
|
||||
[]
|
||||
```
|
||||
|
||||
Note that adding message chunks will merge their corresponding tool call chunks. This is the principle by which LangChain's various [tool output parsers](/docs/modules/model_io/output_parsers/types/openai_tools/) support streaming.
|
||||
|
||||
For example, below we accumulate tool call chunks:
|
||||
|
||||
```python
|
||||
first = True
|
||||
async for chunk in llm_with_tools.astream(query):
|
||||
if first:
|
||||
gathered = chunk
|
||||
first = False
|
||||
else:
|
||||
gathered = gathered + chunk
|
||||
|
||||
print(gathered.tool_call_chunks)
|
||||
```
|
||||
|
||||
```text
|
||||
[]
|
||||
[{'name': 'Multiply', 'args': '', 'id': 'call_2MG1IGft6WmgMooqZgJ07JX6', 'index': 0}]
|
||||
[{'name': 'Multiply', 'args': '{"a"', 'id': 'call_2MG1IGft6WmgMooqZgJ07JX6', 'index': 0}]
|
||||
[{'name': 'Multiply', 'args': '{"a": 3, ', 'id': 'call_2MG1IGft6WmgMooqZgJ07JX6', 'index': 0}]
|
||||
[{'name': 'Multiply', 'args': '{"a": 3, "b": 1', 'id': 'call_2MG1IGft6WmgMooqZgJ07JX6', 'index': 0}]
|
||||
[{'name': 'Multiply', 'args': '{"a": 3, "b": 12}', 'id': 'call_2MG1IGft6WmgMooqZgJ07JX6', 'index': 0}]
|
||||
[{'name': 'Multiply', 'args': '{"a": 3, "b": 12}', 'id': 'call_2MG1IGft6WmgMooqZgJ07JX6', 'index': 0}, {'name': 'Add', 'args': '', 'id': 'call_uGot9MOHDcz67Bj0h13c7QA5', 'index': 1}]
|
||||
[{'name': 'Multiply', 'args': '{"a": 3, "b": 12}', 'id': 'call_2MG1IGft6WmgMooqZgJ07JX6', 'index': 0}, {'name': 'Add', 'args': '{"a"', 'id': 'call_uGot9MOHDcz67Bj0h13c7QA5', 'index': 1}]
|
||||
[{'name': 'Multiply', 'args': '{"a": 3, "b": 12}', 'id': 'call_2MG1IGft6WmgMooqZgJ07JX6', 'index': 0}, {'name': 'Add', 'args': '{"a": 11,', 'id': 'call_uGot9MOHDcz67Bj0h13c7QA5', 'index': 1}]
|
||||
[{'name': 'Multiply', 'args': '{"a": 3, "b": 12}', 'id': 'call_2MG1IGft6WmgMooqZgJ07JX6', 'index': 0}, {'name': 'Add', 'args': '{"a": 11, "b": ', 'id': 'call_uGot9MOHDcz67Bj0h13c7QA5', 'index': 1}]
|
||||
[{'name': 'Multiply', 'args': '{"a": 3, "b": 12}', 'id': 'call_2MG1IGft6WmgMooqZgJ07JX6', 'index': 0}, {'name': 'Add', 'args': '{"a": 11, "b": 49}', 'id': 'call_uGot9MOHDcz67Bj0h13c7QA5', 'index': 1}]
|
||||
[{'name': 'Multiply', 'args': '{"a": 3, "b": 12}', 'id': 'call_2MG1IGft6WmgMooqZgJ07JX6', 'index': 0}, {'name': 'Add', 'args': '{"a": 11, "b": 49}', 'id': 'call_uGot9MOHDcz67Bj0h13c7QA5', 'index': 1}]
|
||||
```
|
||||
|
||||
```python
|
||||
print(type(gathered.tool_call_chunks[0]["args"]))
|
||||
```
|
||||
|
||||
```text
|
||||
<class 'str'>
|
||||
```
|
||||
|
||||
And below we accumulate tool calls to demonstrate partial parsing:
|
||||
|
||||
```python
|
||||
first = True
|
||||
async for chunk in llm_with_tools.astream(query):
|
||||
if first:
|
||||
gathered = chunk
|
||||
first = False
|
||||
else:
|
||||
gathered = gathered + chunk
|
||||
|
||||
print(gathered.tool_calls)
|
||||
```
|
||||
|
||||
```text
|
||||
[]
|
||||
[]
|
||||
[{'name': 'Multiply', 'args': {}, 'id': 'call_z3B4o82SQDY5NCnmrXIcVQo4'}]
|
||||
[{'name': 'Multiply', 'args': {'a': 3}, 'id': 'call_z3B4o82SQDY5NCnmrXIcVQo4'}]
|
||||
[{'name': 'Multiply', 'args': {'a': 3, 'b': 1}, 'id': 'call_z3B4o82SQDY5NCnmrXIcVQo4'}]
|
||||
[{'name': 'Multiply', 'args': {'a': 3, 'b': 12}, 'id': 'call_z3B4o82SQDY5NCnmrXIcVQo4'}]
|
||||
[{'name': 'Multiply', 'args': {'a': 3, 'b': 12}, 'id': 'call_z3B4o82SQDY5NCnmrXIcVQo4'}]
|
||||
[{'name': 'Multiply', 'args': {'a': 3, 'b': 12}, 'id': 'call_z3B4o82SQDY5NCnmrXIcVQo4'}, {'name': 'Add', 'args': {}, 'id': 'call_zPAyMWr8hN1q083GWGX2dSiB'}]
|
||||
[{'name': 'Multiply', 'args': {'a': 3, 'b': 12}, 'id': 'call_z3B4o82SQDY5NCnmrXIcVQo4'}, {'name': 'Add', 'args': {'a': 11}, 'id': 'call_zPAyMWr8hN1q083GWGX2dSiB'}]
|
||||
[{'name': 'Multiply', 'args': {'a': 3, 'b': 12}, 'id': 'call_z3B4o82SQDY5NCnmrXIcVQo4'}, {'name': 'Add', 'args': {'a': 11}, 'id': 'call_zPAyMWr8hN1q083GWGX2dSiB'}]
|
||||
[{'name': 'Multiply', 'args': {'a': 3, 'b': 12}, 'id': 'call_z3B4o82SQDY5NCnmrXIcVQo4'}, {'name': 'Add', 'args': {'a': 11, 'b': 49}, 'id': 'call_zPAyMWr8hN1q083GWGX2dSiB'}]
|
||||
[{'name': 'Multiply', 'args': {'a': 3, 'b': 12}, 'id': 'call_z3B4o82SQDY5NCnmrXIcVQo4'}, {'name': 'Add', 'args': {'a': 11, 'b': 49}, 'id': 'call_zPAyMWr8hN1q083GWGX2dSiB'}]
|
||||
```
|
||||
|
||||
```python
|
||||
print(type(gathered.tool_calls[0]["args"]))
|
||||
```
|
||||
|
||||
```text
|
||||
<class 'dict'>
|
||||
```
|
||||
|
||||
|
||||
## Next steps
|
||||
|
||||
- **Output parsing**: See [OpenAI Tools output
|
||||
parsers](/docs/modules/model_io/output_parsers/types/openai_tools/)
|
||||
and [OpenAI Functions output
|
||||
parsers](/docs/modules/model_io/output_parsers/types/openai_functions/)
|
||||
to learn about extracting the function calling API responses into
|
||||
various formats.
|
||||
- **Structured output chains**: [Some models have constructors](/docs/modules/model_io/chat/structured_output/) that
|
||||
handle creating a structured output chain for you.
|
||||
- **Tool use**: See how to construct chains and agents that actually
|
||||
call the invoked tools in [these
|
||||
guides](/docs/use_cases/tool_use/).
|
||||
@@ -30,4 +30,4 @@ This includes:
|
||||
- [How to use ChatModels that support function calling](./function_calling)
|
||||
- [How to stream responses from a ChatModel](./streaming)
|
||||
- [How to track token usage in a ChatModel call](./token_usage_tracking)
|
||||
- [How to creat a custom ChatModel](./custom_chat_model)
|
||||
- [How to create a custom ChatModel](./custom_chat_model)
|
||||
|
||||
@@ -83,6 +83,14 @@
|
||||
" print(s)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "af204787",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Find out api documentation for [CommaSeparatedListOutputParser](https://api.python.langchain.com/en/latest/output_parsers/langchain_core.output_parsers.list.CommaSeparatedListOutputParser.html#langchain_core.output_parsers.list.CommaSeparatedListOutputParser)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
|
||||
@@ -100,6 +100,14 @@
|
||||
"print(output)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "8a12b77a",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Find out api documentation for [DatetimeOutputParser](https://api.python.langchain.com/en/latest/output_parsers/langchain.output_parsers.datetime.DatetimeOutputParser.html#langchain.output_parsers.datetime.DatetimeOutputParser)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
|
||||
@@ -87,6 +87,14 @@
|
||||
"chain.invoke({\"person\": \"Frank Sinatra\"})"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "b1adc71f",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Find out api documentation for [EnumOutputParser](https://api.python.langchain.com/en/latest/output_parsers/langchain.output_parsers.enum.EnumOutputParser.html#langchain.output_parsers.enum.EnumOutputParser)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"\n",
|
||||
"Keep in mind that large language models are leaky abstractions! You'll have to use an LLM with sufficient capacity to generate well-formed JSON. In the OpenAI family, DaVinci can do reliably but Curie's ability already drops off dramatically. \n",
|
||||
"\n",
|
||||
"You can optionally use Pydantic to declare your data model."
|
||||
"You can optionally use Pydantic to declare your data model. \n"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -172,6 +172,14 @@
|
||||
"chain.invoke({\"query\": joke_query})"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "6d9b8f6c",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Find out api documentation for [JsonOutputParser](https://api.python.langchain.com/en/latest/output_parsers/langchain_core.output_parsers.json.JsonOutputParser.html#langchain_core.output_parsers.json.JsonOutputParser)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
|
||||
@@ -126,6 +126,14 @@
|
||||
"new_parser.parse(misformatted)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "84498e02",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Find out api documentation for [OutputFixingParser](https://api.python.langchain.com/en/latest/output_parsers/langchain.output_parsers.fix.OutputFixingParser.html#langchain.output_parsers.fix.OutputFixingParser)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
|
||||
@@ -203,6 +203,13 @@
|
||||
"parser_output = chain.invoke({\"query\": df_query})"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Find out api documentation for [PandasDataFrameOutputParser](https://api.python.langchain.com/en/latest/output_parsers/langchain.output_parsers.pandas_dataframe.PandasDataFrameOutputParser.html#langchain.output_parsers.pandas_dataframe.PandasDataFrameOutputParser)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
|
||||
@@ -125,6 +125,14 @@
|
||||
"chain.invoke({\"query\": actor_query})"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "e227d9a0",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Find out api documentation for [PydanticOutputParser](https://api.python.langchain.com/en/latest/output_parsers/langchain_core.output_parsers.pydantic.PydanticOutputParser.html#langchain_core.output_parsers.pydantic.PydanticOutputParser)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
|
||||
@@ -243,6 +243,14 @@
|
||||
"main_chain.invoke({\"query\": \"who is leo di caprios gf?\"})"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "e3a2513a",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Find out api documentation for [RetryOutputParser](https://api.python.langchain.com/en/latest/output_parsers/langchain.output_parsers.retry.RetryOutputParser.html#langchain.output_parsers.retry.RetryOutputParser)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
|
||||
@@ -115,6 +115,14 @@
|
||||
" print(s)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "1f97aa07",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Find out api documentation for [StructuredOutputParser](https://api.python.langchain.com/en/latest/output_parsers/langchain.output_parsers.structured.StructuredOutputParser.html#langchain.output_parsers.structured.StructuredOutputParser)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
|
||||
@@ -178,6 +178,14 @@
|
||||
" print(s)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "09c711fb",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Find out api documentation for [XMLOutputParser](https://api.python.langchain.com/en/latest/output_parsers/langchain_core.output_parsers.xml.XMLOutputParser.html#langchain_core.output_parsers.xml.XMLOutputParser)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
|
||||
@@ -86,6 +86,14 @@
|
||||
"chain.invoke({\"query\": joke_query})"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "f859ace0",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Find out api documentation for [YamlOutputParser](https://api.python.langchain.com/en/latest/output_parsers/langchain.output_parsers.yaml.YamlOutputParser.html#langchain.output_parsers.yaml.YamlOutputParser)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"source": [
|
||||
"from langchain.prompts import FewShotPromptTemplate, PromptTemplate\n",
|
||||
"from langchain.prompts.example_selector import SemanticSimilarityExampleSelector\n",
|
||||
"from langchain_community.vectorstores import Chroma\n",
|
||||
"from langchain_chroma import Chroma\n",
|
||||
"from langchain_openai import OpenAIEmbeddings\n",
|
||||
"\n",
|
||||
"example_prompt = PromptTemplate(\n",
|
||||
|
||||
@@ -254,7 +254,7 @@
|
||||
],
|
||||
"source": [
|
||||
"from langchain.prompts.example_selector import SemanticSimilarityExampleSelector\n",
|
||||
"from langchain_community.vectorstores import Chroma\n",
|
||||
"from langchain_chroma import Chroma\n",
|
||||
"from langchain_openai import OpenAIEmbeddings\n",
|
||||
"\n",
|
||||
"example_selector = SemanticSimilarityExampleSelector.from_examples(\n",
|
||||
|
||||
@@ -202,7 +202,7 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.prompts import SemanticSimilarityExampleSelector\n",
|
||||
"from langchain_community.vectorstores import Chroma\n",
|
||||
"from langchain_chroma import Chroma\n",
|
||||
"from langchain_openai import OpenAIEmbeddings"
|
||||
]
|
||||
},
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%pip install --upgrade --quiet langchain langchain-openai\n",
|
||||
"%pip install --upgrade --quiet langchain langchain-openai langchain-chroma\n",
|
||||
"\n",
|
||||
"# Set env var OPENAI_API_KEY or load from a .env file:\n",
|
||||
"import dotenv\n",
|
||||
@@ -391,7 +391,7 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%pip install --upgrade --quiet chromadb beautifulsoup4"
|
||||
"%pip install --upgrade --quiet langchain-chroma beautifulsoup4"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -445,7 +445,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_community.vectorstores import Chroma\n",
|
||||
"from langchain_chroma import Chroma\n",
|
||||
"from langchain_openai import OpenAIEmbeddings\n",
|
||||
"\n",
|
||||
"vectorstore = Chroma.from_documents(documents=all_splits, embedding=OpenAIEmbeddings())"
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%pip install --upgrade --quiet langchain langchain-openai chromadb beautifulsoup4\n",
|
||||
"%pip install --upgrade --quiet langchain langchain-openai langchain-chroma beautifulsoup4\n",
|
||||
"\n",
|
||||
"# Set env var OPENAI_API_KEY or load from a .env file:\n",
|
||||
"import dotenv\n",
|
||||
@@ -129,7 +129,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_community.vectorstores import Chroma\n",
|
||||
"from langchain_chroma import Chroma\n",
|
||||
"from langchain_openai import OpenAIEmbeddings\n",
|
||||
"\n",
|
||||
"vectorstore = Chroma.from_documents(documents=all_splits, embedding=OpenAIEmbeddings())"
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install --upgrade --quiet langchain-openai tiktoken chromadb langchain git\n",
|
||||
"%pip install --upgrade --quiet langchain-openai tiktoken langchain-chroma langchain git\n",
|
||||
"\n",
|
||||
"# Set env var OPENAI_API_KEY or load from a .env file\n",
|
||||
"# import dotenv\n",
|
||||
@@ -201,7 +201,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_community.vectorstores import Chroma\n",
|
||||
"from langchain_chroma import Chroma\n",
|
||||
"from langchain_openai import OpenAIEmbeddings\n",
|
||||
"\n",
|
||||
"db = Chroma.from_documents(texts, OpenAIEmbeddings(disallowed_special=()))\n",
|
||||
|
||||
@@ -89,12 +89,12 @@
|
||||
" # 1. Each field is an `optional` -- this allows the model to decline to extract it!\n",
|
||||
" # 2. Each field has a `description` -- this description is used by the LLM.\n",
|
||||
" # Having a good description can help improve extraction results.\n",
|
||||
" name: Optional[str] = Field(..., description=\"The name of the person\")\n",
|
||||
" name: Optional[str] = Field(default=None, description=\"The name of the person\")\n",
|
||||
" hair_color: Optional[str] = Field(\n",
|
||||
" ..., description=\"The color of the peron's hair if known\"\n",
|
||||
" default=None, description=\"The color of the peron's hair if known\"\n",
|
||||
" )\n",
|
||||
" height_in_meters: Optional[str] = Field(\n",
|
||||
" ..., description=\"Height measured in meters\"\n",
|
||||
" default=None, description=\"Height measured in meters\"\n",
|
||||
" )"
|
||||
]
|
||||
},
|
||||
@@ -254,12 +254,12 @@
|
||||
" # 1. Each field is an `optional` -- this allows the model to decline to extract it!\n",
|
||||
" # 2. Each field has a `description` -- this description is used by the LLM.\n",
|
||||
" # Having a good description can help improve extraction results.\n",
|
||||
" name: Optional[str] = Field(..., description=\"The name of the person\")\n",
|
||||
" name: Optional[str] = Field(default=None, description=\"The name of the person\")\n",
|
||||
" hair_color: Optional[str] = Field(\n",
|
||||
" ..., description=\"The color of the peron's hair if known\"\n",
|
||||
" default=None, description=\"The color of the peron's hair if known\"\n",
|
||||
" )\n",
|
||||
" height_in_meters: Optional[str] = Field(\n",
|
||||
" ..., description=\"Height measured in meters\"\n",
|
||||
" default=None, description=\"Height measured in meters\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# %pip install -qU langchain langchain-community langchain-openai faker"
|
||||
"# %pip install -qU langchain langchain-community langchain-openai faker langchain-chroma"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -394,7 +394,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_community.vectorstores import Chroma\n",
|
||||
"from langchain_chroma import Chroma\n",
|
||||
"from langchain_openai import OpenAIEmbeddings\n",
|
||||
"\n",
|
||||
"embeddings = OpenAIEmbeddings(model=\"text-embedding-3-small\")\n",
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# %pip install -qU langchain langchain-community langchain-openai chromadb"
|
||||
"# %pip install -qU langchain langchain-community langchain-openai langchain-chroma"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -84,7 +84,7 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.text_splitter import RecursiveCharacterTextSplitter\n",
|
||||
"from langchain_community.vectorstores import Chroma\n",
|
||||
"from langchain_chroma import Chroma\n",
|
||||
"from langchain_openai import OpenAIEmbeddings\n",
|
||||
"\n",
|
||||
"texts = [\"Harrison worked at Kensho\", \"Ankush worked at Facebook\"]\n",
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# %pip install -qU langchain langchain-community langchain-openai chromadb"
|
||||
"# %pip install -qU langchain langchain-community langchain-openai langchain-chroma"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -84,7 +84,7 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.text_splitter import RecursiveCharacterTextSplitter\n",
|
||||
"from langchain_community.vectorstores import Chroma\n",
|
||||
"from langchain_chroma import Chroma\n",
|
||||
"from langchain_openai import OpenAIEmbeddings\n",
|
||||
"\n",
|
||||
"texts = [\"Harrison worked at Kensho\"]\n",
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# %pip install -qU langchain langchain-community langchain-openai chromadb"
|
||||
"# %pip install -qU langchain langchain-community langchain-openai langchain-chroma"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -86,7 +86,7 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.text_splitter import RecursiveCharacterTextSplitter\n",
|
||||
"from langchain_community.vectorstores import Chroma\n",
|
||||
"from langchain_chroma import Chroma\n",
|
||||
"from langchain_openai import OpenAIEmbeddings\n",
|
||||
"\n",
|
||||
"texts = [\"Harrison worked at Kensho\"]\n",
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# %pip install -qU langchain langchain-community langchain-openai youtube-transcript-api pytube chromadb"
|
||||
"# %pip install -qU langchain langchain-community langchain-openai youtube-transcript-api pytube langchain-chroma"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -249,7 +249,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_community.vectorstores import Chroma\n",
|
||||
"from langchain_chroma import Chroma\n",
|
||||
"from langchain_openai import OpenAIEmbeddings\n",
|
||||
"from langchain_text_splitters import RecursiveCharacterTextSplitter\n",
|
||||
"\n",
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install --upgrade --quiet langchain langchain-community langchainhub langchain-openai chromadb bs4"
|
||||
"%pip install --upgrade --quiet langchain langchain-community langchainhub langchain-openai langchain-chroma bs4"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -118,8 +118,8 @@
|
||||
"source": [
|
||||
"import bs4\n",
|
||||
"from langchain import hub\n",
|
||||
"from langchain_chroma import Chroma\n",
|
||||
"from langchain_community.document_loaders import WebBaseLoader\n",
|
||||
"from langchain_community.vectorstores import Chroma\n",
|
||||
"from langchain_core.output_parsers import StrOutputParser\n",
|
||||
"from langchain_core.runnables import RunnablePassthrough\n",
|
||||
"from langchain_openai import ChatOpenAI, OpenAIEmbeddings\n",
|
||||
@@ -406,9 +406,9 @@
|
||||
"from langchain import hub\n",
|
||||
"from langchain.chains import create_history_aware_retriever, create_retrieval_chain\n",
|
||||
"from langchain.chains.combine_documents import create_stuff_documents_chain\n",
|
||||
"from langchain_chroma import Chroma\n",
|
||||
"from langchain_community.chat_message_histories import ChatMessageHistory\n",
|
||||
"from langchain_community.document_loaders import WebBaseLoader\n",
|
||||
"from langchain_community.vectorstores import Chroma\n",
|
||||
"from langchain_core.chat_history import BaseChatMessageHistory\n",
|
||||
"from langchain_core.output_parsers import StrOutputParser\n",
|
||||
"from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n",
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install --upgrade --quiet langchain langchain-community langchainhub gpt4all chromadb "
|
||||
"%pip install --upgrade --quiet langchain langchain-community langchainhub gpt4all langchain-chroma "
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -72,8 +72,8 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_chroma import Chroma\n",
|
||||
"from langchain_community.embeddings import GPT4AllEmbeddings\n",
|
||||
"from langchain_community.vectorstores import Chroma\n",
|
||||
"\n",
|
||||
"vectorstore = Chroma.from_documents(documents=all_splits, embedding=GPT4AllEmbeddings())"
|
||||
]
|
||||
|
||||
@@ -72,7 +72,7 @@ in this walkthrough, but everything shown here works with any
|
||||
We’ll use the following packages:
|
||||
|
||||
```python
|
||||
%pip install --upgrade --quiet langchain langchain-community langchainhub langchain-openai chromadb bs4
|
||||
%pip install --upgrade --quiet langchain langchain-community langchainhub langchain-openai langchain-chroma bs4
|
||||
```
|
||||
|
||||
We need to set environment variable `OPENAI_API_KEY` for the embeddings model, which can be done
|
||||
@@ -120,7 +120,7 @@ lines of code:
|
||||
import bs4
|
||||
from langchain import hub
|
||||
from langchain_community.document_loaders import WebBaseLoader
|
||||
from langchain_community.vectorstores import Chroma
|
||||
from langchain_chroma import Chroma
|
||||
from langchain_core.output_parsers import StrOutputParser
|
||||
from langchain_core.runnables import RunnablePassthrough
|
||||
from langchain_openai import OpenAIEmbeddings
|
||||
@@ -350,7 +350,7 @@ vector store and
|
||||
model.
|
||||
|
||||
```python
|
||||
from langchain_community.vectorstores import Chroma
|
||||
from langchain_chroma import Chroma
|
||||
from langchain_openai import OpenAIEmbeddings
|
||||
|
||||
vectorstore = Chroma.from_documents(documents=all_splits, embedding=OpenAIEmbeddings())
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install --upgrade --quiet langchain langchain-community langchainhub langchain-openai chromadb bs4"
|
||||
"%pip install --upgrade --quiet langchain langchain-community langchainhub langchain-openai langchain-chroma bs4"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -113,8 +113,8 @@
|
||||
"source": [
|
||||
"import bs4\n",
|
||||
"from langchain import hub\n",
|
||||
"from langchain_chroma import Chroma\n",
|
||||
"from langchain_community.document_loaders import WebBaseLoader\n",
|
||||
"from langchain_community.vectorstores import Chroma\n",
|
||||
"from langchain_core.output_parsers import StrOutputParser\n",
|
||||
"from langchain_core.runnables import RunnablePassthrough\n",
|
||||
"from langchain_openai import ChatOpenAI, OpenAIEmbeddings\n",
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install --upgrade --quiet langchain langchain-community langchainhub langchain-openai chromadb bs4"
|
||||
"%pip install --upgrade --quiet langchain langchain-community langchainhub langchain-openai langchain-chroma bs4"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -113,8 +113,8 @@
|
||||
"source": [
|
||||
"import bs4\n",
|
||||
"from langchain import hub\n",
|
||||
"from langchain_chroma import Chroma\n",
|
||||
"from langchain_community.document_loaders import WebBaseLoader\n",
|
||||
"from langchain_community.vectorstores import Chroma\n",
|
||||
"from langchain_core.output_parsers import StrOutputParser\n",
|
||||
"from langchain_core.runnables import RunnableParallel, RunnablePassthrough\n",
|
||||
"from langchain_openai import ChatOpenAI, OpenAIEmbeddings\n",
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
"jp-MarkdownHeadingCollapsed": true
|
||||
},
|
||||
"source": [
|
||||
"## Agents\n",
|
||||
"## Repeated tool use with agents\n",
|
||||
"\n",
|
||||
"Chains are great when we know the specific sequence of tool usage needed for any user input. But for certain use cases, how many times we use tools depends on the input. In these cases, we want to let the model itself decide how many times to use tools and in what order. [Agents](/docs/modules/agents/) let us do just this.\n",
|
||||
"\n",
|
||||
"LangChain comes with a number of built-in agents that are optimized for different use cases. Read about all the [agent types here](/docs/modules/agents/agent_types/).\n",
|
||||
"\n",
|
||||
"As an example, let's try out the OpenAI tools agent, which makes use of the new OpenAI tool-calling API (this is only available in the latest OpenAI models, and differs from function-calling in that the model can return multiple function invocations at once).\n",
|
||||
"We'll use the [tool calling agent](/docs/modules/agents/agent_types/tool_calling/), which is generally the most reliable kind and the recommended one for most use cases. \"Tool calling\" in this case refers to a specific type of model API that allows for explicitly passing tool definitions to models and getting explicit tool invocations out. For more on tool calling models see [this guide].(/docs/modules/model_io/chat/function_calling/)\n",
|
||||
"\n",
|
||||
""
|
||||
]
|
||||
@@ -45,7 +45,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install --upgrade --quiet langchain langchain-openai"
|
||||
"%pip install --upgrade --quiet langchain langchainhub"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -53,12 +53,12 @@
|
||||
"id": "a33915ce-00c5-4379-8a83-c0053e471cdb",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"And set these environment variables:"
|
||||
"If you'd like to use LangSmith, set the environment variables below:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 1,
|
||||
"id": "54667a49-c226-486d-a887-33120c90cc91",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -66,9 +66,7 @@
|
||||
"import getpass\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"os.environ[\"OPENAI_API_KEY\"] = getpass.getpass()\n",
|
||||
"\n",
|
||||
"# If you'd like to use LangSmith, uncomment the below\n",
|
||||
"# os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
|
||||
"# os.environ[\"LANGCHAIN_API_KEY\"] = getpass.getpass()"
|
||||
]
|
||||
@@ -85,7 +83,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"execution_count": 2,
|
||||
"id": "1c44ba79-6ab2-4d55-8247-82fca4d9b70c",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -124,19 +122,18 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"execution_count": 3,
|
||||
"id": "e27a4e1a-938b-4b60-8e32-25e4ee530274",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain import hub\n",
|
||||
"from langchain.agents import AgentExecutor, create_openai_tools_agent\n",
|
||||
"from langchain_openai import ChatOpenAI"
|
||||
"from langchain.agents import AgentExecutor, create_tool_calling_agent"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"execution_count": 4,
|
||||
"id": "bcc9536e-0328-4e29-9d3d-133f3e63e589",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -173,27 +170,45 @@
|
||||
"id": "85e9875a-d8d4-4712-b3f0-b513c684451b",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Create agent"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "a1c5319d-6609-449d-8dd0-127e9a600656",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Choose the LLM that will drive the agent\n",
|
||||
"# Only certain models support this\n",
|
||||
"model = ChatOpenAI(model=\"gpt-3.5-turbo-1106\", temperature=0)\n",
|
||||
"## Create agent\n",
|
||||
"\n",
|
||||
"# Construct the OpenAI Tools agent\n",
|
||||
"agent = create_openai_tools_agent(model, tools, prompt)"
|
||||
"We'll need to use a model with tool calling capabilities. You can see which models support tool calling [here](/docs/integrations/chat/).\n",
|
||||
"\n",
|
||||
"```{=mdx}\n",
|
||||
"import ChatModelTabs from \"@theme/ChatModelTabs\";\n",
|
||||
"\n",
|
||||
"<ChatModelTabs customVarName=\"llm\"/>\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "9583aef3-a2cf-461e-8506-8a22f4c730b8",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# | echo: false\n",
|
||||
"# | output: false\n",
|
||||
"from langchain_anthropic import ChatAnthropic\n",
|
||||
"\n",
|
||||
"llm = ChatAnthropic(model=\"claude-3-sonnet-20240229\", temperature=0)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "a1c5319d-6609-449d-8dd0-127e9a600656",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Construct the tool calling agent\n",
|
||||
"agent = create_tool_calling_agent(llm, tools, prompt)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "c86bfe50-c5b3-49ed-86c8-1fe8dcd0c83a",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -212,7 +227,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"execution_count": 8,
|
||||
"id": "c098f8df-fd7f-4c13-963a-8e34194d3f84",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -225,21 +240,23 @@
|
||||
"\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
|
||||
"\u001b[32;1m\u001b[1;3m\n",
|
||||
"Invoking: `exponentiate` with `{'base': 3, 'exponent': 5}`\n",
|
||||
"\n",
|
||||
"responded: [{'text': \"Okay, let's break this down step-by-step:\", 'type': 'text'}, {'id': 'toolu_01CjdiDhDmMtaT1F4R7hSV5D', 'input': {'base': 3, 'exponent': 5}, 'name': 'exponentiate', 'type': 'tool_use'}]\n",
|
||||
"\n",
|
||||
"\u001b[0m\u001b[38;5;200m\u001b[1;3m243\u001b[0m\u001b[32;1m\u001b[1;3m\n",
|
||||
"Invoking: `add` with `{'first_int': 12, 'second_int': 3}`\n",
|
||||
"\n",
|
||||
"responded: [{'text': '3 to the 5th power is 243.', 'type': 'text'}, {'id': 'toolu_01EKqn4E5w3Zj7bQ8s8xmi4R', 'input': {'first_int': 12, 'second_int': 3}, 'name': 'add', 'type': 'tool_use'}]\n",
|
||||
"\n",
|
||||
"\u001b[0m\u001b[33;1m\u001b[1;3m15\u001b[0m\u001b[32;1m\u001b[1;3m\n",
|
||||
"Invoking: `multiply` with `{'first_int': 243, 'second_int': 15}`\n",
|
||||
"\n",
|
||||
"responded: [{'text': '12 + 3 = 15', 'type': 'text'}, {'id': 'toolu_017VZJgZBYbwMo2KGD6o6hsQ', 'input': {'first_int': 243, 'second_int': 15}, 'name': 'multiply', 'type': 'tool_use'}]\n",
|
||||
"\n",
|
||||
"\u001b[0m\u001b[36;1m\u001b[1;3m3645\u001b[0m\u001b[32;1m\u001b[1;3m\n",
|
||||
"Invoking: `exponentiate` with `{'base': 3645, 'exponent': 2}`\n",
|
||||
"Invoking: `multiply` with `{'first_int': 3645, 'second_int': 3645}`\n",
|
||||
"responded: [{'text': '243 * 15 = 3645', 'type': 'text'}, {'id': 'toolu_01RtFCcQgbVGya3NVDgTYKTa', 'input': {'first_int': 3645, 'second_int': 3645}, 'name': 'multiply', 'type': 'tool_use'}]\n",
|
||||
"\n",
|
||||
"\u001b[0m\u001b[36;1m\u001b[1;3m13286025\u001b[0m\u001b[32;1m\u001b[1;3mSo 3645 squared is 13,286,025.\n",
|
||||
"\n",
|
||||
"\u001b[0m\u001b[38;5;200m\u001b[1;3m13286025\u001b[0m\u001b[32;1m\u001b[1;3mThe result of raising 3 to the fifth power and multiplying that by the sum of twelve and three, then squaring the whole result is 13,286,025.\u001b[0m\n",
|
||||
"Therefore, the final result of taking 3 to the 5th power (243), multiplying by 12 + 3 (15), and then squaring the whole result is 13,286,025.\u001b[0m\n",
|
||||
"\n",
|
||||
"\u001b[1m> Finished chain.\u001b[0m\n"
|
||||
]
|
||||
@@ -248,10 +265,10 @@
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'input': 'Take 3 to the fifth power and multiply that by the sum of twelve and three, then square the whole result',\n",
|
||||
" 'output': 'The result of raising 3 to the fifth power and multiplying that by the sum of twelve and three, then squaring the whole result is 13,286,025.'}"
|
||||
" 'output': 'So 3645 squared is 13,286,025.\\n\\nTherefore, the final result of taking 3 to the 5th power (243), multiplying by 12 + 3 (15), and then squaring the whole result is 13,286,025.'}"
|
||||
]
|
||||
},
|
||||
"execution_count": 6,
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -263,13 +280,21 @@
|
||||
" }\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "4ecc190c-c133-493e-bd3e-f73e9690bae1",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"You can see the [LangSmith trace here](https://smith.langchain.com/public/92694ff3-71b7-44ed-bc45-04bdf04d4689/r)."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "poetry-venv",
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "poetry-venv"
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install --upgrade --quiet langchain langchain-openai"
|
||||
"%pip install --upgrade --quiet langchain"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -48,8 +48,6 @@
|
||||
"import getpass\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"os.environ[\"OPENAI_API_KEY\"] = getpass.getpass()\n",
|
||||
"\n",
|
||||
"# If you'd like to use LangSmith, uncomment the below:\n",
|
||||
"# os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
|
||||
"# os.environ[\"LANGCHAIN_API_KEY\"] = getpass.getpass()"
|
||||
@@ -62,33 +60,57 @@
|
||||
"source": [
|
||||
"## Chain\n",
|
||||
"\n",
|
||||
"Suppose we have the following (dummy) tools and tool-calling chain:"
|
||||
"Suppose we have the following (dummy) tools and tool-calling chain:\n",
|
||||
"\n",
|
||||
"```{=mdx}\n",
|
||||
"import ChatModelTabs from \"@theme/ChatModelTabs\";\n",
|
||||
"\n",
|
||||
"<ChatModelTabs customVarName=\"llm\"/>\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "e0ff02ac-e750-493b-9b09-4578711a6726",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# | echo: false\n",
|
||||
"# | outout: false\n",
|
||||
"\n",
|
||||
"from langchain_anthropic import ChatAnthropic\n",
|
||||
"\n",
|
||||
"llm = ChatAnthropic(model=\"claude-3-sonnet-20240229\", temperature=0)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "0221fdfd-2a18-4449-a123-e6b0b15bb3d9",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[{'type': 'count_emails', 'args': {'last_n_days': 5}, 'output': 10}]"
|
||||
"[{'name': 'count_emails',\n",
|
||||
" 'args': {'last_n_days': 5},\n",
|
||||
" 'id': 'toolu_012VHuh7vk5dVNct5SgZj3gh',\n",
|
||||
" 'output': 10}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 2,
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from operator import itemgetter\n",
|
||||
"from typing import Dict, List\n",
|
||||
"\n",
|
||||
"from langchain.output_parsers import JsonOutputToolsParser\n",
|
||||
"from langchain_core.runnables import Runnable, RunnableLambda, RunnablePassthrough\n",
|
||||
"from langchain_core.messages import AIMessage\n",
|
||||
"from langchain_core.runnables import Runnable, RunnablePassthrough\n",
|
||||
"from langchain_core.tools import tool\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"@tool\n",
|
||||
@@ -104,19 +126,19 @@
|
||||
"\n",
|
||||
"\n",
|
||||
"tools = [count_emails, send_email]\n",
|
||||
"model = ChatOpenAI(model=\"gpt-3.5-turbo\", temperature=0).bind_tools(tools)\n",
|
||||
"llm_with_tools = llm.bind_tools(tools)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def call_tool(tool_invocation: dict) -> Runnable:\n",
|
||||
" \"\"\"Function for dynamically constructing the end of the chain based on the model-selected tool.\"\"\"\n",
|
||||
"def call_tools(msg: AIMessage) -> List[Dict]:\n",
|
||||
" \"\"\"Simple sequential tool calling helper.\"\"\"\n",
|
||||
" tool_map = {tool.name: tool for tool in tools}\n",
|
||||
" tool = tool_map[tool_invocation[\"type\"]]\n",
|
||||
" return RunnablePassthrough.assign(output=itemgetter(\"args\") | tool)\n",
|
||||
" tool_calls = msg.tool_calls.copy()\n",
|
||||
" for tool_call in tool_calls:\n",
|
||||
" tool_call[\"output\"] = tool_map[tool_call[\"name\"]].invoke(tool_call[\"args\"])\n",
|
||||
" return tool_calls\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# .map() allows us to apply a function to a list of inputs.\n",
|
||||
"call_tool_list = RunnableLambda(call_tool).map()\n",
|
||||
"chain = model | JsonOutputToolsParser() | call_tool_list\n",
|
||||
"chain = llm_with_tools | call_tools\n",
|
||||
"chain.invoke(\"how many emails did i get in the last 5 days?\")"
|
||||
]
|
||||
},
|
||||
@@ -132,7 +154,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 30,
|
||||
"execution_count": 9,
|
||||
"id": "341fb055-0315-47bc-8f72-ed6103d2981f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -140,23 +162,23 @@
|
||||
"import json\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def human_approval(tool_invocations: list) -> Runnable:\n",
|
||||
"def human_approval(msg: AIMessage) -> Runnable:\n",
|
||||
" tool_strs = \"\\n\\n\".join(\n",
|
||||
" json.dumps(tool_call, indent=2) for tool_call in tool_invocations\n",
|
||||
" json.dumps(tool_call, indent=2) for tool_call in msg.tool_calls\n",
|
||||
" )\n",
|
||||
" msg = (\n",
|
||||
" input_msg = (\n",
|
||||
" f\"Do you approve of the following tool invocations\\n\\n{tool_strs}\\n\\n\"\n",
|
||||
" \"Anything except 'Y'/'Yes' (case-insensitive) will be treated as a no.\"\n",
|
||||
" )\n",
|
||||
" resp = input(msg)\n",
|
||||
" resp = input(input_msg)\n",
|
||||
" if resp.lower() not in (\"yes\", \"y\"):\n",
|
||||
" raise ValueError(f\"Tool invocations not approved:\\n\\n{tool_strs}\")\n",
|
||||
" return tool_invocations"
|
||||
" return msg"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 31,
|
||||
"execution_count": 10,
|
||||
"id": "25dca07b-56ca-4b94-9955-d4f3e9895e03",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -167,34 +189,38 @@
|
||||
"Do you approve of the following tool invocations\n",
|
||||
"\n",
|
||||
"{\n",
|
||||
" \"type\": \"count_emails\",\n",
|
||||
" \"name\": \"count_emails\",\n",
|
||||
" \"args\": {\n",
|
||||
" \"last_n_days\": 5\n",
|
||||
" }\n",
|
||||
" },\n",
|
||||
" \"id\": \"toolu_01LCpjpFxrRspygDscnHYyPm\"\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"Anything except 'Y'/'Yes' (case-insensitive) will be treated as a no. y\n"
|
||||
"Anything except 'Y'/'Yes' (case-insensitive) will be treated as a no. yes\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[{'type': 'count_emails', 'args': {'last_n_days': 5}, 'output': 10}]"
|
||||
"[{'name': 'count_emails',\n",
|
||||
" 'args': {'last_n_days': 5},\n",
|
||||
" 'id': 'toolu_01LCpjpFxrRspygDscnHYyPm',\n",
|
||||
" 'output': 10}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 31,
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"chain = model | JsonOutputToolsParser() | human_approval | call_tool_list\n",
|
||||
"chain = llm_with_tools | human_approval | call_tools\n",
|
||||
"chain.invoke(\"how many emails did i get in the last 5 days?\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 32,
|
||||
"execution_count": 11,
|
||||
"id": "f558f2cd-847b-4ef9-a770-3961082b540c",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -205,11 +231,12 @@
|
||||
"Do you approve of the following tool invocations\n",
|
||||
"\n",
|
||||
"{\n",
|
||||
" \"type\": \"send_email\",\n",
|
||||
" \"name\": \"send_email\",\n",
|
||||
" \"args\": {\n",
|
||||
" \"message\": \"What's up homie\",\n",
|
||||
" \"recipient\": \"sally@gmail.com\"\n",
|
||||
" }\n",
|
||||
" },\n",
|
||||
" \"id\": \"toolu_0158qJVd1AL32Y1xxYUAtNEy\"\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"Anything except 'Y'/'Yes' (case-insensitive) will be treated as a no. no\n"
|
||||
@@ -217,20 +244,20 @@
|
||||
},
|
||||
{
|
||||
"ename": "ValueError",
|
||||
"evalue": "Tool invocations not approved:\n\n{\n \"type\": \"send_email\",\n \"args\": {\n \"message\": \"What's up homie\",\n \"recipient\": \"sally@gmail.com\"\n }\n}",
|
||||
"evalue": "Tool invocations not approved:\n\n{\n \"name\": \"send_email\",\n \"args\": {\n \"message\": \"What's up homie\",\n \"recipient\": \"sally@gmail.com\"\n },\n \"id\": \"toolu_0158qJVd1AL32Y1xxYUAtNEy\"\n}",
|
||||
"output_type": "error",
|
||||
"traceback": [
|
||||
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
|
||||
"\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)",
|
||||
"Cell \u001b[0;32mIn[32], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mchain\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mSend sally@gmail.com an email saying \u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mWhat\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43ms up homie\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n",
|
||||
"File \u001b[0;32m~/langchain/libs/core/langchain_core/runnables/base.py:1774\u001b[0m, in \u001b[0;36mRunnableSequence.invoke\u001b[0;34m(self, input, config)\u001b[0m\n\u001b[1;32m 1772\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 1773\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i, step \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39msteps):\n\u001b[0;32m-> 1774\u001b[0m \u001b[38;5;28minput\u001b[39m \u001b[38;5;241m=\u001b[39m \u001b[43mstep\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 1775\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1776\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;66;43;03m# mark each step as a child run\u001b[39;49;00m\n\u001b[1;32m 1777\u001b[0m \u001b[43m \u001b[49m\u001b[43mpatch_config\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 1778\u001b[0m \u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcallbacks\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrun_manager\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget_child\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43mf\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mseq:step:\u001b[39;49m\u001b[38;5;132;43;01m{\u001b[39;49;00m\u001b[43mi\u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[38;5;132;43;01m}\u001b[39;49;00m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1779\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1780\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1781\u001b[0m \u001b[38;5;66;03m# finish the root run\u001b[39;00m\n\u001b[1;32m 1782\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n",
|
||||
"File \u001b[0;32m~/langchain/libs/core/langchain_core/runnables/base.py:3074\u001b[0m, in \u001b[0;36mRunnableLambda.invoke\u001b[0;34m(self, input, config, **kwargs)\u001b[0m\n\u001b[1;32m 3072\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"Invoke this runnable synchronously.\"\"\"\u001b[39;00m\n\u001b[1;32m 3073\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mhasattr\u001b[39m(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mfunc\u001b[39m\u001b[38;5;124m\"\u001b[39m):\n\u001b[0;32m-> 3074\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_call_with_config\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 3075\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_invoke\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 3076\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 3077\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_config\u001b[49m\u001b[43m(\u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfunc\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 3078\u001b[0m \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[1;32m 3079\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 3080\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 3081\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\n\u001b[1;32m 3082\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mCannot invoke a coroutine function synchronously.\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 3083\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mUse `ainvoke` instead.\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 3084\u001b[0m )\n",
|
||||
"File \u001b[0;32m~/langchain/libs/core/langchain_core/runnables/base.py:975\u001b[0m, in \u001b[0;36mRunnable._call_with_config\u001b[0;34m(self, func, input, config, run_type, **kwargs)\u001b[0m\n\u001b[1;32m 971\u001b[0m context \u001b[38;5;241m=\u001b[39m copy_context()\n\u001b[1;32m 972\u001b[0m context\u001b[38;5;241m.\u001b[39mrun(var_child_runnable_config\u001b[38;5;241m.\u001b[39mset, child_config)\n\u001b[1;32m 973\u001b[0m output \u001b[38;5;241m=\u001b[39m cast(\n\u001b[1;32m 974\u001b[0m Output,\n\u001b[0;32m--> 975\u001b[0m \u001b[43mcontext\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 976\u001b[0m \u001b[43m \u001b[49m\u001b[43mcall_func_with_variable_args\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 977\u001b[0m \u001b[43m \u001b[49m\u001b[43mfunc\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;66;43;03m# type: ignore[arg-type]\u001b[39;49;00m\n\u001b[1;32m 978\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;66;43;03m# type: ignore[arg-type]\u001b[39;49;00m\n\u001b[1;32m 979\u001b[0m \u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 980\u001b[0m \u001b[43m \u001b[49m\u001b[43mrun_manager\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 981\u001b[0m \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[1;32m 982\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m,\n\u001b[1;32m 983\u001b[0m )\n\u001b[1;32m 984\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[1;32m 985\u001b[0m run_manager\u001b[38;5;241m.\u001b[39mon_chain_error(e)\n",
|
||||
"File \u001b[0;32m~/langchain/libs/core/langchain_core/runnables/config.py:323\u001b[0m, in \u001b[0;36mcall_func_with_variable_args\u001b[0;34m(func, input, config, run_manager, **kwargs)\u001b[0m\n\u001b[1;32m 321\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m run_manager \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m accepts_run_manager(func):\n\u001b[1;32m 322\u001b[0m kwargs[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mrun_manager\u001b[39m\u001b[38;5;124m\"\u001b[39m] \u001b[38;5;241m=\u001b[39m run_manager\n\u001b[0;32m--> 323\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43minput\u001b[39;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",
|
||||
"File \u001b[0;32m~/langchain/libs/core/langchain_core/runnables/base.py:2950\u001b[0m, in \u001b[0;36mRunnableLambda._invoke\u001b[0;34m(self, input, run_manager, config, **kwargs)\u001b[0m\n\u001b[1;32m 2948\u001b[0m output \u001b[38;5;241m=\u001b[39m chunk\n\u001b[1;32m 2949\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m-> 2950\u001b[0m output \u001b[38;5;241m=\u001b[39m \u001b[43mcall_func_with_variable_args\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 2951\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfunc\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrun_manager\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\n\u001b[1;32m 2952\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 2953\u001b[0m \u001b[38;5;66;03m# If the output is a runnable, invoke it\u001b[39;00m\n\u001b[1;32m 2954\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(output, Runnable):\n",
|
||||
"File \u001b[0;32m~/langchain/libs/core/langchain_core/runnables/config.py:323\u001b[0m, in \u001b[0;36mcall_func_with_variable_args\u001b[0;34m(func, input, config, run_manager, **kwargs)\u001b[0m\n\u001b[1;32m 321\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m run_manager \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m accepts_run_manager(func):\n\u001b[1;32m 322\u001b[0m kwargs[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mrun_manager\u001b[39m\u001b[38;5;124m\"\u001b[39m] \u001b[38;5;241m=\u001b[39m run_manager\n\u001b[0;32m--> 323\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43minput\u001b[39;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",
|
||||
"Cell \u001b[0;32mIn[30], line 11\u001b[0m, in \u001b[0;36mhuman_approval\u001b[0;34m(tool_invocations)\u001b[0m\n\u001b[1;32m 9\u001b[0m resp \u001b[38;5;241m=\u001b[39m \u001b[38;5;28minput\u001b[39m(msg)\n\u001b[1;32m 10\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m resp\u001b[38;5;241m.\u001b[39mlower() \u001b[38;5;129;01min\u001b[39;00m (\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124myes\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124my\u001b[39m\u001b[38;5;124m\"\u001b[39m):\n\u001b[0;32m---> 11\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mTool invocations not approved:\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;132;01m{\u001b[39;00mtool_strs\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 12\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m tool_invocations\n",
|
||||
"\u001b[0;31mValueError\u001b[0m: Tool invocations not approved:\n\n{\n \"type\": \"send_email\",\n \"args\": {\n \"message\": \"What's up homie\",\n \"recipient\": \"sally@gmail.com\"\n }\n}"
|
||||
"Cell \u001b[0;32mIn[11], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mchain\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mSend sally@gmail.com an email saying \u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mWhat\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43ms up homie\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n",
|
||||
"File \u001b[0;32m~/langchain/libs/core/langchain_core/runnables/base.py:2499\u001b[0m, in \u001b[0;36mRunnableSequence.invoke\u001b[0;34m(self, input, config)\u001b[0m\n\u001b[1;32m 2497\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 2498\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i, step \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39msteps):\n\u001b[0;32m-> 2499\u001b[0m \u001b[38;5;28minput\u001b[39m \u001b[38;5;241m=\u001b[39m \u001b[43mstep\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 2500\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 2501\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;66;43;03m# mark each step as a child run\u001b[39;49;00m\n\u001b[1;32m 2502\u001b[0m \u001b[43m \u001b[49m\u001b[43mpatch_config\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 2503\u001b[0m \u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcallbacks\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrun_manager\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget_child\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43mf\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mseq:step:\u001b[39;49m\u001b[38;5;132;43;01m{\u001b[39;49;00m\u001b[43mi\u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[38;5;132;43;01m}\u001b[39;49;00m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[1;32m 2504\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 2505\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 2506\u001b[0m \u001b[38;5;66;03m# finish the root run\u001b[39;00m\n\u001b[1;32m 2507\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n",
|
||||
"File \u001b[0;32m~/langchain/libs/core/langchain_core/runnables/base.py:3961\u001b[0m, in \u001b[0;36mRunnableLambda.invoke\u001b[0;34m(self, input, config, **kwargs)\u001b[0m\n\u001b[1;32m 3959\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"Invoke this runnable synchronously.\"\"\"\u001b[39;00m\n\u001b[1;32m 3960\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mhasattr\u001b[39m(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mfunc\u001b[39m\u001b[38;5;124m\"\u001b[39m):\n\u001b[0;32m-> 3961\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_call_with_config\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 3962\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_invoke\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 3963\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 3964\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_config\u001b[49m\u001b[43m(\u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfunc\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 3965\u001b[0m \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[1;32m 3966\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 3967\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 3968\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\n\u001b[1;32m 3969\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mCannot invoke a coroutine function synchronously.\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 3970\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mUse `ainvoke` instead.\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 3971\u001b[0m )\n",
|
||||
"File \u001b[0;32m~/langchain/libs/core/langchain_core/runnables/base.py:1625\u001b[0m, in \u001b[0;36mRunnable._call_with_config\u001b[0;34m(self, func, input, config, run_type, **kwargs)\u001b[0m\n\u001b[1;32m 1621\u001b[0m context \u001b[38;5;241m=\u001b[39m copy_context()\n\u001b[1;32m 1622\u001b[0m context\u001b[38;5;241m.\u001b[39mrun(var_child_runnable_config\u001b[38;5;241m.\u001b[39mset, child_config)\n\u001b[1;32m 1623\u001b[0m output \u001b[38;5;241m=\u001b[39m cast(\n\u001b[1;32m 1624\u001b[0m Output,\n\u001b[0;32m-> 1625\u001b[0m \u001b[43mcontext\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 1626\u001b[0m \u001b[43m \u001b[49m\u001b[43mcall_func_with_variable_args\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;66;43;03m# type: ignore[arg-type]\u001b[39;49;00m\n\u001b[1;32m 1627\u001b[0m \u001b[43m \u001b[49m\u001b[43mfunc\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;66;43;03m# type: ignore[arg-type]\u001b[39;49;00m\n\u001b[1;32m 1628\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;66;43;03m# type: ignore[arg-type]\u001b[39;49;00m\n\u001b[1;32m 1629\u001b[0m \u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1630\u001b[0m \u001b[43m \u001b[49m\u001b[43mrun_manager\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1631\u001b[0m \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[1;32m 1632\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m,\n\u001b[1;32m 1633\u001b[0m )\n\u001b[1;32m 1634\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[1;32m 1635\u001b[0m run_manager\u001b[38;5;241m.\u001b[39mon_chain_error(e)\n",
|
||||
"File \u001b[0;32m~/langchain/libs/core/langchain_core/runnables/config.py:347\u001b[0m, in \u001b[0;36mcall_func_with_variable_args\u001b[0;34m(func, input, config, run_manager, **kwargs)\u001b[0m\n\u001b[1;32m 345\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m run_manager \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m accepts_run_manager(func):\n\u001b[1;32m 346\u001b[0m kwargs[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mrun_manager\u001b[39m\u001b[38;5;124m\"\u001b[39m] \u001b[38;5;241m=\u001b[39m run_manager\n\u001b[0;32m--> 347\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43minput\u001b[39;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",
|
||||
"File \u001b[0;32m~/langchain/libs/core/langchain_core/runnables/base.py:3835\u001b[0m, in \u001b[0;36mRunnableLambda._invoke\u001b[0;34m(self, input, run_manager, config, **kwargs)\u001b[0m\n\u001b[1;32m 3833\u001b[0m output \u001b[38;5;241m=\u001b[39m chunk\n\u001b[1;32m 3834\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m-> 3835\u001b[0m output \u001b[38;5;241m=\u001b[39m \u001b[43mcall_func_with_variable_args\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 3836\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfunc\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrun_manager\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\n\u001b[1;32m 3837\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 3838\u001b[0m \u001b[38;5;66;03m# If the output is a runnable, invoke it\u001b[39;00m\n\u001b[1;32m 3839\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(output, Runnable):\n",
|
||||
"File \u001b[0;32m~/langchain/libs/core/langchain_core/runnables/config.py:347\u001b[0m, in \u001b[0;36mcall_func_with_variable_args\u001b[0;34m(func, input, config, run_manager, **kwargs)\u001b[0m\n\u001b[1;32m 345\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m run_manager \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m accepts_run_manager(func):\n\u001b[1;32m 346\u001b[0m kwargs[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mrun_manager\u001b[39m\u001b[38;5;124m\"\u001b[39m] \u001b[38;5;241m=\u001b[39m run_manager\n\u001b[0;32m--> 347\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43minput\u001b[39;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",
|
||||
"Cell \u001b[0;32mIn[9], line 14\u001b[0m, in \u001b[0;36mhuman_approval\u001b[0;34m(msg)\u001b[0m\n\u001b[1;32m 12\u001b[0m resp \u001b[38;5;241m=\u001b[39m \u001b[38;5;28minput\u001b[39m(input_msg)\n\u001b[1;32m 13\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m resp\u001b[38;5;241m.\u001b[39mlower() \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m (\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124myes\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124my\u001b[39m\u001b[38;5;124m\"\u001b[39m):\n\u001b[0;32m---> 14\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mTool invocations not approved:\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;132;01m{\u001b[39;00mtool_strs\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 15\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m msg\n",
|
||||
"\u001b[0;31mValueError\u001b[0m: Tool invocations not approved:\n\n{\n \"name\": \"send_email\",\n \"args\": {\n \"message\": \"What's up homie\",\n \"recipient\": \"sally@gmail.com\"\n },\n \"id\": \"toolu_0158qJVd1AL32Y1xxYUAtNEy\"\n}"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -249,9 +276,9 @@
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "poetry-venv",
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "poetry-venv"
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install --upgrade --quiet langchain langchain-openai"
|
||||
"%pip install --upgrade --quiet langchain-core"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -45,12 +45,12 @@
|
||||
"id": "59d08fd0-ddd9-4c74-bcea-a5ca3a86e542",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"And set these environment variables:"
|
||||
"If you'd like to trace your runs in [LangSmith](/docs/langsmith/) uncomment and set the following environment variables:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 1,
|
||||
"id": "4185e74b-0500-4cad-ace0-bac37de466ac",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -58,9 +58,6 @@
|
||||
"import getpass\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"os.environ[\"OPENAI_API_KEY\"] = getpass.getpass()\n",
|
||||
"\n",
|
||||
"# If you'd like to use LangSmith, uncomment the below\n",
|
||||
"# os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
|
||||
"# os.environ[\"LANGCHAIN_API_KEY\"] = getpass.getpass()"
|
||||
]
|
||||
@@ -77,7 +74,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"execution_count": 5,
|
||||
"id": "e13ec98c-8521-4d63-b521-caf92da87b70",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -96,12 +93,12 @@
|
||||
"id": "3de233af-b3bd-4f0c-8b1a-83527143a8db",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"And now we can add to it a `exponentiate` and `add` tool:"
|
||||
"And now we can add to it an `exponentiate` and `add` tool:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"execution_count": 6,
|
||||
"id": "e93661cd-a2ba-4ada-91ad-baf1b60879ec",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -123,60 +120,78 @@
|
||||
"id": "bbea4555-ed10-4a18-b802-e9a3071f132b",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"The main difference between using one Tool and many, is that in the case of many we can't be sure which Tool the model will invoke. So we cannot hardcode, like we did in the [Quickstart](/docs/use_cases/tool_use/quickstart), a specific tool into our chain. Instead we'll add `call_tool_list`, a `RunnableLambda` that takes the `JsonOutputToolsParser` output and actually builds the end of the chain based on it, meaning it appends the Tools that were envoked to the end of the chain at runtime. We can do this because LCEL has the cool property that in any Runnable (the core building block of LCEL) sequence, if one component returns more Runnables, those are run as part of the chain."
|
||||
"The main difference between using one Tool and many is that we can't be sure which Tool the model will invoke upfront, so we cannot hardcode, like we did in the [Quickstart](/docs/use_cases/tool_use/quickstart), a specific tool into our chain. Instead we'll add `call_tools`, a `RunnableLambda` that takes the output AI message with tools calls and routes to the correct tools.\n",
|
||||
"\n",
|
||||
"```{=mdx}\n",
|
||||
"import ChatModelTabs from \"@theme/ChatModelTabs\";\n",
|
||||
"\n",
|
||||
"<ChatModelTabs customVarName=\"llm\"/>\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"execution_count": 7,
|
||||
"id": "f00f0f3f-8530-4c1d-a26c-d20824e31faf",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_anthropic import ChatAnthropic\n",
|
||||
"\n",
|
||||
"llm = ChatAnthropic(model=\"claude-3-sonnet-20240229\", temperature=0)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"id": "c35359ae-a740-48c5-b5e7-1a377fb25aa2",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from operator import itemgetter\n",
|
||||
"from typing import Union\n",
|
||||
"from typing import Dict, List, Union\n",
|
||||
"\n",
|
||||
"from langchain.output_parsers import JsonOutputToolsParser\n",
|
||||
"from langchain_core.messages import AIMessage\n",
|
||||
"from langchain_core.runnables import (\n",
|
||||
" Runnable,\n",
|
||||
" RunnableLambda,\n",
|
||||
" RunnableMap,\n",
|
||||
" RunnablePassthrough,\n",
|
||||
")\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"\n",
|
||||
"model = ChatOpenAI(model=\"gpt-3.5-turbo\")\n",
|
||||
"tools = [multiply, exponentiate, add]\n",
|
||||
"model_with_tools = model.bind_tools(tools)\n",
|
||||
"llm_with_tools = llm.bind_tools(tools)\n",
|
||||
"tool_map = {tool.name: tool for tool in tools}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def call_tool(tool_invocation: dict) -> Union[str, Runnable]:\n",
|
||||
" \"\"\"Function for dynamically constructing the end of the chain based on the model-selected tool.\"\"\"\n",
|
||||
" tool = tool_map[tool_invocation[\"type\"]]\n",
|
||||
" return RunnablePassthrough.assign(output=itemgetter(\"args\") | tool)\n",
|
||||
"def call_tools(msg: AIMessage) -> Runnable:\n",
|
||||
" \"\"\"Simple sequential tool calling helper.\"\"\"\n",
|
||||
" tool_map = {tool.name: tool for tool in tools}\n",
|
||||
" tool_calls = msg.tool_calls.copy()\n",
|
||||
" for tool_call in tool_calls:\n",
|
||||
" tool_call[\"output\"] = tool_map[tool_call[\"name\"]].invoke(tool_call[\"args\"])\n",
|
||||
" return tool_calls\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# .map() allows us to apply a function to a list of inputs.\n",
|
||||
"call_tool_list = RunnableLambda(call_tool).map()\n",
|
||||
"chain = model_with_tools | JsonOutputToolsParser() | call_tool_list"
|
||||
"chain = llm_with_tools | call_tools"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"execution_count": 12,
|
||||
"id": "ea6dbb32-ec9b-4c70-a90f-a2db93978cf1",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[{'type': 'multiply',\n",
|
||||
"[{'name': 'multiply',\n",
|
||||
" 'args': {'first_int': 23, 'second_int': 7},\n",
|
||||
" 'id': 'toolu_01Wf8kUs36kxRKLDL8vs7G8q',\n",
|
||||
" 'output': 161}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 14,
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -187,19 +202,20 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 15,
|
||||
"execution_count": 13,
|
||||
"id": "b1c6c0f8-6d04-40d4-a40e-8719ca7b27c2",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[{'type': 'add',\n",
|
||||
"[{'name': 'add',\n",
|
||||
" 'args': {'first_int': 1000000, 'second_int': 1000000000},\n",
|
||||
" 'id': 'toolu_012aK4xZBQg2sXARsFZnqxHh',\n",
|
||||
" 'output': 1001000000}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 15,
|
||||
"execution_count": 13,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -210,19 +226,20 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 16,
|
||||
"execution_count": 14,
|
||||
"id": "ce76f299-1a4d-421c-afa4-a6346e34285c",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[{'type': 'exponentiate',\n",
|
||||
"[{'name': 'exponentiate',\n",
|
||||
" 'args': {'base': 37, 'exponent': 3},\n",
|
||||
" 'id': 'toolu_01VDU6X3ugDb9cpnnmCZFPbC',\n",
|
||||
" 'output': 50653}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 16,
|
||||
"execution_count": 14,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -234,9 +251,9 @@
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "poetry-venv",
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "poetry-venv"
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"source": [
|
||||
"# Parallel tool use\n",
|
||||
"\n",
|
||||
"In the [Chains with multiple tools](/docs/use_cases/tool_use/multiple_tools) guide we saw how to build function-calling chains that select between multiple tools. Some models, like the OpenAI models released in Fall 2023, also support parallel function calling, which allows you to invoke multiple functions (or the same function multiple times) in a single model call. Our previous chain from the multiple tools guides actually already supports this, we just need to use an OpenAI model capable of parallel function calling."
|
||||
"In the [Chains with multiple tools](/docs/use_cases/tool_use/multiple_tools) guide we saw how to build function-calling chains that select between multiple tools. Some models, like the OpenAI models released in Fall 2023, also support parallel function calling, which allows you to invoke multiple functions (or the same function multiple times) in a single model call. Our previous chain from the multiple tools guides actually already supports this."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -27,7 +27,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install --upgrade --quiet langchain langchain-openai"
|
||||
"%pip install --upgrade --quiet langchain-core"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -35,7 +35,7 @@
|
||||
"id": "59d08fd0-ddd9-4c74-bcea-a5ca3a86e542",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"And set these environment variables:"
|
||||
"If you'd like to trace your runs in [LangSmith](/docs/langsmith/) uncomment and set the following environment variables:"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -48,9 +48,6 @@
|
||||
"import getpass\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"os.environ[\"OPENAI_API_KEY\"] = getpass.getpass()\n",
|
||||
"\n",
|
||||
"# If you'd like to use LangSmith, uncomment the below\n",
|
||||
"# os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
|
||||
"# os.environ[\"LANGCHAIN_API_KEY\"] = getpass.getpass()"
|
||||
]
|
||||
@@ -65,7 +62,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"execution_count": 3,
|
||||
"id": "e13ec98c-8521-4d63-b521-caf92da87b70",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -98,67 +95,91 @@
|
||||
"source": [
|
||||
"# Chain\n",
|
||||
"\n",
|
||||
"Notice we use an `-1106` model, which as of this writing is the only kind that supports parallel function calling:"
|
||||
"```{=mdx}\n",
|
||||
"import ChatModelTabs from \"@theme/ChatModelTabs\";\n",
|
||||
"\n",
|
||||
"<ChatModelTabs customVarName=\"llm\" hideGoogle=\"true\"/>\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"execution_count": 7,
|
||||
"id": "f67d91d8-cc38-4065-8f80-901e079954dd",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# | echo: false\n",
|
||||
"# | output: false\n",
|
||||
"\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"\n",
|
||||
"llm = ChatOpenAI(model=\"gpt-3.5-turbo-0125\", temperature=0)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "c35359ae-a740-48c5-b5e7-1a377fb25aa2",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from operator import itemgetter\n",
|
||||
"from typing import Union\n",
|
||||
"from typing import Dict, List, Union\n",
|
||||
"\n",
|
||||
"from langchain.output_parsers import JsonOutputToolsParser\n",
|
||||
"from langchain_core.messages import AIMessage\n",
|
||||
"from langchain_core.runnables import (\n",
|
||||
" Runnable,\n",
|
||||
" RunnableLambda,\n",
|
||||
" RunnableMap,\n",
|
||||
" RunnablePassthrough,\n",
|
||||
")\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"\n",
|
||||
"model = ChatOpenAI(model=\"gpt-3.5-turbo-1106\")\n",
|
||||
"tools = [multiply, exponentiate, add]\n",
|
||||
"model_with_tools = model.bind_tools(tools)\n",
|
||||
"llm_with_tools = llm.bind_tools(tools)\n",
|
||||
"tool_map = {tool.name: tool for tool in tools}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def call_tool(tool_invocation: dict) -> Union[str, Runnable]:\n",
|
||||
" \"\"\"Function for dynamically constructing the end of the chain based on the model-selected tool.\"\"\"\n",
|
||||
" tool = tool_map[tool_invocation[\"type\"]]\n",
|
||||
" return RunnablePassthrough.assign(output=itemgetter(\"args\") | tool)\n",
|
||||
"def call_tools(msg: AIMessage) -> Runnable:\n",
|
||||
" \"\"\"Simple sequential tool calling helper.\"\"\"\n",
|
||||
" tool_map = {tool.name: tool for tool in tools}\n",
|
||||
" tool_calls = msg.tool_calls.copy()\n",
|
||||
" for tool_call in tool_calls:\n",
|
||||
" tool_call[\"output\"] = tool_map[tool_call[\"name\"]].invoke(tool_call[\"args\"])\n",
|
||||
" return tool_calls\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# .map() allows us to apply a function to a list of inputs.\n",
|
||||
"call_tool_list = RunnableLambda(call_tool).map()\n",
|
||||
"chain = model_with_tools | JsonOutputToolsParser() | call_tool_list"
|
||||
"chain = llm_with_tools | call_tools"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"execution_count": 9,
|
||||
"id": "ea6dbb32-ec9b-4c70-a90f-a2db93978cf1",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[{'type': 'multiply',\n",
|
||||
"[{'name': 'multiply',\n",
|
||||
" 'args': {'first_int': 23, 'second_int': 7},\n",
|
||||
" 'id': 'call_22tgOrsVLyLMsl2RLbUhtycw',\n",
|
||||
" 'output': 161},\n",
|
||||
" {'type': 'add', 'args': {'first_int': 5, 'second_int': 18}, 'output': 23},\n",
|
||||
" {'type': 'add',\n",
|
||||
" {'name': 'multiply',\n",
|
||||
" 'args': {'first_int': 5, 'second_int': 18},\n",
|
||||
" 'id': 'call_EbKHEG3TjqBhEwb7aoxUtgzf',\n",
|
||||
" 'output': 90},\n",
|
||||
" {'name': 'add',\n",
|
||||
" 'args': {'first_int': 1000000, 'second_int': 1000000000},\n",
|
||||
" 'id': 'call_LUhu2IT3vINxlTc5fCVY6Nhi',\n",
|
||||
" 'output': 1001000000},\n",
|
||||
" {'type': 'exponentiate',\n",
|
||||
" {'name': 'exponentiate',\n",
|
||||
" 'args': {'base': 37, 'exponent': 3},\n",
|
||||
" 'id': 'call_bnCZIXelOKkmcyd4uGXId9Ct',\n",
|
||||
" 'output': 50653}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 12,
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -172,9 +193,9 @@
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "poetry-venv",
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "poetry-venv"
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
"id": "14b94240",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Tool use without function calling\n",
|
||||
"# Using models that don't support tool calling\n",
|
||||
"\n",
|
||||
"In this guide we'll build a Chain that does not rely on any special model APIs (like function-calling, which we showed in the [Quickstart](/docs/use_cases/tool_use/quickstart)) and instead just prompts the model directly to invoke tools."
|
||||
"In this guide we'll build a Chain that does not rely on any special model APIs (like tool calling, which we showed in the [Quickstart](/docs/use_cases/tool_use/quickstart)) and instead just prompts the model directly to invoke tools."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -393,9 +393,9 @@
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "poetry-venv",
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "poetry-venv"
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install --upgrade --quiet langchain langchain-openai"
|
||||
"%pip install --upgrade --quiet langchain"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -45,7 +45,7 @@
|
||||
"id": "36a9c6fc-8264-462f-b8d7-9c7bbec22ef9",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"And set these environment variables:"
|
||||
"If you'd like to trace your runs in [LangSmith](/docs/langsmith/) uncomment and set the following environment variables:"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -58,9 +58,6 @@
|
||||
"import getpass\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"os.environ[\"OPENAI_API_KEY\"] = getpass.getpass()\n",
|
||||
"\n",
|
||||
"# If you'd like to use LangSmith, uncomment the below\n",
|
||||
"# os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
|
||||
"# os.environ[\"LANGCHAIN_API_KEY\"] = getpass.getpass()"
|
||||
]
|
||||
@@ -77,7 +74,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"execution_count": 6,
|
||||
"id": "90187d07",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -145,22 +142,31 @@
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"### Function calling\n",
|
||||
"One of the most reliable ways to use tools with LLMs is with function calling APIs (also sometimes called tool calling or parallel function calling). This only works with models that explicitly support function calling, like OpenAI models. To learn more head to the [function calling guide](/docs/modules/model_io/chat/function_calling).\n",
|
||||
"### Tool/function calling\n",
|
||||
"One of the most reliable ways to use tools with LLMs is with tool calling APIs (also sometimes called function calling). This only works with models that explicitly support tool calling. You can see which models support tool calling [here](/docs/integrations/chat/), and learn more about how to use tool calling in [this guide](/docs/modules/model_io/chat/function_calling).\n",
|
||||
"\n",
|
||||
"First we'll define our model and tools. We'll start with just a single tool, `multiply`."
|
||||
"First we'll define our model and tools. We'll start with just a single tool, `multiply`.\n",
|
||||
"\n",
|
||||
"```{=mdx}\n",
|
||||
"import ChatModelTabs from \"@theme/ChatModelTabs\";\n",
|
||||
"\n",
|
||||
"<ChatModelTabs customVarName=\"llm\"/>\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"execution_count": 7,
|
||||
"id": "9bce8935-1465-45ac-8a93-314222c753c4",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# | echo: false\n",
|
||||
"# | output: false\n",
|
||||
"\n",
|
||||
"from langchain_openai.chat_models import ChatOpenAI\n",
|
||||
"\n",
|
||||
"model = ChatOpenAI(model=\"gpt-3.5-turbo-1106\")"
|
||||
"llm = ChatOpenAI(model=\"gpt-3.5-turbo-0125\", temperature=0)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -168,131 +174,57 @@
|
||||
"id": "c22e6f0f-c5ad-4c0f-9514-e626704ea51c",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Next we'll convert our LangChain Tool to an OpenAI format JSONSchema function, and bind this as the `tools` argument to be passed to all ChatOpenAI calls. Since we only have a single Tool and in this initial chain we want to make sure it's always used, we'll also specify `tool_choice`. See the [OpenAI chat API reference](https://platform.openai.com/docs/api-reference/chat/create#chat-create-tool_choice) for more on these parameters:"
|
||||
"We'll use `bind_tools` to pass the definition of our tool in as part of each call to the model, so that the model can invoke the tool when appropriate:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"execution_count": 8,
|
||||
"id": "3bfe2cdc-7d72-457c-a9a1-5fa1e0bcde55",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model_with_tools = model.bind_tools([multiply], tool_choice=\"multiply\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "19f6285f-d8b1-432c-8c07-f7aee3fc0fa4",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[{'type': 'function',\n",
|
||||
" 'function': {'name': 'multiply',\n",
|
||||
" 'description': 'multiply(first_int: int, second_int: int) -> int - Multiply two integers together.',\n",
|
||||
" 'parameters': {'type': 'object',\n",
|
||||
" 'properties': {'first_int': {'type': 'integer'},\n",
|
||||
" 'second_int': {'type': 'integer'}},\n",
|
||||
" 'required': ['first_int', 'second_int']}}}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"model_with_tools.kwargs[\"tools\"]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "340c1b04-38cb-4467-83ca-8aa2b59176d8",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'type': 'function', 'function': {'name': 'multiply'}}"
|
||||
]
|
||||
},
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"model_with_tools.kwargs[\"tool_choice\"]"
|
||||
"llm_with_tools = llm.bind_tools([multiply])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "9fa2ba14-9a97-4960-a6c7-422edecdaf4b",
|
||||
"id": "07fc830e-a6d2-4fac-904b-b94072e64018",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Now we'll compose out tool-calling model with a `JsonOutputToolsParser`, a built-in LangChain output parser that converts an OpenAI function-calling response to a list of `{\"type\": \"TOOL_NAME\", \"args\": {...}}` dicts with the tools to invoke and arguments to invoke them with."
|
||||
"When the model invokes the tool, this will show up in the `AIMessage.tool_calls` attribute of the output:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "5518aba4-c44d-4896-9b63-fc9d56c245df",
|
||||
"execution_count": 9,
|
||||
"id": "68f30343-14ef-48f1-badd-b6a03977316d",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[{'type': 'multiply', 'args': {'first_int': 4, 'second_int': 23}}]"
|
||||
"[{'name': 'multiply',\n",
|
||||
" 'args': {'first_int': 5, 'second_int': 42},\n",
|
||||
" 'id': 'call_cCP9oA3tRz7HDrjFn1FdmDaG'}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 7,
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from langchain.output_parsers import JsonOutputToolsParser\n",
|
||||
"\n",
|
||||
"chain = model_with_tools | JsonOutputToolsParser()\n",
|
||||
"chain.invoke(\"What's four times 23\")"
|
||||
"msg = llm_with_tools.invoke(\"whats 5 times forty two\")\n",
|
||||
"msg.tool_calls"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "7f712d8d-0314-4d3d-b563-378b72fd8bb5",
|
||||
"id": "330015a3-a5a7-433a-826a-6277766f6c27",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Since we know we're always invoking the `multiply` tool, we can simplify our output a bit to return only the args for the `multiply` tool using the `JsonoutputKeyToolsParser`. To further simplify we'll specify `first_tool_only=True`, so that instead of a list of tool invocations our output parser returns only the first tool invocation."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "cfacfcdc-8a45-4c60-a175-7efe9534f83e",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'first_int': 4, 'second_int': 23}"
|
||||
]
|
||||
},
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from langchain.output_parsers import JsonOutputKeyToolsParser\n",
|
||||
"\n",
|
||||
"chain = model_with_tools | JsonOutputKeyToolsParser(\n",
|
||||
" key_name=\"multiply\", first_tool_only=True\n",
|
||||
")\n",
|
||||
"chain.invoke(\"What's four times 23\")"
|
||||
"Check out the [LangSmith trace here](https://smith.langchain.com/public/81ff0cbd-e05b-4720-bf61-2c9807edb708/r)."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -302,12 +234,12 @@
|
||||
"source": [
|
||||
"### Invoking the tool\n",
|
||||
"\n",
|
||||
"Great! We're able to generate tool invocations. But what if we want to actually call the tool? To do that we just need to pass them to the tool:"
|
||||
"Great! We're able to generate tool invocations. But what if we want to actually call the tool? To do so we'll need to pass the generated tool args to our tool. As a simple example we'll just extract the arguments of the first tool_call:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"execution_count": 12,
|
||||
"id": "4f5325ca-e5dc-4d1a-ba36-b085a029c90a",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -317,7 +249,7 @@
|
||||
"92"
|
||||
]
|
||||
},
|
||||
"execution_count": 10,
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -325,15 +257,18 @@
|
||||
"source": [
|
||||
"from operator import itemgetter\n",
|
||||
"\n",
|
||||
"# Note: the `.map()` at the end of `multiply` allows us to pass in a list of `multiply` arguments instead of a single one.\n",
|
||||
"chain = (\n",
|
||||
" model_with_tools\n",
|
||||
" | JsonOutputKeyToolsParser(key_name=\"multiply\", first_tool_only=True)\n",
|
||||
" | multiply\n",
|
||||
")\n",
|
||||
"chain = llm_with_tools | (lambda x: x.tool_calls[0][\"args\"]) | multiply\n",
|
||||
"chain.invoke(\"What's four times 23\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "79a9eb63-383d-4dd4-a162-08b4a52ef4d9",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Check out the [LangSmith trace here](https://smith.langchain.com/public/16bbabb9-fc9b-41e5-a33d-487c42df4f85/r)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "0521d3d5",
|
||||
@@ -345,47 +280,54 @@
|
||||
"\n",
|
||||
"LangChain comes with a number of built-in agents that are optimized for different use cases. Read about all the [agent types here](/docs/modules/agents/agent_types/).\n",
|
||||
"\n",
|
||||
"As an example, let's try out the OpenAI tools agent, which makes use of the new OpenAI tool-calling API (this is only available in the latest OpenAI models, and differs from function-calling in that the model can return multiple function invocations at once)\n",
|
||||
"We'll use the [tool calling agent](/docs/modules/agents/agent_types/tool_calling/), which is generally the most reliable kind and the recommended one for most use cases.\n",
|
||||
"\n",
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 86,
|
||||
"execution_count": 13,
|
||||
"id": "21723cf4-9421-4a8d-92a6-eeeb8f4367f1",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain import hub\n",
|
||||
"from langchain.agents import AgentExecutor, create_openai_tools_agent\n",
|
||||
"from langchain_openai import ChatOpenAI"
|
||||
"from langchain.agents import AgentExecutor, create_tool_calling_agent"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 88,
|
||||
"execution_count": 14,
|
||||
"id": "6be83879-9da3-4dd9-b147-a79f76affd7a",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[SystemMessagePromptTemplate(prompt=PromptTemplate(input_variables=[], template='You are a helpful assistant')),\n",
|
||||
" MessagesPlaceholder(variable_name='chat_history', optional=True),\n",
|
||||
" HumanMessagePromptTemplate(prompt=PromptTemplate(input_variables=['input'], template='{input}')),\n",
|
||||
" MessagesPlaceholder(variable_name='agent_scratchpad')]"
|
||||
]
|
||||
},
|
||||
"execution_count": 88,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"================================\u001b[1m System Message \u001b[0m================================\n",
|
||||
"\n",
|
||||
"You are a helpful assistant\n",
|
||||
"\n",
|
||||
"=============================\u001b[1m Messages Placeholder \u001b[0m=============================\n",
|
||||
"\n",
|
||||
"\u001b[33;1m\u001b[1;3m{chat_history}\u001b[0m\n",
|
||||
"\n",
|
||||
"================================\u001b[1m Human Message \u001b[0m=================================\n",
|
||||
"\n",
|
||||
"\u001b[33;1m\u001b[1;3m{input}\u001b[0m\n",
|
||||
"\n",
|
||||
"=============================\u001b[1m Messages Placeholder \u001b[0m=============================\n",
|
||||
"\n",
|
||||
"\u001b[33;1m\u001b[1;3m{agent_scratchpad}\u001b[0m\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Get the prompt to use - you can modify this!\n",
|
||||
"# Get the prompt to use - can be replaced with any prompt that includes variables \"agent_scratchpad\" and \"input\"!\n",
|
||||
"prompt = hub.pull(\"hwchase17/openai-tools-agent\")\n",
|
||||
"prompt.messages"
|
||||
"prompt.pretty_print()"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -398,7 +340,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"execution_count": 15,
|
||||
"id": "95c86d32-ee45-4c87-a28c-14eff19b49e9",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -420,22 +362,18 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 90,
|
||||
"execution_count": 16,
|
||||
"id": "17b09ac6-c9b7-4340-a8a0-3d3061f7888c",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Choose the LLM that will drive the agent\n",
|
||||
"# Only certain models support this\n",
|
||||
"model = ChatOpenAI(model=\"gpt-3.5-turbo-1106\", temperature=0)\n",
|
||||
"\n",
|
||||
"# Construct the OpenAI Tools agent\n",
|
||||
"agent = create_openai_tools_agent(model, tools, prompt)"
|
||||
"# Construct the tool calling agent\n",
|
||||
"agent = create_tool_calling_agent(llm, tools, prompt)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 91,
|
||||
"execution_count": 17,
|
||||
"id": "675091d2-cac9-45c4-a5d7-b760ee6c1986",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -454,7 +392,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 95,
|
||||
"execution_count": 18,
|
||||
"id": "f7dbb240-809e-4e41-8f63-1a4636e8e26d",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -478,10 +416,16 @@
|
||||
"\n",
|
||||
"\n",
|
||||
"\u001b[0m\u001b[36;1m\u001b[1;3m3645\u001b[0m\u001b[32;1m\u001b[1;3m\n",
|
||||
"Invoking: `exponentiate` with `{'base': 3645, 'exponent': 2}`\n",
|
||||
"Invoking: `exponentiate` with `{'base': 405, 'exponent': 2}`\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\u001b[0m\u001b[38;5;200m\u001b[1;3m13286025\u001b[0m\u001b[32;1m\u001b[1;3mThe result of raising 3 to the fifth power and multiplying that by the sum of twelve and three, then squaring the whole result is 13,286,025.\u001b[0m\n",
|
||||
"\u001b[0m\u001b[38;5;200m\u001b[1;3m164025\u001b[0m\u001b[32;1m\u001b[1;3mThe result of taking 3 to the fifth power is 243. \n",
|
||||
"\n",
|
||||
"The sum of twelve and three is 15. \n",
|
||||
"\n",
|
||||
"Multiplying 243 by 15 gives 3645. \n",
|
||||
"\n",
|
||||
"Finally, squaring 3645 gives 164025.\u001b[0m\n",
|
||||
"\n",
|
||||
"\u001b[1m> Finished chain.\u001b[0m\n"
|
||||
]
|
||||
@@ -490,10 +434,10 @@
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'input': 'Take 3 to the fifth power and multiply that by the sum of twelve and three, then square the whole result',\n",
|
||||
" 'output': 'The result of raising 3 to the fifth power and multiplying that by the sum of twelve and three, then squaring the whole result is 13,286,025.'}"
|
||||
" 'output': 'The result of taking 3 to the fifth power is 243. \\n\\nThe sum of twelve and three is 15. \\n\\nMultiplying 243 by 15 gives 3645. \\n\\nFinally, squaring 3645 gives 164025.'}"
|
||||
]
|
||||
},
|
||||
"execution_count": 95,
|
||||
"execution_count": 18,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -506,6 +450,14 @@
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "8fdb0ed9-1763-4778-a7d6-026578cd9585",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Check out the [LangSmith trace here](https://smith.langchain.com/public/eeeb27a4-a2f8-4f06-a3af-9c983f76146c/r)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "b0e4b7f4-58ce-4ca0-a986-d05a436a7ccf",
|
||||
@@ -524,9 +476,9 @@
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "poetry-venv",
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "poetry-venv"
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"id": "5d60cbb9-2a6a-43ea-a9e9-f67b16ddd2b2",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Tool error handling\n",
|
||||
"# Handling tool errors\n",
|
||||
"\n",
|
||||
"Using a model to invoke a tool has some obvious potential failure modes. Firstly, the model needs to return a output that can be parsed at all. Secondly, the model needs to return tool arguments that are valid.\n",
|
||||
"\n",
|
||||
@@ -29,7 +29,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install --upgrade --quiet langchain langchain-openai"
|
||||
"%pip install --upgrade --quiet langchain-core langchain-openai"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -37,7 +37,7 @@
|
||||
"id": "68107597-0c8c-4bb5-8c12-9992fabdf71a",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"And set these environment variables:"
|
||||
"If you'd like to trace your runs in [LangSmith](/docs/langsmith/) uncomment and set the following environment variables:"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -50,9 +50,6 @@
|
||||
"import getpass\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"os.environ[\"OPENAI_API_KEY\"] = getpass.getpass()\n",
|
||||
"\n",
|
||||
"# If you'd like to use LangSmith, uncomment the below:\n",
|
||||
"# os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
|
||||
"# os.environ[\"LANGCHAIN_API_KEY\"] = getpass.getpass()"
|
||||
]
|
||||
@@ -64,12 +61,33 @@
|
||||
"source": [
|
||||
"## Chain\n",
|
||||
"\n",
|
||||
"Suppose we have the following (dummy) tool and tool-calling chain. We'll make our tool intentionally convoluted to try and trip up the model."
|
||||
"Suppose we have the following (dummy) tool and tool-calling chain. We'll make our tool intentionally convoluted to try and trip up the model.\n",
|
||||
"\n",
|
||||
"```{=mdx}\n",
|
||||
"import ChatModelTabs from \"@theme/ChatModelTabs\";\n",
|
||||
"\n",
|
||||
"<ChatModelTabs customVarName=\"llm\"/>\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "86258950-5e61-4340-81b9-84a5d26e8773",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# | echo: false\n",
|
||||
"# | output: false\n",
|
||||
"\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"\n",
|
||||
"llm = ChatOpenAI(model=\"gpt-3.5-turbo-0125\", temperature=0)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "1d20604e-c4d1-4d21-841b-23e4f61aec36",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -91,13 +109,8 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Define model and bind tool\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"\n",
|
||||
"model = ChatOpenAI(model=\"gpt-3.5-turbo\", temperature=0)\n",
|
||||
"model_with_tools = model.bind_tools(\n",
|
||||
"llm_with_tools = llm.bind_tools(\n",
|
||||
" [complex_tool],\n",
|
||||
" tool_choice=\"complex_tool\",\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
@@ -109,16 +122,7 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Define chain\n",
|
||||
"from operator import itemgetter\n",
|
||||
"\n",
|
||||
"from langchain.output_parsers import JsonOutputKeyToolsParser\n",
|
||||
"from langchain_core.runnables import Runnable, RunnableLambda, RunnablePassthrough\n",
|
||||
"\n",
|
||||
"chain = (\n",
|
||||
" model_with_tools\n",
|
||||
" | JsonOutputKeyToolsParser(key_name=\"complex_tool\", first_tool_only=True)\n",
|
||||
" | complex_tool\n",
|
||||
")"
|
||||
"chain = llm_with_tools | (lambda msg: msg.tool_calls[0][\"args\"]) | complex_tool"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -131,25 +135,26 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"execution_count": 12,
|
||||
"id": "d354664c-ac44-4967-a35f-8912b3ad9477",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"ename": "ValidationError",
|
||||
"evalue": "1 validation error for complex_toolSchemaSchema\ndict_arg\n field required (type=value_error.missing)",
|
||||
"evalue": "1 validation error for complex_toolSchema\ndict_arg\n field required (type=value_error.missing)",
|
||||
"output_type": "error",
|
||||
"traceback": [
|
||||
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
|
||||
"\u001b[0;31mValidationError\u001b[0m Traceback (most recent call last)",
|
||||
"Cell \u001b[0;32mIn[6], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mchain\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 2\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43muse complex tool. the args are 5, 2.1, empty dictionary. don\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mt forget dict_arg\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\n\u001b[1;32m 3\u001b[0m \u001b[43m)\u001b[49m\n",
|
||||
"File \u001b[0;32m~/langchain/libs/core/langchain_core/runnables/base.py:1774\u001b[0m, in \u001b[0;36mRunnableSequence.invoke\u001b[0;34m(self, input, config)\u001b[0m\n\u001b[1;32m 1772\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 1773\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i, step \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39msteps):\n\u001b[0;32m-> 1774\u001b[0m \u001b[38;5;28minput\u001b[39m \u001b[38;5;241m=\u001b[39m \u001b[43mstep\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 1775\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1776\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;66;43;03m# mark each step as a child run\u001b[39;49;00m\n\u001b[1;32m 1777\u001b[0m \u001b[43m \u001b[49m\u001b[43mpatch_config\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 1778\u001b[0m \u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcallbacks\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrun_manager\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget_child\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43mf\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mseq:step:\u001b[39;49m\u001b[38;5;132;43;01m{\u001b[39;49;00m\u001b[43mi\u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[38;5;132;43;01m}\u001b[39;49;00m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1779\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1780\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1781\u001b[0m \u001b[38;5;66;03m# finish the root run\u001b[39;00m\n\u001b[1;32m 1782\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n",
|
||||
"File \u001b[0;32m~/langchain/libs/core/langchain_core/tools.py:210\u001b[0m, in \u001b[0;36mBaseTool.invoke\u001b[0;34m(self, input, config, **kwargs)\u001b[0m\n\u001b[1;32m 203\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21minvoke\u001b[39m(\n\u001b[1;32m 204\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[1;32m 205\u001b[0m \u001b[38;5;28minput\u001b[39m: Union[\u001b[38;5;28mstr\u001b[39m, Dict],\n\u001b[1;32m 206\u001b[0m config: Optional[RunnableConfig] \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[1;32m 207\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs: Any,\n\u001b[1;32m 208\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m Any:\n\u001b[1;32m 209\u001b[0m config \u001b[38;5;241m=\u001b[39m ensure_config(config)\n\u001b[0;32m--> 210\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 211\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 212\u001b[0m \u001b[43m \u001b[49m\u001b[43mcallbacks\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mconfig\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mcallbacks\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 213\u001b[0m \u001b[43m \u001b[49m\u001b[43mtags\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mconfig\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mtags\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 214\u001b[0m \u001b[43m \u001b[49m\u001b[43mmetadata\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mconfig\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mmetadata\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 215\u001b[0m \u001b[43m \u001b[49m\u001b[43mrun_name\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mconfig\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mrun_name\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 216\u001b[0m \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[1;32m 217\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n",
|
||||
"File \u001b[0;32m~/langchain/libs/core/langchain_core/tools.py:315\u001b[0m, in \u001b[0;36mBaseTool.run\u001b[0;34m(self, tool_input, verbose, start_color, color, callbacks, tags, metadata, run_name, **kwargs)\u001b[0m\n\u001b[1;32m 301\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mrun\u001b[39m(\n\u001b[1;32m 302\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[1;32m 303\u001b[0m tool_input: Union[\u001b[38;5;28mstr\u001b[39m, Dict],\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 312\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs: Any,\n\u001b[1;32m 313\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m Any:\n\u001b[1;32m 314\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Run the tool.\"\"\"\u001b[39;00m\n\u001b[0;32m--> 315\u001b[0m parsed_input \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_parse_input\u001b[49m\u001b[43m(\u001b[49m\u001b[43mtool_input\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 316\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[39mverbose \u001b[38;5;129;01mand\u001b[39;00m verbose \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 317\u001b[0m verbose_ \u001b[38;5;241m=\u001b[39m verbose\n",
|
||||
"File \u001b[0;32m~/langchain/libs/core/langchain_core/tools.py:250\u001b[0m, in \u001b[0;36mBaseTool._parse_input\u001b[0;34m(self, tool_input)\u001b[0m\n\u001b[1;32m 248\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 249\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m input_args \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m--> 250\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[43minput_args\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mparse_obj\u001b[49m\u001b[43m(\u001b[49m\u001b[43mtool_input\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 251\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m {\n\u001b[1;32m 252\u001b[0m k: \u001b[38;5;28mgetattr\u001b[39m(result, k)\n\u001b[1;32m 253\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m k, v \u001b[38;5;129;01min\u001b[39;00m result\u001b[38;5;241m.\u001b[39mdict()\u001b[38;5;241m.\u001b[39mitems()\n\u001b[1;32m 254\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m k \u001b[38;5;129;01min\u001b[39;00m tool_input\n\u001b[1;32m 255\u001b[0m }\n\u001b[1;32m 256\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m tool_input\n",
|
||||
"Cell \u001b[0;32mIn[12], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mchain\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 2\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43muse complex tool. the args are 5, 2.1, empty dictionary. don\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mt forget dict_arg\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\n\u001b[1;32m 3\u001b[0m \u001b[43m)\u001b[49m\n",
|
||||
"File \u001b[0;32m~/langchain/libs/core/langchain_core/runnables/base.py:2499\u001b[0m, in \u001b[0;36mRunnableSequence.invoke\u001b[0;34m(self, input, config)\u001b[0m\n\u001b[1;32m 2497\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 2498\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i, step \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39msteps):\n\u001b[0;32m-> 2499\u001b[0m \u001b[38;5;28minput\u001b[39m \u001b[38;5;241m=\u001b[39m \u001b[43mstep\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 2500\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 2501\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;66;43;03m# mark each step as a child run\u001b[39;49;00m\n\u001b[1;32m 2502\u001b[0m \u001b[43m \u001b[49m\u001b[43mpatch_config\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 2503\u001b[0m \u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcallbacks\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrun_manager\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget_child\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43mf\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mseq:step:\u001b[39;49m\u001b[38;5;132;43;01m{\u001b[39;49;00m\u001b[43mi\u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[38;5;132;43;01m}\u001b[39;49;00m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[1;32m 2504\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 2505\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 2506\u001b[0m \u001b[38;5;66;03m# finish the root run\u001b[39;00m\n\u001b[1;32m 2507\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n",
|
||||
"File \u001b[0;32m~/langchain/libs/core/langchain_core/tools.py:241\u001b[0m, in \u001b[0;36mBaseTool.invoke\u001b[0;34m(self, input, config, **kwargs)\u001b[0m\n\u001b[1;32m 234\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21minvoke\u001b[39m(\n\u001b[1;32m 235\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[1;32m 236\u001b[0m \u001b[38;5;28minput\u001b[39m: Union[\u001b[38;5;28mstr\u001b[39m, Dict],\n\u001b[1;32m 237\u001b[0m config: Optional[RunnableConfig] \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[1;32m 238\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs: Any,\n\u001b[1;32m 239\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m Any:\n\u001b[1;32m 240\u001b[0m config \u001b[38;5;241m=\u001b[39m ensure_config(config)\n\u001b[0;32m--> 241\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 242\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 243\u001b[0m \u001b[43m \u001b[49m\u001b[43mcallbacks\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mconfig\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mcallbacks\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 244\u001b[0m \u001b[43m \u001b[49m\u001b[43mtags\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mconfig\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mtags\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 245\u001b[0m \u001b[43m \u001b[49m\u001b[43mmetadata\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mconfig\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mmetadata\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 246\u001b[0m \u001b[43m \u001b[49m\u001b[43mrun_name\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mconfig\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mrun_name\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 247\u001b[0m \u001b[43m \u001b[49m\u001b[43mrun_id\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mconfig\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mpop\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mrun_id\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 248\u001b[0m \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[1;32m 249\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n",
|
||||
"File \u001b[0;32m~/langchain/libs/core/langchain_core/tools.py:387\u001b[0m, in \u001b[0;36mBaseTool.run\u001b[0;34m(self, tool_input, verbose, start_color, color, callbacks, tags, metadata, run_name, run_id, **kwargs)\u001b[0m\n\u001b[1;32m 385\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m ValidationError \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[1;32m 386\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[39mhandle_validation_error:\n\u001b[0;32m--> 387\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m e\n\u001b[1;32m 388\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mhandle_validation_error, \u001b[38;5;28mbool\u001b[39m):\n\u001b[1;32m 389\u001b[0m observation \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mTool input validation error\u001b[39m\u001b[38;5;124m\"\u001b[39m\n",
|
||||
"File \u001b[0;32m~/langchain/libs/core/langchain_core/tools.py:378\u001b[0m, in \u001b[0;36mBaseTool.run\u001b[0;34m(self, tool_input, verbose, start_color, color, callbacks, tags, metadata, run_name, run_id, **kwargs)\u001b[0m\n\u001b[1;32m 364\u001b[0m run_manager \u001b[38;5;241m=\u001b[39m callback_manager\u001b[38;5;241m.\u001b[39mon_tool_start(\n\u001b[1;32m 365\u001b[0m {\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mname\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mname, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mdescription\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mdescription},\n\u001b[1;32m 366\u001b[0m tool_input \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(tool_input, \u001b[38;5;28mstr\u001b[39m) \u001b[38;5;28;01melse\u001b[39;00m \u001b[38;5;28mstr\u001b[39m(tool_input),\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 375\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs,\n\u001b[1;32m 376\u001b[0m )\n\u001b[1;32m 377\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 378\u001b[0m parsed_input \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_parse_input\u001b[49m\u001b[43m(\u001b[49m\u001b[43mtool_input\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 379\u001b[0m tool_args, tool_kwargs \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_to_args_and_kwargs(parsed_input)\n\u001b[1;32m 380\u001b[0m observation \u001b[38;5;241m=\u001b[39m (\n\u001b[1;32m 381\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_run(\u001b[38;5;241m*\u001b[39mtool_args, run_manager\u001b[38;5;241m=\u001b[39mrun_manager, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mtool_kwargs)\n\u001b[1;32m 382\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m new_arg_supported\n\u001b[1;32m 383\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_run(\u001b[38;5;241m*\u001b[39mtool_args, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mtool_kwargs)\n\u001b[1;32m 384\u001b[0m )\n",
|
||||
"File \u001b[0;32m~/langchain/libs/core/langchain_core/tools.py:283\u001b[0m, in \u001b[0;36mBaseTool._parse_input\u001b[0;34m(self, tool_input)\u001b[0m\n\u001b[1;32m 281\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 282\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m input_args \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m--> 283\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[43minput_args\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mparse_obj\u001b[49m\u001b[43m(\u001b[49m\u001b[43mtool_input\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 284\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m {\n\u001b[1;32m 285\u001b[0m k: \u001b[38;5;28mgetattr\u001b[39m(result, k)\n\u001b[1;32m 286\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m k, v \u001b[38;5;129;01min\u001b[39;00m result\u001b[38;5;241m.\u001b[39mdict()\u001b[38;5;241m.\u001b[39mitems()\n\u001b[1;32m 287\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m k \u001b[38;5;129;01min\u001b[39;00m tool_input\n\u001b[1;32m 288\u001b[0m }\n\u001b[1;32m 289\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m tool_input\n",
|
||||
"File \u001b[0;32m~/langchain/.venv/lib/python3.9/site-packages/pydantic/v1/main.py:526\u001b[0m, in \u001b[0;36mBaseModel.parse_obj\u001b[0;34m(cls, obj)\u001b[0m\n\u001b[1;32m 524\u001b[0m exc \u001b[38;5;241m=\u001b[39m \u001b[38;5;167;01mTypeError\u001b[39;00m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mcls\u001b[39m\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__name__\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m expected dict not \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mobj\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__class__\u001b[39m\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__name__\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m'\u001b[39m)\n\u001b[1;32m 525\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m ValidationError([ErrorWrapper(exc, loc\u001b[38;5;241m=\u001b[39mROOT_KEY)], \u001b[38;5;28mcls\u001b[39m) \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01me\u001b[39;00m\n\u001b[0;32m--> 526\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mcls\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mobj\u001b[49m\u001b[43m)\u001b[49m\n",
|
||||
"File \u001b[0;32m~/langchain/.venv/lib/python3.9/site-packages/pydantic/v1/main.py:341\u001b[0m, in \u001b[0;36mBaseModel.__init__\u001b[0;34m(__pydantic_self__, **data)\u001b[0m\n\u001b[1;32m 339\u001b[0m values, fields_set, validation_error \u001b[38;5;241m=\u001b[39m validate_model(__pydantic_self__\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__class__\u001b[39m, data)\n\u001b[1;32m 340\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m validation_error:\n\u001b[0;32m--> 341\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m validation_error\n\u001b[1;32m 342\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 343\u001b[0m object_setattr(__pydantic_self__, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124m__dict__\u001b[39m\u001b[38;5;124m'\u001b[39m, values)\n",
|
||||
"\u001b[0;31mValidationError\u001b[0m: 1 validation error for complex_toolSchemaSchema\ndict_arg\n field required (type=value_error.missing)"
|
||||
"\u001b[0;31mValidationError\u001b[0m: 1 validation error for complex_toolSchema\ndict_arg\n field required (type=value_error.missing)"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -171,14 +176,14 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"execution_count": 6,
|
||||
"id": "8fedb550-683d-45ae-8876-ae7acb332019",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from typing import Any\n",
|
||||
"\n",
|
||||
"from langchain_core.runnables import RunnableConfig\n",
|
||||
"from langchain_core.runnables import Runnable, RunnableConfig\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def try_except_tool(tool_args: dict, config: RunnableConfig) -> Runnable:\n",
|
||||
@@ -188,16 +193,12 @@
|
||||
" return f\"Calling tool with arguments:\\n\\n{tool_args}\\n\\nraised the following error:\\n\\n{type(e)}: {e}\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"chain = (\n",
|
||||
" model_with_tools\n",
|
||||
" | JsonOutputKeyToolsParser(key_name=\"complex_tool\", first_tool_only=True)\n",
|
||||
" | try_except_tool\n",
|
||||
")"
|
||||
"chain = llm_with_tools | (lambda msg: msg.tool_calls[0][\"args\"]) | try_except_tool"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"execution_count": 15,
|
||||
"id": "71a2c98d-c0be-4c0a-bb3d-41ad4596526c",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -211,7 +212,7 @@
|
||||
"\n",
|
||||
"raised the following error:\n",
|
||||
"\n",
|
||||
"<class 'pydantic.v1.error_wrappers.ValidationError'>: 1 validation error for complex_toolSchemaSchema\n",
|
||||
"<class 'pydantic.v1.error_wrappers.ValidationError'>: 1 validation error for complex_toolSchema\n",
|
||||
"dict_arg\n",
|
||||
" field required (type=value_error.missing)\n"
|
||||
]
|
||||
@@ -237,7 +238,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"execution_count": 17,
|
||||
"id": "02cc4223-35fa-4240-976a-012299ca703c",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -247,25 +248,17 @@
|
||||
"10.5"
|
||||
]
|
||||
},
|
||||
"execution_count": 5,
|
||||
"execution_count": 17,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"chain = (\n",
|
||||
" model_with_tools\n",
|
||||
" | JsonOutputKeyToolsParser(key_name=\"complex_tool\", first_tool_only=True)\n",
|
||||
" | complex_tool\n",
|
||||
")\n",
|
||||
"chain = llm_with_tools | (lambda msg: msg.tool_calls[0][\"args\"]) | complex_tool\n",
|
||||
"better_model = ChatOpenAI(model=\"gpt-4-1106-preview\", temperature=0).bind_tools(\n",
|
||||
" [complex_tool], tool_choice=\"complex_tool\"\n",
|
||||
")\n",
|
||||
"better_chain = (\n",
|
||||
" better_model\n",
|
||||
" | JsonOutputKeyToolsParser(key_name=\"complex_tool\", first_tool_only=True)\n",
|
||||
" | complex_tool\n",
|
||||
")\n",
|
||||
"better_chain = better_model | (lambda msg: msg.tool_calls[0][\"args\"]) | complex_tool\n",
|
||||
"\n",
|
||||
"chain_with_fallback = chain.with_fallbacks([better_chain])\n",
|
||||
"chain_with_fallback.invoke(\n",
|
||||
@@ -278,7 +271,7 @@
|
||||
"id": "412f8c4e-cc83-4d87-84a1-5ba2f8edb1e9",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Looking at the [Langsmith trace](https://smith.langchain.com/public/241e1266-8555-4d49-99dc-b8df46109c39/r) for this chain run, we can see that the first chain call fails as expected and it's the fallback that succeeds."
|
||||
"Looking at the [Langsmith trace](https://smith.langchain.com/public/00e91fc2-e1a4-4b0f-a82e-e6b3119d196c/r) for this chain run, we can see that the first chain call fails as expected and it's the fallback that succeeds."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -293,7 +286,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"execution_count": 13,
|
||||
"id": "b5659956-9454-468a-9753-a3ff9052b8f5",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -301,7 +294,7 @@
|
||||
"import json\n",
|
||||
"from typing import Any\n",
|
||||
"\n",
|
||||
"from langchain_core.messages import AIMessage, HumanMessage, ToolMessage\n",
|
||||
"from langchain_core.messages import AIMessage, HumanMessage, ToolCall, ToolMessage\n",
|
||||
"from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n",
|
||||
"from langchain_core.runnables import RunnablePassthrough\n",
|
||||
"\n",
|
||||
@@ -309,36 +302,30 @@
|
||||
"class CustomToolException(Exception):\n",
|
||||
" \"\"\"Custom LangChain tool exception.\"\"\"\n",
|
||||
"\n",
|
||||
" def __init__(self, tool_call: dict, exception: Exception) -> None:\n",
|
||||
" def __init__(self, tool_call: ToolCall, exception: Exception) -> None:\n",
|
||||
" super().__init__()\n",
|
||||
" self.tool_call = tool_call\n",
|
||||
" self.exception = exception\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def tool_custom_exception(tool_call: dict, config: RunnableConfig) -> Runnable:\n",
|
||||
"def tool_custom_exception(msg: AIMessage, config: RunnableConfig) -> Runnable:\n",
|
||||
" try:\n",
|
||||
" return complex_tool.invoke(tool_call[\"args\"], config=config)\n",
|
||||
" return complex_tool.invoke(msg.tool_calls[0][\"args\"], config=config)\n",
|
||||
" except Exception as e:\n",
|
||||
" raise CustomToolException(tool_call, e)\n",
|
||||
" raise CustomToolException(msg.tool_calls[0], e)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def exception_to_messages(inputs: dict) -> dict:\n",
|
||||
" exception = inputs.pop(\"exception\")\n",
|
||||
" tool_call = {\n",
|
||||
" \"type\": \"function\",\n",
|
||||
" \"function\": {\n",
|
||||
" \"name\": \"complex_tool\",\n",
|
||||
" \"arguments\": json.dumps(exception.tool_call[\"args\"]),\n",
|
||||
" },\n",
|
||||
" \"id\": exception.tool_call[\"id\"],\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" # Add historical messages to the original input, so the model knows that it made a mistake with the last tool call.\n",
|
||||
" messages = [\n",
|
||||
" AIMessage(content=\"\", additional_kwargs={\"tool_calls\": [tool_call]}),\n",
|
||||
" ToolMessage(tool_call_id=tool_call[\"id\"], content=str(exception.exception)),\n",
|
||||
" AIMessage(content=\"\", tool_calls=[exception.tool_call]),\n",
|
||||
" ToolMessage(\n",
|
||||
" tool_call_id=exception.tool_call[\"id\"], content=str(exception.exception)\n",
|
||||
" ),\n",
|
||||
" HumanMessage(\n",
|
||||
" content=\"The last tool calls raised exceptions. Try calling the tools again with corrected arguments.\"\n",
|
||||
" content=\"The last tool call raised an exception. Try calling the tool again with corrected arguments. Do not repeat mistakes.\"\n",
|
||||
" ),\n",
|
||||
" ]\n",
|
||||
" inputs[\"last_output\"] = messages\n",
|
||||
@@ -351,14 +338,7 @@
|
||||
"prompt = ChatPromptTemplate.from_messages(\n",
|
||||
" [(\"human\", \"{input}\"), MessagesPlaceholder(\"last_output\", optional=True)]\n",
|
||||
")\n",
|
||||
"chain = (\n",
|
||||
" prompt\n",
|
||||
" | model_with_tools\n",
|
||||
" | JsonOutputKeyToolsParser(\n",
|
||||
" key_name=\"complex_tool\", return_id=True, first_tool_only=True\n",
|
||||
" )\n",
|
||||
" | tool_custom_exception\n",
|
||||
")\n",
|
||||
"chain = prompt | llm_with_tools | tool_custom_exception\n",
|
||||
"\n",
|
||||
"# If the initial chain call fails, we rerun it withe the exception passed in as a message.\n",
|
||||
"self_correcting_chain = chain.with_fallbacks(\n",
|
||||
@@ -368,7 +348,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"execution_count": 14,
|
||||
"id": "4c45f5bd-cbb4-47d5-b4b6-aec50673c750",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -378,7 +358,7 @@
|
||||
"10.5"
|
||||
]
|
||||
},
|
||||
"execution_count": 10,
|
||||
"execution_count": 14,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -396,15 +376,15 @@
|
||||
"id": "50d269a9-3cab-4a37-ba2f-805296453627",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"And our chain succeeds! Looking at the [LangSmith trace](https://smith.langchain.com/public/b780b740-daf5-43aa-a217-6d4600aba41b/r), we can see that indeed our initial chain still fails, and it's only on retrying that the chain succeeds."
|
||||
"And our chain succeeds! Looking at the [LangSmith trace](https://smith.langchain.com/public/c11e804c-e14f-4059-bd09-64766f999c14/r), we can see that indeed our initial chain still fails, and it's only on retrying that the chain succeeds."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "poetry-venv",
|
||||
"display_name": "poetry-venv-2",
|
||||
"language": "python",
|
||||
"name": "poetry-venv"
|
||||
"name": "poetry-venv-2"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
|
||||
@@ -480,8 +480,8 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.retrievers.web_research import WebResearchRetriever\n",
|
||||
"from langchain_chroma import Chroma\n",
|
||||
"from langchain_community.utilities import GoogleSearchAPIWrapper\n",
|
||||
"from langchain_community.vectorstores import Chroma\n",
|
||||
"from langchain_openai import ChatOpenAI, OpenAIEmbeddings"
|
||||
]
|
||||
},
|
||||
|
||||
@@ -20,13 +20,13 @@ CHAT_MODEL_FEAT_TABLE_CORRECTION = {
|
||||
"ChatMLflowAIGateway": {"_agenerate": False},
|
||||
"PromptLayerChatOpenAI": {"_stream": False, "_astream": False},
|
||||
"ChatKonko": {"_astream": False, "_agenerate": False},
|
||||
"ChatAnthropic": {"tool_calling": True},
|
||||
"ChatMistralAI": {"tool_calling": True},
|
||||
"ChatFireworks": {"tool_calling": True},
|
||||
"ChatOpenAI": {"tool_calling": True},
|
||||
"ChatVertexAI": {"tool_calling": True},
|
||||
"ChatGroq": {"tool_calling": "partial"},
|
||||
"ChatCohere": {"tool_calling": "partial"},
|
||||
"ChatAnthropic": {"tool_calling": True, "package": "langchain-anthropic"},
|
||||
"ChatMistralAI": {"tool_calling": True, "package": "langchain-mistralai"},
|
||||
"ChatFireworks": {"tool_calling": True, "package": "langchain-fireworks"},
|
||||
"ChatOpenAI": {"tool_calling": True, "package": "langchain-openai"},
|
||||
"ChatVertexAI": {"tool_calling": True, "package": "langchain-google-vertexai"},
|
||||
"ChatGroq": {"tool_calling": "partial", "package": "langchain-groq"},
|
||||
"ChatCohere": {"tool_calling": "partial", "package": "langchain-cohere"},
|
||||
}
|
||||
|
||||
|
||||
@@ -152,6 +152,7 @@ def get_chat_model_table() -> str:
|
||||
"_stream",
|
||||
"_astream",
|
||||
"tool_calling",
|
||||
"package",
|
||||
]
|
||||
title = [
|
||||
"Model",
|
||||
@@ -160,6 +161,7 @@ def get_chat_model_table() -> str:
|
||||
"Stream",
|
||||
"Async stream",
|
||||
"Tool calling",
|
||||
"Python Package",
|
||||
]
|
||||
rows = [title, [":-"] + [":-:"] * (len(title) - 1)]
|
||||
for llm, feats in sorted(final_feats.items()):
|
||||
@@ -167,12 +169,16 @@ def get_chat_model_table() -> str:
|
||||
row = [llm, "✅"]
|
||||
for h in header[1:]:
|
||||
value = feats.get(h)
|
||||
if value == "partial":
|
||||
row[header.index(h)] = "🟡"
|
||||
elif value is True:
|
||||
row[header.index(h)] = "✅"
|
||||
index = header.index(h)
|
||||
if h == "package":
|
||||
row.append(value or "langchain-community")
|
||||
else:
|
||||
row[header.index(h)] = "❌"
|
||||
if value == "partial":
|
||||
row.append("🟡")
|
||||
elif value is True:
|
||||
row.append("✅")
|
||||
else:
|
||||
row.append("❌")
|
||||
rows.append(row)
|
||||
return "\n".join(["|".join(row) for row in rows])
|
||||
|
||||
|
||||
@@ -23,15 +23,17 @@ os.environ["${apiKeyName}"] = getpass.getpass()`;
|
||||
* @typedef {Object} ChatModelTabsProps - Component props.
|
||||
* @property {string} [openaiParams] - Parameters for OpenAI chat model. Defaults to `model="gpt-3.5-turbo-0125"`
|
||||
* @property {string} [anthropicParams] - Parameters for Anthropic chat model. Defaults to `model="claude-3-sonnet-20240229"`
|
||||
* @property {string} [cohereParams] - Parameters for Cohere chat model. Defaults to `model="command-r"`
|
||||
* @property {string} [fireworksParams] - Parameters for Fireworks chat model. Defaults to `model="accounts/fireworks/models/mixtral-8x7b-instruct"`
|
||||
* @property {string} [mistralParams] - Parameters for Mistral chat model. Defaults to `model="mistral-large-latest"`
|
||||
* @property {string} [googleParams] - Parameters for Google chat model. Defaults to `model="gemini-pro"`
|
||||
* @property {string} [togetherParams] - Parameters for Together chat model. Defaults to `model="mistralai/Mixtral-8x7B-Instruct-v0.1"`
|
||||
* @property {boolean} [hideOpenai] - Whether or not to hide OpenAI chat model.
|
||||
* @property {boolean} [hideAnthropic] - Whether or not to hide Anthropic chat model.
|
||||
* @property {boolean} [hideCohere] - Whether or not to hide Cohere chat model.
|
||||
* @property {boolean} [hideFireworks] - Whether or not to hide Fireworks chat model.
|
||||
* @property {boolean} [hideMistral] - Whether or not to hide Mistral chat model.
|
||||
* @property {boolean} [hideGoogle] - Whether or not to hide Google chat model.
|
||||
* @property {boolean} [hideGoogle] - Whether or not to hide Google VertexAI chat model.
|
||||
* @property {boolean} [hideTogether] - Whether or not to hide Together chat model.
|
||||
* @property {string} [customVarName] - Custom variable name for the model. Defaults to `model`.
|
||||
*/
|
||||
@@ -43,12 +45,14 @@ export default function ChatModelTabs(props) {
|
||||
const {
|
||||
openaiParams,
|
||||
anthropicParams,
|
||||
cohereParams,
|
||||
fireworksParams,
|
||||
mistralParams,
|
||||
googleParams,
|
||||
togetherParams,
|
||||
hideOpenai,
|
||||
hideAnthropic,
|
||||
hideCohere,
|
||||
hideFireworks,
|
||||
hideMistral,
|
||||
hideGoogle,
|
||||
@@ -59,6 +63,7 @@ export default function ChatModelTabs(props) {
|
||||
const openAIParamsOrDefault = openaiParams ?? `model="gpt-3.5-turbo-0125"`;
|
||||
const anthropicParamsOrDefault =
|
||||
anthropicParams ?? `model="claude-3-sonnet-20240229"`;
|
||||
const cohereParamsOrDefault = cohereParams ?? `model="command-r"`;
|
||||
const fireworksParamsOrDefault =
|
||||
fireworksParams ??
|
||||
`model="accounts/fireworks/models/mixtral-8x7b-instruct"`;
|
||||
@@ -90,6 +95,24 @@ export default function ChatModelTabs(props) {
|
||||
default: false,
|
||||
shouldHide: hideAnthropic,
|
||||
},
|
||||
{
|
||||
value: "Google",
|
||||
label: "Google",
|
||||
text: `from langchain_google_vertexai import ChatVertexAI\n\n${llmVarName} = ChatVertexAI(${googleParamsOrDefault})`,
|
||||
apiKeyName: "GOOGLE_API_KEY",
|
||||
packageName: "langchain-google-vertexai",
|
||||
default: false,
|
||||
shouldHide: hideGoogle,
|
||||
},
|
||||
{
|
||||
value: "Cohere",
|
||||
label: "Cohere",
|
||||
text: `from langchain_cohere import ChatCohere\n\n${llmVarName} = ChatCohere(${cohereParamsOrDefault})`,
|
||||
apiKeyName: "COHERE_API_KEY",
|
||||
packageName: "langchain-cohere",
|
||||
default: false,
|
||||
shouldHide: hideCohere,
|
||||
},
|
||||
{
|
||||
value: "FireworksAI",
|
||||
label: "FireworksAI",
|
||||
@@ -108,15 +131,6 @@ export default function ChatModelTabs(props) {
|
||||
default: false,
|
||||
shouldHide: hideMistral,
|
||||
},
|
||||
{
|
||||
value: "Google",
|
||||
label: "Google",
|
||||
text: `from langchain_google_genai import ChatGoogleGenerativeAI\n\n${llmVarName} = ChatGoogleGenerativeAI(${googleParamsOrDefault})`,
|
||||
apiKeyName: "GOOGLE_API_KEY",
|
||||
packageName: "langchain-google-genai",
|
||||
default: false,
|
||||
shouldHide: hideGoogle,
|
||||
},
|
||||
{
|
||||
value: "TogetherAI",
|
||||
label: "TogetherAI",
|
||||
|
||||
BIN
docs/static/img/langsmith_rag_eval.png
vendored
Normal file
BIN
docs/static/img/langsmith_rag_eval.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 183 KiB |
BIN
docs/static/img/langsmith_rag_flow.png
vendored
Normal file
BIN
docs/static/img/langsmith_rag_flow.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 148 KiB |
BIN
docs/static/img/langsmith_rag_flow_doc_relevance.png
vendored
Normal file
BIN
docs/static/img/langsmith_rag_flow_doc_relevance.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 121 KiB |
BIN
docs/static/img/langsmith_rag_flow_hallucination.png
vendored
Normal file
BIN
docs/static/img/langsmith_rag_flow_hallucination.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 121 KiB |
@@ -33,7 +33,7 @@ __app_route_code__
|
||||
|
||||
(Optional) Let's now configure LangSmith.
|
||||
LangSmith will help us trace, monitor and debug LangChain applications.
|
||||
LangSmith is currently in private beta, you can sign up [here](https://smith.langchain.com/).
|
||||
You can sign up for LangSmith [here](https://smith.langchain.com/).
|
||||
If you don't have access, you can skip this section
|
||||
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ langchain app remove my/custom/path/rag
|
||||
|
||||
## Setup LangSmith (Optional)
|
||||
LangSmith will help us trace, monitor and debug LangChain applications.
|
||||
LangSmith is currently in private beta, you can sign up [here](https://smith.langchain.com/).
|
||||
You can sign up for LangSmith [here](https://smith.langchain.com/).
|
||||
If you don't have access, you can skip this section
|
||||
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ from langchain_community.tools.connery import ConneryService
|
||||
|
||||
class ConneryToolkit(BaseToolkit):
|
||||
"""
|
||||
A LangChain Toolkit with a list of Connery Actions as tools.
|
||||
Toolkit with a list of Connery Actions as tools.
|
||||
"""
|
||||
|
||||
tools: List[BaseTool]
|
||||
|
||||
@@ -348,7 +348,7 @@ def create_openapi_agent(
|
||||
allow_dangerous_requests: bool = False,
|
||||
**kwargs: Any,
|
||||
) -> Any:
|
||||
"""Instantiate OpenAI API planner and controller for a given spec.
|
||||
"""Construct an OpenAI API planner and controller for a given spec.
|
||||
|
||||
Inject credentials via requests_wrapper.
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ def analyze_text(
|
||||
|
||||
|
||||
class FlyteCallbackHandler(BaseMetadataCallbackHandler, BaseCallbackHandler):
|
||||
"""This callback handler that is used within a Flyte task."""
|
||||
"""Callback handler that is used within a Flyte task."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize callback handler."""
|
||||
|
||||
@@ -98,7 +98,7 @@ class LabelStudioCallbackHandler(BaseCallbackHandler):
|
||||
... mode='prompt'
|
||||
... )
|
||||
>>> llm = OpenAI(callbacks=[handler])
|
||||
>>> llm.predict('Tell me a story about a dog.')
|
||||
>>> llm.invoke('Tell me a story about a dog.')
|
||||
"""
|
||||
|
||||
DEFAULT_PROJECT_NAME: str = "LangChain-%Y-%m-%d"
|
||||
|
||||
@@ -204,7 +204,7 @@ class LLMonitorCallbackHandler(BaseCallbackHandler):
|
||||
llmonitor_callback = LLMonitorCallbackHandler()
|
||||
llm = OpenAI(callbacks=[llmonitor_callback],
|
||||
metadata={"userId": "user-123"})
|
||||
llm.predict("Hello, how are you?")
|
||||
llm.invoke("Hello, how are you?")
|
||||
```
|
||||
"""
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ MODEL_COST_PER_1K_TOKENS = {
|
||||
"gpt-4-1106-preview": 0.01,
|
||||
"gpt-4-0125-preview": 0.01,
|
||||
"gpt-4-turbo-preview": 0.01,
|
||||
"gpt-4-turbo": 0.01,
|
||||
"gpt-4-turbo-2024-04-09": 0.01,
|
||||
# GPT-4 output
|
||||
"gpt-4-completion": 0.06,
|
||||
"gpt-4-0314-completion": 0.06,
|
||||
@@ -28,6 +30,8 @@ MODEL_COST_PER_1K_TOKENS = {
|
||||
"gpt-4-1106-preview-completion": 0.03,
|
||||
"gpt-4-0125-preview-completion": 0.03,
|
||||
"gpt-4-turbo-preview-completion": 0.03,
|
||||
"gpt-4-turbo-completion": 0.03,
|
||||
"gpt-4-turbo-2024-04-09-completion": 0.03,
|
||||
# GPT-3.5 input
|
||||
# gpt-3.5-turbo points at gpt-3.5-turbo-0613 until Feb 16, 2024.
|
||||
# Switches to gpt-3.5-turbo-0125 after.
|
||||
|
||||
@@ -9,14 +9,14 @@ if TYPE_CHECKING:
|
||||
|
||||
|
||||
class ChildType(Enum):
|
||||
"""The enumerator of the child type."""
|
||||
"""Enumerator of the child type."""
|
||||
|
||||
MARKDOWN = "MARKDOWN"
|
||||
EXCEPTION = "EXCEPTION"
|
||||
|
||||
|
||||
class ChildRecord(NamedTuple):
|
||||
"""The child record as a NamedTuple."""
|
||||
"""Child record as a NamedTuple."""
|
||||
|
||||
type: ChildType
|
||||
kwargs: Dict[str, Any]
|
||||
@@ -24,7 +24,7 @@ class ChildRecord(NamedTuple):
|
||||
|
||||
|
||||
class MutableExpander:
|
||||
"""A Streamlit expander that can be renamed and dynamically expanded/collapsed."""
|
||||
"""Streamlit expander that can be renamed and dynamically expanded/collapsed."""
|
||||
|
||||
def __init__(self, parent_container: DeltaGenerator, label: str, expanded: bool):
|
||||
"""Create a new MutableExpander.
|
||||
@@ -51,7 +51,7 @@ class MutableExpander:
|
||||
|
||||
@property
|
||||
def label(self) -> str:
|
||||
"""The expander's label string."""
|
||||
"""Expander's label string."""
|
||||
return self._label
|
||||
|
||||
@property
|
||||
|
||||
@@ -40,7 +40,7 @@ class LLMThoughtState(Enum):
|
||||
|
||||
|
||||
class ToolRecord(NamedTuple):
|
||||
"""The tool record as a NamedTuple."""
|
||||
"""Tool record as a NamedTuple."""
|
||||
|
||||
name: str
|
||||
input_str: str
|
||||
|
||||
@@ -21,7 +21,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BaseMessageConverter(ABC):
|
||||
"""Class that converts BaseMessage to the SQLAlchemy model."""
|
||||
"""Convert BaseMessage to the SQLAlchemy model."""
|
||||
|
||||
@abstractmethod
|
||||
def from_sql_model(self, sql_message: Any) -> BaseMessage:
|
||||
|
||||
@@ -196,7 +196,7 @@ _message_type_lookups = {"human": "user", "ai": "assistant"}
|
||||
|
||||
|
||||
class BedrockChat(BaseChatModel, BedrockBase):
|
||||
"""A chat model that uses the Bedrock API."""
|
||||
"""Chat model that uses the Bedrock API."""
|
||||
|
||||
@property
|
||||
def _llm_type(self) -> str:
|
||||
|
||||
@@ -26,8 +26,7 @@ DEFAULT_SYSTEM_PROMPT = """You are a helpful, respectful, and honest assistant."
|
||||
|
||||
|
||||
class ChatHuggingFace(BaseChatModel):
|
||||
"""
|
||||
Wrapper for using Hugging Face LLM's as ChatModels.
|
||||
"""Hugging Face LLMs as ChatModels.
|
||||
|
||||
Works with `HuggingFaceTextGenInference`, `HuggingFaceEndpoint`,
|
||||
and `HuggingFaceHub` LLMs.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user