forked from Archives/langchain
Merge branch 'ankush/async-llm' into ankush/async-llmchain
This commit is contained in:
commit
2611fdd03e
@ -22,3 +22,9 @@ This repo serves as a template for how deploy a LangChain with Gradio.
|
||||
It implements a chatbot interface, with a "Bring-Your-Own-Token" approach (nice for not wracking up big bills).
|
||||
It also contains instructions for how to deploy this app on the Hugging Face platform.
|
||||
This is heavily influenced by James Weaver's [excellent examples](https://huggingface.co/JavaFXpert).
|
||||
|
||||
## [Beam](https://github.com/slai-labs/get-beam/tree/main/examples/langchain-question-answering)
|
||||
|
||||
This repo serves as a template for how deploy a LangChain with [Beam](https://beam.cloud).
|
||||
|
||||
It implements a Question Answering app and contains instructions for deploying the app as a serverless REST API.
|
@ -77,6 +77,17 @@ Open Source
|
||||
|
||||
+++
|
||||
|
||||
A jupyter notebook demonstrating how you could create a semantic search engine on documents in one of your Google Folders
|
||||
|
||||
---
|
||||
|
||||
.. link-button:: https://github.com/venuv/langchain_semantic_search
|
||||
:type: url
|
||||
:text: Google Folder Semantic Search
|
||||
:classes: stretched-link btn-lg
|
||||
|
||||
+++
|
||||
|
||||
Build a GitHub support bot with GPT3, LangChain, and Python.
|
||||
|
||||
---
|
||||
|
@ -21,6 +21,24 @@
|
||||
"from langchain import OpenAI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "9a58e15e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"llm = OpenAI(model_name='code-davinci-002', temperature=0, max_tokens=512)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "095adc76",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Math Prompt"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
@ -28,7 +46,6 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"llm = OpenAI(model_name='code-davinci-002', temperature=0, max_tokens=512)\n",
|
||||
"pal_chain = PALChain.from_math_prompt(llm, verbose=True)"
|
||||
]
|
||||
},
|
||||
@ -64,7 +81,7 @@
|
||||
" result = total_pets\n",
|
||||
" return result\u001b[0m\n",
|
||||
"\n",
|
||||
"\u001b[1m> Finished PALChain chain.\u001b[0m\n"
|
||||
"\u001b[1m> Finished chain.\u001b[0m\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -82,6 +99,14 @@
|
||||
"pal_chain.run(question)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "0269d20a",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Colored Objects"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
@ -89,7 +114,6 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"llm = OpenAI(model_name='code-davinci-002', temperature=0, max_tokens=512)\n",
|
||||
"pal_chain = PALChain.from_colored_object_prompt(llm, verbose=True)"
|
||||
]
|
||||
},
|
||||
@ -147,10 +171,94 @@
|
||||
"pal_chain.run(question)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "fc3d7f10",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Intermediate Steps\n",
|
||||
"You can also use the intermediate steps flag to return the code executed that generates the answer."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "9d2d9c61",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"pal_chain = PALChain.from_colored_object_prompt(llm, verbose=True, return_intermediate_steps=True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "b29b971b",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"question = \"On the desk, you see two blue booklets, two purple booklets, and two yellow pairs of sunglasses. If I remove all the pairs of sunglasses from the desk, how many purple items remain on it?\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "a2c40c28",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"\n",
|
||||
"\n",
|
||||
"\u001b[1m> Entering new PALChain chain...\u001b[0m\n",
|
||||
"\u001b[32;1m\u001b[1;3m# Put objects into a list to record ordering\n",
|
||||
"objects = []\n",
|
||||
"objects += [('booklet', 'blue')] * 2\n",
|
||||
"objects += [('booklet', 'purple')] * 2\n",
|
||||
"objects += [('sunglasses', 'yellow')] * 2\n",
|
||||
"\n",
|
||||
"# Remove all pairs of sunglasses\n",
|
||||
"objects = [object for object in objects if object[0] != 'sunglasses']\n",
|
||||
"\n",
|
||||
"# Count number of purple objects\n",
|
||||
"num_purple = len([object for object in objects if object[1] == 'purple'])\n",
|
||||
"answer = num_purple\u001b[0m\n",
|
||||
"\n",
|
||||
"\u001b[1m> Finished chain.\u001b[0m\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"result = pal_chain({\"question\": question})"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"id": "efddd033",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"\"# Put objects into a list to record ordering\\nobjects = []\\nobjects += [('booklet', 'blue')] * 2\\nobjects += [('booklet', 'purple')] * 2\\nobjects += [('sunglasses', 'yellow')] * 2\\n\\n# Remove all pairs of sunglasses\\nobjects = [object for object in objects if object[0] != 'sunglasses']\\n\\n# Count number of purple objects\\nnum_purple = len([object for object in objects if object[1] == 'purple'])\\nanswer = num_purple\""
|
||||
]
|
||||
},
|
||||
"execution_count": 11,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"result['intermediate_steps']"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "4ab20fec",
|
||||
"id": "dfd88594",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
|
@ -23,7 +23,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 1,
|
||||
"id": "8244ff60",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@ -81,7 +81,7 @@
|
||||
" template=\"Input: {input}\\nOutput: {output}\",\n",
|
||||
")\n",
|
||||
"example_selector = LengthBasedExampleSelector(\n",
|
||||
" # These are the examples is has available to choose from.\n",
|
||||
" # These are the examples it has available to choose from.\n",
|
||||
" examples=examples, \n",
|
||||
" # This is the PromptTemplate being used to format the examples.\n",
|
||||
" example_prompt=example_prompt, \n",
|
||||
@ -439,10 +439,242 @@
|
||||
"print(similar_prompt.format(adjective=\"worried\"))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "4aaeed2f",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## NGram Overlap ExampleSelector\n",
|
||||
"\n",
|
||||
"The NGramOverlapExampleSelector selects and orders examples based on which examples are most similar to the input, according to an ngram overlap score. The ngram overlap score is a float between 0.0 and 1.0, inclusive. \n",
|
||||
"\n",
|
||||
"The selector allows for a threshold score to be set. Examples with an ngram overlap score less than or equal to the threshold are excluded. The threshold is set to -1.0, by default, so will not exclude any examples, only reorder them. Setting the threshold to 0.0 will exclude examples that have no ngram overlaps with the input.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "9cbc0acc",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.prompts import PromptTemplate\n",
|
||||
"from langchain.prompts.example_selector.ngram_overlap import NGramOverlapExampleSelector"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "4f318f4b",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# These are examples of a fictional translation task.\n",
|
||||
"examples = [\n",
|
||||
" {\"input\": \"See Spot run.\", \"output\": \"Ver correr a Spot.\"},\n",
|
||||
" {\"input\": \"My dog barks.\", \"output\": \"Mi perro ladra.\"},\n",
|
||||
" {\"input\": \"Spot can run.\", \"output\": \"Spot puede correr.\"},\n",
|
||||
"]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "bf75e0fe",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"example_prompt = PromptTemplate(\n",
|
||||
" input_variables=[\"input\", \"output\"],\n",
|
||||
" template=\"Input: {input}\\nOutput: {output}\",\n",
|
||||
")\n",
|
||||
"example_selector = NGramOverlapExampleSelector(\n",
|
||||
" # These are the examples it has available to choose from.\n",
|
||||
" examples=examples, \n",
|
||||
" # This is the PromptTemplate being used to format the examples.\n",
|
||||
" example_prompt=example_prompt, \n",
|
||||
" # This is the threshold, at which selector stops.\n",
|
||||
" # It is set to -1.0 by default.\n",
|
||||
" threshold=-1.0,\n",
|
||||
" # For negative threshold:\n",
|
||||
" # Selector sorts examples by ngram overlap score, and excludes none.\n",
|
||||
" # For threshold greater than 1.0:\n",
|
||||
" # Selector excludes all examples, and returns an empty list.\n",
|
||||
" # For threshold equal to 0.0:\n",
|
||||
" # Selector sorts examples by ngram overlap score,\n",
|
||||
" # and excludes those with no ngram overlap with input.\n",
|
||||
")\n",
|
||||
"dynamic_prompt = FewShotPromptTemplate(\n",
|
||||
" # We provide an ExampleSelector instead of examples.\n",
|
||||
" example_selector=example_selector,\n",
|
||||
" example_prompt=example_prompt,\n",
|
||||
" prefix=\"Give the Spanish translation of every input\",\n",
|
||||
" suffix=\"Input: {sentence}\\nOutput:\", \n",
|
||||
" input_variables=[\"sentence\"],\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "83fb218a",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Give the Spanish translation of every input\n",
|
||||
"\n",
|
||||
"Input: Spot can run.\n",
|
||||
"Output: Spot puede correr.\n",
|
||||
"\n",
|
||||
"Input: See Spot run.\n",
|
||||
"Output: Ver correr a Spot.\n",
|
||||
"\n",
|
||||
"Input: My dog barks.\n",
|
||||
"Output: Mi perro ladra.\n",
|
||||
"\n",
|
||||
"Input: Spot can run fast.\n",
|
||||
"Output:\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# An example input with large ngram overlap with \"Spot can run.\"\n",
|
||||
"# and no overlap with \"My dog barks.\"\n",
|
||||
"print(dynamic_prompt.format(sentence=\"Spot can run fast.\"))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "485f5307",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Give the Spanish translation of every input\n",
|
||||
"\n",
|
||||
"Input: Spot can run.\n",
|
||||
"Output: Spot puede correr.\n",
|
||||
"\n",
|
||||
"Input: See Spot run.\n",
|
||||
"Output: Ver correr a Spot.\n",
|
||||
"\n",
|
||||
"Input: Spot plays fetch.\n",
|
||||
"Output: Spot juega a buscar.\n",
|
||||
"\n",
|
||||
"Input: My dog barks.\n",
|
||||
"Output: Mi perro ladra.\n",
|
||||
"\n",
|
||||
"Input: Spot can run fast.\n",
|
||||
"Output:\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# You can add examples to NGramOverlapExampleSelector as well.\n",
|
||||
"new_example = {\"input\": \"Spot plays fetch.\", \"output\": \"Spot juega a buscar.\"}\n",
|
||||
"\n",
|
||||
"example_selector.add_example(new_example)\n",
|
||||
"print(dynamic_prompt.format(sentence=\"Spot can run fast.\"))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "606ce697",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Give the Spanish translation of every input\n",
|
||||
"\n",
|
||||
"Input: Spot can run.\n",
|
||||
"Output: Spot puede correr.\n",
|
||||
"\n",
|
||||
"Input: See Spot run.\n",
|
||||
"Output: Ver correr a Spot.\n",
|
||||
"\n",
|
||||
"Input: Spot plays fetch.\n",
|
||||
"Output: Spot juega a buscar.\n",
|
||||
"\n",
|
||||
"Input: Spot can run fast.\n",
|
||||
"Output:\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# You can set a threshold at which examples are excluded.\n",
|
||||
"# For example, setting threshold equal to 0.0\n",
|
||||
"# excludes examples with no ngram overlaps with input.\n",
|
||||
"# Since \"My dog barks.\" has no ngram overlaps with \"Spot can run fast.\"\n",
|
||||
"# it is excluded.\n",
|
||||
"example_selector.threshold=0.0\n",
|
||||
"print(dynamic_prompt.format(sentence=\"Spot can run fast.\"))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 87,
|
||||
"id": "7f8d72f7",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Give the Spanish translation of every input\n",
|
||||
"\n",
|
||||
"Input: Spot can run.\n",
|
||||
"Output: Spot puede correr.\n",
|
||||
"\n",
|
||||
"Input: Spot plays fetch.\n",
|
||||
"Output: Spot juega a buscar.\n",
|
||||
"\n",
|
||||
"Input: Spot can play fetch.\n",
|
||||
"Output:\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Setting small nonzero threshold\n",
|
||||
"example_selector.threshold=0.09\n",
|
||||
"print(dynamic_prompt.format(sentence=\"Spot can play fetch.\"))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 88,
|
||||
"id": "09633aa8",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Give the Spanish translation of every input\n",
|
||||
"\n",
|
||||
"Input: Spot can play fetch.\n",
|
||||
"Output:\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Setting threshold greater than 1.0\n",
|
||||
"example_selector.threshold=1.0+1e-9\n",
|
||||
"print(dynamic_prompt.format(sentence=\"Spot can play fetch.\"))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "c746d6f4",
|
||||
"id": "39f30097",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
|
@ -77,7 +77,6 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "42f76e43",
|
||||
"metadata": {},
|
||||
@ -138,7 +137,6 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "ed47bb62",
|
||||
"metadata": {},
|
||||
@ -196,11 +194,137 @@
|
||||
"source": [
|
||||
"doc_result = embeddings.embed_documents([text])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "fff4734f",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## TensorflowHub\n",
|
||||
"Let's load the TensorflowHub Embedding class."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "f822104b",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.embeddings import TensorflowHubEmbeddings"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "bac84e46",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"2023-01-30 23:53:01.652176: I tensorflow/core/platform/cpu_feature_guard.cc:193] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations: AVX2 FMA\n",
|
||||
"To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.\n",
|
||||
"2023-01-30 23:53:34.362802: I tensorflow/core/platform/cpu_feature_guard.cc:193] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations: AVX2 FMA\n",
|
||||
"To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"embeddings = TensorflowHubEmbeddings()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "4790d770",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"text = \"This is a test document.\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "f556dcdb",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"query_result = embeddings.embed_query(text)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "59428e05",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## InstructEmbeddings\n",
|
||||
"Let's load the HuggingFace instruct Embeddings class."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "92c5b61e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.embeddings import HuggingFaceInstructEmbeddings"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"id": "062547b9",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"load INSTRUCTOR_Transformer\n",
|
||||
"max_seq_length 512\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"embeddings = HuggingFaceInstructEmbeddings(query_instruction=\"Represent the query for retrieval: \")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"id": "e1dcc4bd",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"text = \"This is a test document.\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"id": "90f0db94",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"query_result = embeddings.embed_query(text)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "a961cdb5",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "cohere",
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
@ -214,7 +338,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.8"
|
||||
"version": "3.10.9"
|
||||
},
|
||||
"vscode": {
|
||||
"interpreter": {
|
||||
|
@ -152,7 +152,7 @@
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Document creation\n",
|
||||
"We can also use the text splitter to create \"Documents\" directly. Documents a way of bundling pieces of text with associated metadata so that chains can interact with them. We can also create documents with empty metadata though!\n",
|
||||
"We can also use the text splitter to create \"Documents\" directly. Documents are a way of bundling pieces of text with associated metadata so that chains can interact with them. We can also create documents with empty metadata though!\n",
|
||||
"\n",
|
||||
"In the below example, we pass two pieces of text to get split up (we pass two just to show off the interface of splitting multiple pieces of text)."
|
||||
]
|
||||
|
@ -75,8 +75,8 @@ class ConversationalAgent(Agent):
|
||||
return self.ai_prefix
|
||||
|
||||
def _extract_tool_and_input(self, llm_output: str) -> Optional[Tuple[str, str]]:
|
||||
if f"{self.ai_prefix}: " in llm_output:
|
||||
return self.ai_prefix, llm_output.split(f"{self.ai_prefix}: ")[-1]
|
||||
if f"{self.ai_prefix}:" in llm_output:
|
||||
return self.ai_prefix, llm_output.split(f"{self.ai_prefix}:")[-1].strip()
|
||||
regex = r"Action: (.*?)\nAction Input: (.*)"
|
||||
match = re.search(regex, llm_output)
|
||||
if not match:
|
||||
|
@ -22,7 +22,6 @@ from langchain.chains.transform import TransformChain
|
||||
from langchain.chains.vector_db_qa.base import VectorDBQA
|
||||
|
||||
__all__ = [
|
||||
"APIChain",
|
||||
"ConversationChain",
|
||||
"LLMChain",
|
||||
"LLMBashChain",
|
||||
|
@ -1,4 +1,5 @@
|
||||
"""Chain that just formats a prompt and calls an LLM."""
|
||||
from string import Formatter
|
||||
from typing import Any, Dict, List, Sequence, Union
|
||||
|
||||
from pydantic import BaseModel, Extra
|
||||
@ -7,6 +8,7 @@ from langchain.chains.base import Chain
|
||||
from langchain.input import get_colored_text
|
||||
from langchain.llms.base import BaseLLM
|
||||
from langchain.prompts.base import BasePromptTemplate
|
||||
from langchain.prompts.prompt import PromptTemplate
|
||||
from langchain.schema import LLMResult
|
||||
|
||||
|
||||
@ -126,3 +128,14 @@ class LLMChain(Chain, BaseModel):
|
||||
@property
|
||||
def _chain_type(self) -> str:
|
||||
return "llm_chain"
|
||||
|
||||
@classmethod
|
||||
def from_string(cls, llm: BaseLLM, template: str) -> Chain:
|
||||
"""Create LLMChain from LLM and template."""
|
||||
input_variables = {
|
||||
v for _, v, _, _ in Formatter().parse(template) if v is not None
|
||||
}
|
||||
prompt_template = PromptTemplate(
|
||||
input_variables=list(input_variables), template=template
|
||||
)
|
||||
return cls(llm=llm, prompt=prompt_template)
|
||||
|
@ -336,7 +336,7 @@ class Crawler:
|
||||
element_node_value = strings[node_value[index]]
|
||||
if (
|
||||
element_node_value == "|"
|
||||
): # commonly used as a seperator, does not add much context - lets save ourselves some token space
|
||||
): # commonly used as a separator, does not add much context - lets save ourselves some token space
|
||||
continue
|
||||
elif (
|
||||
node_name == "input"
|
||||
|
@ -4,7 +4,7 @@ As in https://arxiv.org/pdf/2211.10435.pdf.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel, Extra
|
||||
|
||||
@ -24,7 +24,10 @@ class PALChain(Chain, BaseModel):
|
||||
prompt: BasePromptTemplate
|
||||
stop: str = "\n\n"
|
||||
get_answer_expr: str = "print(solution())"
|
||||
python_globals: Optional[Dict[str, Any]] = None
|
||||
python_locals: Optional[Dict[str, Any]] = None
|
||||
output_key: str = "result" #: :meta private:
|
||||
return_intermediate_steps: bool = False
|
||||
|
||||
class Config:
|
||||
"""Configuration for this pydantic object."""
|
||||
@ -46,7 +49,10 @@ class PALChain(Chain, BaseModel):
|
||||
|
||||
:meta private:
|
||||
"""
|
||||
return [self.output_key]
|
||||
if not self.return_intermediate_steps:
|
||||
return [self.output_key]
|
||||
else:
|
||||
return [self.output_key, "intermediate_steps"]
|
||||
|
||||
def _call(self, inputs: Dict[str, str]) -> Dict[str, str]:
|
||||
llm_chain = LLMChain(llm=self.llm, prompt=self.prompt)
|
||||
@ -54,9 +60,12 @@ class PALChain(Chain, BaseModel):
|
||||
self.callback_manager.on_text(
|
||||
code, color="green", end="\n", verbose=self.verbose
|
||||
)
|
||||
repl = PythonREPL()
|
||||
repl = PythonREPL(_globals=self.python_globals, _locals=self.python_locals)
|
||||
res = repl.run(code + f"\n{self.get_answer_expr}")
|
||||
return {self.output_key: res.strip()}
|
||||
output = {self.output_key: res.strip()}
|
||||
if self.return_intermediate_steps:
|
||||
output["intermediate_steps"] = code
|
||||
return output
|
||||
|
||||
@classmethod
|
||||
def from_math_prompt(cls, llm: BaseLLM, **kwargs: Any) -> PALChain:
|
||||
|
@ -21,7 +21,7 @@ class SQLDatabaseChain(Chain, BaseModel):
|
||||
|
||||
from langchain import SQLDatabaseChain, OpenAI, SQLDatabase
|
||||
db = SQLDatabase(...)
|
||||
db_chain = SelfAskWithSearchChain(llm=OpenAI(), database=db)
|
||||
db_chain = SQLDatabaseChain(llm=OpenAI(), database=db)
|
||||
"""
|
||||
|
||||
llm: BaseLLM
|
||||
|
@ -3,9 +3,13 @@ import logging
|
||||
from typing import Any
|
||||
|
||||
from langchain.embeddings.cohere import CohereEmbeddings
|
||||
from langchain.embeddings.huggingface import HuggingFaceEmbeddings
|
||||
from langchain.embeddings.huggingface import (
|
||||
HuggingFaceEmbeddings,
|
||||
HuggingFaceInstructEmbeddings,
|
||||
)
|
||||
from langchain.embeddings.huggingface_hub import HuggingFaceHubEmbeddings
|
||||
from langchain.embeddings.openai import OpenAIEmbeddings
|
||||
from langchain.embeddings.tensorflow_hub import TensorflowHubEmbeddings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -14,6 +18,8 @@ __all__ = [
|
||||
"HuggingFaceEmbeddings",
|
||||
"CohereEmbeddings",
|
||||
"HuggingFaceHubEmbeddings",
|
||||
"TensorflowHubEmbeddings",
|
||||
"HuggingFaceInstructEmbeddings",
|
||||
]
|
||||
|
||||
|
||||
|
@ -25,6 +25,9 @@ class CohereEmbeddings(BaseModel, Embeddings):
|
||||
model: str = "large"
|
||||
"""Model name to use."""
|
||||
|
||||
truncate: str = "NONE"
|
||||
"""Truncate embeddings that are too long from start or end ("NONE"|"START"|"END")"""
|
||||
|
||||
cohere_api_key: Optional[str] = None
|
||||
|
||||
class Config:
|
||||
@ -58,7 +61,9 @@ class CohereEmbeddings(BaseModel, Embeddings):
|
||||
Returns:
|
||||
List of embeddings, one for each text.
|
||||
"""
|
||||
embeddings = self.client.embed(model=self.model, texts=texts).embeddings
|
||||
embeddings = self.client.embed(
|
||||
model=self.model, texts=texts, truncate=self.truncate
|
||||
).embeddings
|
||||
return embeddings
|
||||
|
||||
def embed_query(self, text: str) -> List[float]:
|
||||
@ -70,5 +75,7 @@ class CohereEmbeddings(BaseModel, Embeddings):
|
||||
Returns:
|
||||
Embeddings for the text.
|
||||
"""
|
||||
embedding = self.client.embed(model=self.model, texts=[text]).embeddings[0]
|
||||
embedding = self.client.embed(
|
||||
model=self.model, texts=[text], truncate=self.truncate
|
||||
).embeddings[0]
|
||||
return embedding
|
||||
|
@ -6,6 +6,11 @@ from pydantic import BaseModel, Extra
|
||||
from langchain.embeddings.base import Embeddings
|
||||
|
||||
DEFAULT_MODEL_NAME = "sentence-transformers/all-mpnet-base-v2"
|
||||
DEFAULT_INSTRUCT_MODEL = "hkunlp/instructor-large"
|
||||
DEFAULT_EMBED_INSTRUCTION = "Represent the document for retrieval: "
|
||||
DEFAULT_QUERY_INSTRUCTION = (
|
||||
"Represent the question for retrieving supporting documents: "
|
||||
)
|
||||
|
||||
|
||||
class HuggingFaceEmbeddings(BaseModel, Embeddings):
|
||||
@ -68,3 +73,68 @@ class HuggingFaceEmbeddings(BaseModel, Embeddings):
|
||||
text = text.replace("\n", " ")
|
||||
embedding = self.client.encode(text)
|
||||
return embedding.tolist()
|
||||
|
||||
|
||||
class HuggingFaceInstructEmbeddings(BaseModel, Embeddings):
|
||||
"""Wrapper around sentence_transformers embedding models.
|
||||
|
||||
To use, you should have the ``sentence_transformers`` python package installed.
|
||||
|
||||
Example:
|
||||
.. code-block:: python
|
||||
|
||||
from langchain.embeddings import HuggingFaceInstructEmbeddings
|
||||
model_name = "hkunlp/instructor-large"
|
||||
hf = HuggingFaceInstructEmbeddings(model_name=model_name)
|
||||
"""
|
||||
|
||||
client: Any #: :meta private:
|
||||
model_name: str = DEFAULT_INSTRUCT_MODEL
|
||||
"""Model name to use."""
|
||||
embed_instruction: str = DEFAULT_EMBED_INSTRUCTION
|
||||
"""Instruction to use for embedding documents."""
|
||||
query_instruction: str = DEFAULT_QUERY_INSTRUCTION
|
||||
"""Instruction to use for embedding query."""
|
||||
|
||||
def __init__(self, **kwargs: Any):
|
||||
"""Initialize the sentence_transformer."""
|
||||
super().__init__(**kwargs)
|
||||
try:
|
||||
from InstructorEmbedding import INSTRUCTOR
|
||||
|
||||
self.client = INSTRUCTOR(self.model_name)
|
||||
except ImportError as e:
|
||||
raise ValueError("Dependencies for InstructorEmbedding not found.") from e
|
||||
|
||||
class Config:
|
||||
"""Configuration for this pydantic object."""
|
||||
|
||||
extra = Extra.forbid
|
||||
|
||||
def embed_documents(self, texts: List[str]) -> List[List[float]]:
|
||||
"""Compute doc embeddings using a HuggingFace instruct model.
|
||||
|
||||
Args:
|
||||
texts: The list of texts to embed.
|
||||
|
||||
Returns:
|
||||
List of embeddings, one for each text.
|
||||
"""
|
||||
instruction_pairs = []
|
||||
for text in texts:
|
||||
instruction_pairs.append([self.embed_instruction, text])
|
||||
embeddings = self.client.encode(instruction_pairs)
|
||||
return embeddings.tolist()
|
||||
|
||||
def embed_query(self, text: str) -> List[float]:
|
||||
"""Compute query embeddings using a HuggingFace instruct model.
|
||||
|
||||
Args:
|
||||
text: The text to embed.
|
||||
|
||||
Returns:
|
||||
Embeddings for the text.
|
||||
"""
|
||||
instruction_pair = [self.query_instruction, text]
|
||||
embedding = self.client.encode([instruction_pair])[0]
|
||||
return embedding.tolist()
|
||||
|
70
langchain/embeddings/tensorflow_hub.py
Normal file
70
langchain/embeddings/tensorflow_hub.py
Normal file
@ -0,0 +1,70 @@
|
||||
"""Wrapper around TensorflowHub embedding models."""
|
||||
from typing import Any, List
|
||||
|
||||
from pydantic import BaseModel, Extra
|
||||
|
||||
from langchain.embeddings.base import Embeddings
|
||||
|
||||
DEFAULT_MODEL_URL = "https://tfhub.dev/google/universal-sentence-encoder-multilingual/3"
|
||||
|
||||
|
||||
class TensorflowHubEmbeddings(BaseModel, Embeddings):
|
||||
"""Wrapper around tensorflow_hub embedding models.
|
||||
|
||||
To use, you should have the ``tensorflow_text`` python package installed.
|
||||
|
||||
Example:
|
||||
.. code-block:: python
|
||||
|
||||
from langchain.embeddings import TensorflowHubEmbeddings
|
||||
url = "https://tfhub.dev/google/universal-sentence-encoder-multilingual/3"
|
||||
tf = TensorflowHubEmbeddings(model_url=url)
|
||||
"""
|
||||
|
||||
embed: Any #: :meta private:
|
||||
model_url: str = DEFAULT_MODEL_URL
|
||||
"""Model name to use."""
|
||||
|
||||
def __init__(self, **kwargs: Any):
|
||||
"""Initialize the tensorflow_hub and tensorflow_text."""
|
||||
super().__init__(**kwargs)
|
||||
try:
|
||||
import tensorflow_hub
|
||||
import tensorflow_text # noqa
|
||||
|
||||
self.embed = tensorflow_hub.load(self.model_url)
|
||||
except ImportError as e:
|
||||
raise ValueError(
|
||||
"Could not import some python packages." "Please install them."
|
||||
) from e
|
||||
|
||||
class Config:
|
||||
"""Configuration for this pydantic object."""
|
||||
|
||||
extra = Extra.forbid
|
||||
|
||||
def embed_documents(self, texts: List[str]) -> List[List[float]]:
|
||||
"""Compute doc embeddings using a TensorflowHub embedding model.
|
||||
|
||||
Args:
|
||||
texts: The list of texts to embed.
|
||||
|
||||
Returns:
|
||||
List of embeddings, one for each text.
|
||||
"""
|
||||
texts = list(map(lambda x: x.replace("\n", " "), texts))
|
||||
embeddings = self.embed(texts).numpy()
|
||||
return embeddings.tolist()
|
||||
|
||||
def embed_query(self, text: str) -> List[float]:
|
||||
"""Compute query embeddings using a TensorflowHub embedding model.
|
||||
|
||||
Args:
|
||||
text: The text to embed.
|
||||
|
||||
Returns:
|
||||
Embeddings for the text.
|
||||
"""
|
||||
text = text.replace("\n", " ")
|
||||
embedding = self.embed(text).numpy()[0]
|
||||
return embedding.tolist()
|
@ -2,7 +2,7 @@
|
||||
import json
|
||||
from abc import ABC, abstractmethod
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Mapping, Optional, Union
|
||||
from typing import Any, Dict, List, Mapping, Optional, Union, Tuple
|
||||
|
||||
import yaml
|
||||
from pydantic import BaseModel, Extra, Field, validator
|
||||
@ -17,7 +17,8 @@ def _get_verbosity() -> bool:
|
||||
return langchain.verbose
|
||||
|
||||
|
||||
def get_prompts(params, prompts):
|
||||
def get_prompts(params: Dict[str, Any], prompts: List[str]) -> tuple[Dict[int, list], str, list[int], list[str]]:
|
||||
"""Get prompts that are already cached."""
|
||||
llm_string = str(sorted([(k, v) for k, v in params.items()]))
|
||||
missing_prompts = []
|
||||
missing_prompt_idxs = []
|
||||
@ -32,7 +33,10 @@ def get_prompts(params, prompts):
|
||||
return existing_prompts, llm_string, missing_prompt_idxs, missing_prompts
|
||||
|
||||
|
||||
def get_llm_output(existing_prompts, llm_string, missing_prompt_idxs, new_results, prompts):
|
||||
def get_llm_output(
|
||||
existing_prompts, llm_string, missing_prompt_idxs, new_results, prompts
|
||||
):
|
||||
"""Get the LLM output."""
|
||||
for i, result in enumerate(new_results.generations):
|
||||
existing_prompts[missing_prompt_idxs[i]] = result
|
||||
prompt = prompts[missing_prompt_idxs[i]]
|
||||
@ -83,7 +87,7 @@ class BaseLLM(BaseModel, ABC):
|
||||
"""Run the LLM on the given prompts."""
|
||||
|
||||
@abstractmethod
|
||||
async def _async_generate(
|
||||
async def _agenerate(
|
||||
self, prompts: List[str], stop: Optional[List[str]] = None
|
||||
) -> LLMResult:
|
||||
"""Run the LLM on the given prompts."""
|
||||
@ -111,7 +115,12 @@ class BaseLLM(BaseModel, ABC):
|
||||
return output
|
||||
params = self.dict()
|
||||
params["stop"] = stop
|
||||
existing_prompts, llm_string, missing_prompt_idxs, missing_prompts = get_prompts(params, prompts)
|
||||
(
|
||||
existing_prompts,
|
||||
llm_string,
|
||||
missing_prompt_idxs,
|
||||
missing_prompts,
|
||||
) = get_prompts(params, prompts)
|
||||
if len(missing_prompts) > 0:
|
||||
self.callback_manager.on_llm_start(
|
||||
{"name": self.__class__.__name__}, missing_prompts, verbose=self.verbose
|
||||
@ -122,13 +131,15 @@ class BaseLLM(BaseModel, ABC):
|
||||
self.callback_manager.on_llm_error(e, verbose=self.verbose)
|
||||
raise e
|
||||
self.callback_manager.on_llm_end(new_results, verbose=self.verbose)
|
||||
llm_output = get_llm_output(existing_prompts, llm_string, missing_prompt_idxs, new_results, prompts)
|
||||
llm_output = get_llm_output(
|
||||
existing_prompts, llm_string, missing_prompt_idxs, new_results, prompts
|
||||
)
|
||||
else:
|
||||
llm_output = {}
|
||||
generations = [existing_prompts[i] for i in range(len(prompts))]
|
||||
return LLMResult(generations=generations, llm_output=llm_output)
|
||||
|
||||
async def async_generate(
|
||||
async def agenerate(
|
||||
self, prompts: List[str], stop: Optional[List[str]] = None
|
||||
) -> LLMResult:
|
||||
disregard_cache = self.cache is not None and not self.cache
|
||||
@ -142,7 +153,7 @@ class BaseLLM(BaseModel, ABC):
|
||||
{"name": self.__class__.__name__}, prompts, verbose=self.verbose
|
||||
)
|
||||
try:
|
||||
output = await self._async_generate(prompts, stop=stop)
|
||||
output = await self._agenerate(prompts, stop=stop)
|
||||
except (KeyboardInterrupt, Exception) as e:
|
||||
self.callback_manager.on_llm_error(e, verbose=self.verbose)
|
||||
raise e
|
||||
@ -150,18 +161,25 @@ class BaseLLM(BaseModel, ABC):
|
||||
return output
|
||||
params = self.dict()
|
||||
params["stop"] = stop
|
||||
existing_prompts, llm_string, missing_prompt_idxs, missing_prompts = get_prompts(params, prompts)
|
||||
(
|
||||
existing_prompts,
|
||||
llm_string,
|
||||
missing_prompt_idxs,
|
||||
missing_prompts,
|
||||
) = get_prompts(params, prompts)
|
||||
if len(missing_prompts) > 0:
|
||||
self.callback_manager.on_llm_start(
|
||||
{"name": self.__class__.__name__}, missing_prompts, verbose=self.verbose
|
||||
)
|
||||
try:
|
||||
new_results = await self._async_generate(missing_prompts, stop=stop)
|
||||
new_results = await self._agenerate(missing_prompts, stop=stop)
|
||||
except (KeyboardInterrupt, Exception) as e:
|
||||
self.callback_manager.on_llm_error(e, verbose=self.verbose)
|
||||
raise e
|
||||
self.callback_manager.on_llm_end(new_results, verbose=self.verbose)
|
||||
llm_output = get_llm_output(existing_prompts, llm_string, missing_prompt_idxs, new_results, prompts)
|
||||
llm_output = get_llm_output(
|
||||
existing_prompts, llm_string, missing_prompt_idxs, new_results, prompts
|
||||
)
|
||||
else:
|
||||
llm_output = {}
|
||||
generations = [existing_prompts[i] for i in range(len(prompts))]
|
||||
@ -268,7 +286,7 @@ class LLM(BaseLLM):
|
||||
generations.append([Generation(text=text)])
|
||||
return LLMResult(generations=generations)
|
||||
|
||||
async def _async_generate(
|
||||
async def _agenerate(
|
||||
self, prompts: List[str], stop: Optional[List[str]] = None
|
||||
) -> LLMResult:
|
||||
"""Run the LLM on the given prompt and input."""
|
||||
|
@ -1,9 +1,16 @@
|
||||
"""Wrapper around OpenAI APIs."""
|
||||
import logging
|
||||
import sys
|
||||
from typing import Any, Dict, Generator, List, Mapping, Optional, Tuple, Union
|
||||
from typing import Any, Dict, Generator, List, Mapping, Optional, Tuple, Union, Set
|
||||
|
||||
from pydantic import BaseModel, Extra, Field, root_validator
|
||||
from tenacity import (
|
||||
after_log,
|
||||
retry,
|
||||
retry_if_exception_type,
|
||||
stop_after_attempt,
|
||||
wait_exponential,
|
||||
)
|
||||
|
||||
from langchain.llms.base import BaseLLM
|
||||
from langchain.schema import Generation, LLMResult
|
||||
@ -12,6 +19,16 @@ from langchain.utils import get_from_dict_or_env
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def update_token_usage(keys: Set[str], response: Dict[str, Any], token_usage: Dict[str, Any]) -> None:
|
||||
"""Update token usage."""
|
||||
_keys_to_use = keys.intersection(response["usage"])
|
||||
for _key in _keys_to_use:
|
||||
if _key not in token_usage:
|
||||
token_usage[_key] = response["usage"][_key]
|
||||
else:
|
||||
token_usage[_key] += response["usage"][_key]
|
||||
|
||||
|
||||
class BaseOpenAI(BaseLLM, BaseModel):
|
||||
"""Wrapper around OpenAI large language models.
|
||||
|
||||
@ -56,6 +73,8 @@ class BaseOpenAI(BaseLLM, BaseModel):
|
||||
"""Timeout for requests to OpenAI completion API. Default is 600 seconds."""
|
||||
logit_bias: Optional[Dict[str, float]] = Field(default_factory=dict)
|
||||
"""Adjust the probability of specific tokens being generated."""
|
||||
max_retries: int = 6
|
||||
"""Maximum number of retries to make when generating."""
|
||||
|
||||
class Config:
|
||||
"""Configuration for this pydantic object."""
|
||||
@ -115,6 +134,32 @@ class BaseOpenAI(BaseLLM, BaseModel):
|
||||
}
|
||||
return {**normal_params, **self.model_kwargs}
|
||||
|
||||
def completion_with_retry(self, **kwargs: Any) -> Any:
|
||||
"""Use tenacity to retry the completion call."""
|
||||
import openai
|
||||
|
||||
min_seconds = 4
|
||||
max_seconds = 10
|
||||
# Wait 2^x * 1 second between each retry starting with
|
||||
# 4 seconds, then up to 10 seconds, then 10 seconds afterwards
|
||||
|
||||
@retry(
|
||||
reraise=True,
|
||||
stop=stop_after_attempt(self.max_retries),
|
||||
wait=wait_exponential(multiplier=1, min=min_seconds, max=max_seconds),
|
||||
retry=(
|
||||
retry_if_exception_type(openai.error.Timeout)
|
||||
| retry_if_exception_type(openai.error.APIError)
|
||||
| retry_if_exception_type(openai.error.APIConnectionError)
|
||||
| retry_if_exception_type(openai.error.RateLimitError)
|
||||
),
|
||||
after=after_log(logger, logging.DEBUG),
|
||||
)
|
||||
def _completion_with_retry(**kwargs: Any) -> Any:
|
||||
return self.client.create(**kwargs)
|
||||
|
||||
return _completion_with_retry(**kwargs)
|
||||
|
||||
def _generate(
|
||||
self, prompts: List[str], stop: Optional[List[str]] = None
|
||||
) -> LLMResult:
|
||||
@ -141,19 +186,15 @@ class BaseOpenAI(BaseLLM, BaseModel):
|
||||
# Includes prompt, completion, and total tokens used.
|
||||
_keys = {"completion_tokens", "prompt_tokens", "total_tokens"}
|
||||
for _prompts in sub_prompts:
|
||||
response = self.client.create(prompt=_prompts, **params)
|
||||
response = self.completion_with_retry(prompt=_prompts, **params)
|
||||
choices.extend(response["choices"])
|
||||
_keys_to_use = _keys.intersection(response["usage"])
|
||||
for _key in _keys_to_use:
|
||||
if _key not in token_usage:
|
||||
token_usage[_key] = response["usage"][_key]
|
||||
else:
|
||||
token_usage[_key] += response["usage"][_key]
|
||||
update_token_usage(_keys, response, token_usage)
|
||||
return self.create_llm_result(choices, prompts, token_usage)
|
||||
|
||||
async def _async_generate(
|
||||
async def _agenerate(
|
||||
self, prompts: List[str], stop: Optional[List[str]] = None
|
||||
) -> LLMResult:
|
||||
"""Call out to OpenAI's endpoint async with k unique prompts."""
|
||||
params = self._invocation_params
|
||||
sub_prompts = self.get_sub_prompts(params, prompts, stop)
|
||||
choices = []
|
||||
@ -164,15 +205,11 @@ class BaseOpenAI(BaseLLM, BaseModel):
|
||||
for _prompts in sub_prompts:
|
||||
response = await self.client.acreate(prompt=_prompts, **params)
|
||||
choices.extend(response["choices"])
|
||||
_keys_to_use = _keys.intersection(response["usage"])
|
||||
for _key in _keys_to_use:
|
||||
if _key not in token_usage:
|
||||
token_usage[_key] = response["usage"][_key]
|
||||
else:
|
||||
token_usage[_key] += response["usage"][_key]
|
||||
update_token_usage(_keys, response, token_usage)
|
||||
return self.create_llm_result(choices, prompts, token_usage)
|
||||
|
||||
def get_sub_prompts(self, params, prompts, stop):
|
||||
"""Get the sub prompts for llm call."""
|
||||
if stop is not None:
|
||||
if "stop" in params:
|
||||
raise ValueError("`stop` found in both the input and default params.")
|
||||
|
112
langchain/prompts/example_selector/ngram_overlap.py
Normal file
112
langchain/prompts/example_selector/ngram_overlap.py
Normal file
@ -0,0 +1,112 @@
|
||||
"""Select and order examples based on ngram overlap score (sentence_bleu score).
|
||||
|
||||
https://www.nltk.org/_modules/nltk/translate/bleu_score.html
|
||||
https://aclanthology.org/P02-1040.pdf
|
||||
"""
|
||||
from typing import Dict, List
|
||||
|
||||
import numpy as np
|
||||
from pydantic import BaseModel, root_validator
|
||||
|
||||
from langchain.prompts.example_selector.base import BaseExampleSelector
|
||||
from langchain.prompts.prompt import PromptTemplate
|
||||
|
||||
|
||||
def ngram_overlap_score(source: List[str], example: List[str]) -> float:
|
||||
"""Compute ngram overlap score of source and example as sentence_bleu score.
|
||||
|
||||
Use sentence_bleu with method1 smoothing function and auto reweighting.
|
||||
Return float value between 0.0 and 1.0 inclusive.
|
||||
https://www.nltk.org/_modules/nltk/translate/bleu_score.html
|
||||
https://aclanthology.org/P02-1040.pdf
|
||||
"""
|
||||
from nltk.translate.bleu_score import ( # type: ignore
|
||||
SmoothingFunction,
|
||||
sentence_bleu,
|
||||
)
|
||||
|
||||
hypotheses = source[0].split()
|
||||
references = [s.split() for s in example]
|
||||
|
||||
return float(
|
||||
sentence_bleu(
|
||||
references,
|
||||
hypotheses,
|
||||
smoothing_function=SmoothingFunction().method1,
|
||||
auto_reweigh=True,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class NGramOverlapExampleSelector(BaseExampleSelector, BaseModel):
|
||||
"""Select and order examples based on ngram overlap score (sentence_bleu score).
|
||||
|
||||
https://www.nltk.org/_modules/nltk/translate/bleu_score.html
|
||||
https://aclanthology.org/P02-1040.pdf
|
||||
"""
|
||||
|
||||
examples: List[dict]
|
||||
"""A list of the examples that the prompt template expects."""
|
||||
|
||||
example_prompt: PromptTemplate
|
||||
"""Prompt template used to format the examples."""
|
||||
|
||||
threshold: float = -1.0
|
||||
"""Threshold at which algorithm stops. Set to -1.0 by default.
|
||||
|
||||
For negative threshold:
|
||||
select_examples sorts examples by ngram_overlap_score, but excludes none.
|
||||
For threshold greater than 1.0:
|
||||
select_examples excludes all examples, and returns an empty list.
|
||||
For threshold equal to 0.0:
|
||||
select_examples sorts examples by ngram_overlap_score,
|
||||
and excludes examples with no ngram overlap with input.
|
||||
"""
|
||||
|
||||
@root_validator(pre=True)
|
||||
def check_dependencies(cls, values: Dict) -> Dict:
|
||||
"""Check that valid dependencies exist."""
|
||||
try:
|
||||
from nltk.translate.bleu_score import ( # noqa: disable=F401
|
||||
SmoothingFunction,
|
||||
sentence_bleu,
|
||||
)
|
||||
except ImportError as e:
|
||||
raise ValueError(
|
||||
"Not all the correct dependencies for this ExampleSelect exist"
|
||||
) from e
|
||||
|
||||
return values
|
||||
|
||||
def add_example(self, example: Dict[str, str]) -> None:
|
||||
"""Add new example to list."""
|
||||
self.examples.append(example)
|
||||
|
||||
def select_examples(self, input_variables: Dict[str, str]) -> List[dict]:
|
||||
"""Return list of examples sorted by ngram_overlap_score with input.
|
||||
|
||||
Descending order.
|
||||
Excludes any examples with ngram_overlap_score less than or equal to threshold.
|
||||
"""
|
||||
inputs = list(input_variables.values())
|
||||
examples = []
|
||||
k = len(self.examples)
|
||||
score = [0.0] * k
|
||||
first_prompt_template_key = self.example_prompt.input_variables[0]
|
||||
|
||||
for i in range(k):
|
||||
score[i] = ngram_overlap_score(
|
||||
inputs, [self.examples[i][first_prompt_template_key]]
|
||||
)
|
||||
|
||||
while True:
|
||||
arg_max = np.argmax(score)
|
||||
if (score[arg_max] < self.threshold) or abs(
|
||||
score[arg_max] - self.threshold
|
||||
) < 1e-9:
|
||||
break
|
||||
|
||||
examples.append(self.examples[arg_max])
|
||||
score[arg_max] = self.threshold - 1.0
|
||||
|
||||
return examples
|
@ -96,7 +96,8 @@ class GoogleSearchAPIWrapper(BaseModel):
|
||||
if len(results) == 0:
|
||||
return "No good Google Search Result was found"
|
||||
for result in results:
|
||||
snippets.append(result["snippet"])
|
||||
if "snippet" in result:
|
||||
snippets.append(result["snippet"])
|
||||
|
||||
return " ".join(snippets)
|
||||
|
||||
@ -119,10 +120,11 @@ class GoogleSearchAPIWrapper(BaseModel):
|
||||
return [{"Result": "No good Google Search Result was found"}]
|
||||
for result in results:
|
||||
metadata_result = {
|
||||
"snippet": result["snippet"],
|
||||
"title": result["title"],
|
||||
"link": result["link"],
|
||||
}
|
||||
if "snippet" in result:
|
||||
metadata_result["snippet"] = result["snippet"]
|
||||
metadata_results.append(metadata_result)
|
||||
|
||||
return metadata_results
|
||||
|
1174
poetry.lock
generated
1174
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "langchain"
|
||||
version = "0.0.74"
|
||||
version = "0.0.76"
|
||||
description = "Building applications with LLMs through composability"
|
||||
authors = []
|
||||
license = "MIT"
|
||||
@ -35,6 +35,8 @@ google-api-python-client = {version = "2.70.0", optional = true}
|
||||
wolframalpha = {version = "5.0.0", optional = true}
|
||||
qdrant-client = {version = "^0.11.7", optional = true}
|
||||
dataclasses-json = "^0.5.7"
|
||||
tensorflow-text = {version = "^2.11.0", optional = true, python = "^3.10, <3.12"}
|
||||
tenacity = "^8.1.0"
|
||||
|
||||
[tool.poetry.group.docs.dependencies]
|
||||
autodoc_pydantic = "^1.8.0"
|
||||
@ -58,6 +60,7 @@ duckdb-engine = "^0.6.6"
|
||||
pytest-watcher = "^0.2.6"
|
||||
freezegun = "^1.2.2"
|
||||
responses = "^0.22.0"
|
||||
pytest-asyncio = "^0.20.3"
|
||||
|
||||
[tool.poetry.group.lint.dependencies]
|
||||
flake8-docstrings = "^1.6.0"
|
||||
@ -81,7 +84,7 @@ playwright = "^1.28.0"
|
||||
|
||||
[tool.poetry.extras]
|
||||
llms = ["cohere", "openai", "nlpcloud", "huggingface_hub", "manifest-ml", "torch", "transformers"]
|
||||
all = ["cohere", "openai", "nlpcloud", "huggingface_hub", "manifest-ml", "elasticsearch", "google-search-results", "faiss-cpu", "sentence_transformers", "transformers", "spacy", "nltk", "wikipedia", "beautifulsoup4", "tiktoken", "torch", "jinja2", "pinecone-client", "weaviate-client", "redis", "google-api-python-client", "wolframalpha", "qdrant-client"]
|
||||
all = ["cohere", "openai", "nlpcloud", "huggingface_hub", "manifest-ml", "elasticsearch", "google-search-results", "faiss-cpu", "sentence_transformers", "transformers", "spacy", "nltk", "wikipedia", "beautifulsoup4", "tiktoken", "torch", "jinja2", "pinecone-client", "weaviate-client", "redis", "google-api-python-client", "wolframalpha", "qdrant-client", "tensorflow-text"]
|
||||
|
||||
[tool.isort]
|
||||
profile = "black"
|
||||
|
@ -1,7 +1,10 @@
|
||||
"""Test huggingface embeddings."""
|
||||
import unittest
|
||||
|
||||
from langchain.embeddings.huggingface import HuggingFaceEmbeddings
|
||||
from langchain.embeddings.huggingface import (
|
||||
HuggingFaceEmbeddings,
|
||||
HuggingFaceInstructEmbeddings,
|
||||
)
|
||||
|
||||
|
||||
@unittest.skip("This test causes a segfault.")
|
||||
@ -21,3 +24,20 @@ def test_huggingface_embedding_query() -> None:
|
||||
embedding = HuggingFaceEmbeddings()
|
||||
output = embedding.embed_query(document)
|
||||
assert len(output) == 768
|
||||
|
||||
|
||||
def test_huggingface_instructor_embedding_documents() -> None:
|
||||
"""Test huggingface embeddings."""
|
||||
documents = ["foo bar"]
|
||||
embedding = HuggingFaceInstructEmbeddings()
|
||||
output = embedding.embed_documents(documents)
|
||||
assert len(output) == 1
|
||||
assert len(output[0]) == 768
|
||||
|
||||
|
||||
def test_huggingface_instructor_embedding_query() -> None:
|
||||
"""Test huggingface embeddings."""
|
||||
query = "foo bar"
|
||||
embedding = HuggingFaceInstructEmbeddings()
|
||||
output = embedding.embed_query(query)
|
||||
assert len(output) == 768
|
||||
|
19
tests/integration_tests/embeddings/test_tensorflow_hub.py
Normal file
19
tests/integration_tests/embeddings/test_tensorflow_hub.py
Normal file
@ -0,0 +1,19 @@
|
||||
"""Test TensorflowHub embeddings."""
|
||||
from langchain.embeddings import TensorflowHubEmbeddings
|
||||
|
||||
|
||||
def test_tensorflowhub_embedding_documents() -> None:
|
||||
"""Test tensorflowhub embeddings."""
|
||||
documents = ["foo bar"]
|
||||
embedding = TensorflowHubEmbeddings()
|
||||
output = embedding.embed_documents(documents)
|
||||
assert len(output) == 1
|
||||
assert len(output[0]) == 512
|
||||
|
||||
|
||||
def test_tensorflowhub_embedding_query() -> None:
|
||||
"""Test tensorflowhub embeddings."""
|
||||
document = "foo bar"
|
||||
embedding = TensorflowHubEmbeddings()
|
||||
output = embedding.embed_query(document)
|
||||
assert len(output) == 512
|
@ -7,6 +7,7 @@ import pytest
|
||||
|
||||
from langchain.llms.loading import load_llm
|
||||
from langchain.llms.openai import OpenAI
|
||||
from langchain.schema import LLMResult
|
||||
|
||||
|
||||
def test_openai_call() -> None:
|
||||
@ -74,3 +75,11 @@ def test_openai_streaming_error() -> None:
|
||||
llm = OpenAI(best_of=2)
|
||||
with pytest.raises(ValueError):
|
||||
llm.stream("I'm Pickle Rick")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_openai_async_generate() -> None:
|
||||
"""Test async generation."""
|
||||
llm = OpenAI(max_tokens=10)
|
||||
output = await llm.agenerate(["Hello, how are you?"])
|
||||
assert isinstance(output, LLMResult)
|
||||
|
@ -0,0 +1,73 @@
|
||||
"""Test functionality related to ngram overlap based selector."""
|
||||
|
||||
import pytest
|
||||
|
||||
from langchain.prompts.example_selector.ngram_overlap import (
|
||||
NGramOverlapExampleSelector,
|
||||
ngram_overlap_score,
|
||||
)
|
||||
from langchain.prompts.prompt import PromptTemplate
|
||||
|
||||
EXAMPLES = [
|
||||
{"input": "See Spot run.", "output": "foo1"},
|
||||
{"input": "My dog barks.", "output": "foo2"},
|
||||
{"input": "Spot can run.", "output": "foo3"},
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def selector() -> NGramOverlapExampleSelector:
|
||||
"""Get ngram overlap based selector to use in tests."""
|
||||
prompts = PromptTemplate(
|
||||
input_variables=["input", "output"], template="Input: {input}\nOutput: {output}"
|
||||
)
|
||||
selector = NGramOverlapExampleSelector(
|
||||
examples=EXAMPLES,
|
||||
example_prompt=prompts,
|
||||
)
|
||||
return selector
|
||||
|
||||
|
||||
def test_selector_valid(selector: NGramOverlapExampleSelector) -> None:
|
||||
"""Test NGramOverlapExampleSelector can select examples."""
|
||||
sentence = "Spot can run."
|
||||
output = selector.select_examples({"input": sentence})
|
||||
assert output == [EXAMPLES[2], EXAMPLES[0], EXAMPLES[1]]
|
||||
|
||||
|
||||
def test_selector_add_example(selector: NGramOverlapExampleSelector) -> None:
|
||||
"""Test NGramOverlapExampleSelector can add an example."""
|
||||
new_example = {"input": "Spot plays fetch.", "output": "foo4"}
|
||||
selector.add_example(new_example)
|
||||
sentence = "Spot can run."
|
||||
output = selector.select_examples({"input": sentence})
|
||||
assert output == [EXAMPLES[2], EXAMPLES[0]] + [new_example] + [EXAMPLES[1]]
|
||||
|
||||
|
||||
def test_selector_threshold_zero(selector: NGramOverlapExampleSelector) -> None:
|
||||
"""Tests NGramOverlapExampleSelector threshold set to 0.0."""
|
||||
selector.threshold = 0.0
|
||||
sentence = "Spot can run."
|
||||
output = selector.select_examples({"input": sentence})
|
||||
assert output == [EXAMPLES[2], EXAMPLES[0]]
|
||||
|
||||
|
||||
def test_selector_threshold_more_than_one(
|
||||
selector: NGramOverlapExampleSelector,
|
||||
) -> None:
|
||||
"""Tests NGramOverlapExampleSelector threshold greater than 1.0."""
|
||||
selector.threshold = 1.0 + 1e-9
|
||||
sentence = "Spot can run."
|
||||
output = selector.select_examples({"input": sentence})
|
||||
assert output == []
|
||||
|
||||
|
||||
def test_ngram_overlap_score(selector: NGramOverlapExampleSelector) -> None:
|
||||
"""Tests that ngram_overlap_score returns correct values."""
|
||||
selector.threshold = 1.0 + 1e-9
|
||||
none = ngram_overlap_score(["Spot can run."], ["My dog barks."])
|
||||
some = ngram_overlap_score(["Spot can run."], ["See Spot run."])
|
||||
complete = ngram_overlap_score(["Spot can run."], ["Spot can run."])
|
||||
|
||||
check = [abs(none - 0.0) < 1e-9, 0.0 < some < 1.0, abs(complete - 1.0) < 1e-9]
|
||||
assert check == [True, True, True]
|
@ -33,7 +33,7 @@ class FakeLLM(BaseLLM, BaseModel):
|
||||
) -> LLMResult:
|
||||
return LLMResult(generations=[[Generation(text="foo") for _ in range(self.n)]])
|
||||
|
||||
async def _async_generate(
|
||||
async def _agenerate(
|
||||
self, prompts: List[str], stop: Optional[List[str]] = None
|
||||
) -> LLMResult:
|
||||
return LLMResult(generations=[[Generation(text="foo") for _ in range(self.n)]])
|
||||
|
@ -1,6 +1,7 @@
|
||||
from langchain.llms import OpenAI
|
||||
import asyncio
|
||||
|
||||
from langchain.llms import OpenAI
|
||||
|
||||
|
||||
def generate_serially():
|
||||
llm = OpenAI(temperature=0)
|
||||
@ -10,7 +11,7 @@ def generate_serially():
|
||||
|
||||
|
||||
async def async_generate(llm):
|
||||
resp = await llm.async_generate(["Hello, how are you?"])
|
||||
resp = await llm.agenerate(["Hello, how are you?"])
|
||||
# print(resp)
|
||||
|
||||
|
||||
@ -22,6 +23,7 @@ async def generate_concurrently():
|
||||
|
||||
if __name__ == "__main__":
|
||||
import time
|
||||
|
||||
s = time.perf_counter()
|
||||
asyncio.run(generate_concurrently())
|
||||
elapsed = time.perf_counter() - s
|
||||
|
Loading…
Reference in New Issue
Block a user